1

I have a php script that displays a web form to the user. When the user submits the form, i save the data in the database and then i fork a process, the son, to send some SMS to the database users afected with the new changes.

When i fork, i check for the son, and he sends the SMS correctly, and in the end exits. But by some reason, the father waits for the son to do his tasks. I dunno why this is happening..

Here is a sample of my code:

    // before this point, i've inserted some data in the database, and now i commit the transaction
    $conn->commit();
    $pid = pcntl_fork();
if ($pid == 0) {    // its the son, so he will send the messages
  $conn = new PDO('oci:dbname='.SERVER.';charset='.CHARSET, DATABASE, PASSWORD);
  $suppliersPhoneNumber = getSuppliersPhoneNumber($conn, ...);
  $conn = null;
  $sms = new MessageSender($suppliersPhoneNumber, $_POST['subCategory']);
  $sms->handleMessages();   // send the sms     
      //sleep(60);      
  exit(0);  // the son won't execute more code
}/

The line with the code "sleep(60)" is how i know that the father is waiting for the child. But how is this possible if the son exits?? I know the father waits for the son, cause in fact my script freezes for 1 minute, the waiting time.

My idea is to have a father inserting the required data in the database, in the end he spawns a new child to send the messages, but doesn't waits for him, so we can send a response page to the user saying everything went fine, while the messages are effectively being sent.

What is going wrong here?

Thks in advance

EDIT The problem was not solve, instead i followed the solution of Paulo H. registed below. Indeed it was a better way.

Paulo Rodrigues
  • 593
  • 14
  • 32
  • This isn't really an answer to your question, but forking PHP processes attached to Apache (or some other web server) is complicated due to inherited resources. Personally, I would create a database table and insert a row into this each time the form is submitted. Using CRON you could run another PHP script every minute to check for new entries in this table and send SMSs for each new entry. For me this is a much simpler way to decouple background logic. – SlappyTheFish Dec 07 '11 at 13:00

3 Answers3

1

I believe that you are using Apache to run this code, I suggest you run a completely separate process in the background.

I think the problem is happening because Apache waits for this child process before sending the information to the browser. Anyway do not recommend fork if not in a php-cli script.

Usually the child process stay in zombie mode until the parent process call a "wait", but there not seems to be the case.

Community
  • 1
  • 1
Paulo H.
  • 1,228
  • 10
  • 15
  • I followed the link you gave, and in there they called the script through exec. How would that change what i have Here? Yes, indeed i'm running this using Apache. But its weird, since i created an example that forks too. In that example the parent does the fork, than the son just waits 60 seconds, but the parent redirects the page to google.com. The curious thing here, is that in this example, the parent does not wait for the son, he redirects at once to google when i load my script through the browser. – Paulo Rodrigues Dec 07 '11 at 12:28
  • You must create another PHP Script, then call `exec(sprintf("%s > %s 2>&1 & echo $!", "php yourscript.php", "/path/to/debug/file"),$childpid);` ` – Paulo H. Dec 07 '11 at 12:33
  • Why using exec it's going to make any difference? – Paulo Rodrigues Dec 07 '11 at 12:43
  • The Exec will create a process from scratch, while the fork will create a new process by copying all the information from memory until fork, including the resource handlers and other variables. This prevents Apache hangs because of the fork, in my opinion the best solution. – Paulo H. Dec 07 '11 at 13:02
  • Ok i will try this right away. But everytime i used exec, i did it in a forked child process to, because if everything goes well, nothing is executed after exec. Am i right? I am suppose to do the fork anyway, in the child call exec to a php script that sends the SMS right? – Paulo Rodrigues Dec 07 '11 at 13:28
  • This exec is async if you use the sample above, will not block. You not need the fork anymore. – Paulo H. Dec 07 '11 at 13:41
  • If i do this with my comand in the $cmd variable, the sms aren't being sent. although, if i call the script with only exec($cmd), it works, but the script who calls the exec waits for the exec to finish. In the exec php page, it says if i don't redirect the output stream of the exec, the caller script will wait..How can i redirect the output stream of exec? – Paulo Rodrigues Dec 07 '11 at 15:28
  • Ok by some reason i can't use sprintf, cause it doesn't work. But i was able to use exec with the following string: "php /path/messages_script.php > /path/outputfile.txt" as a command. It worked, the exec executed, but the script who calls exec waited for him to finish.. – Paulo Rodrigues Dec 07 '11 at 15:45
  • `>` and `2>&1` redirect the output to the file defined (`"/path/to/debug/file"`) check if you have permission to write in this file (the file you must define)... – Paulo H. Dec 07 '11 at 15:49
  • Yes i have permissions. Read and write on the output file.. It worked now. I managed to get it working. I'll try on the server and paste here the results. Hope it works – Paulo Rodrigues Dec 07 '11 at 16:04
  • I tested in the server but it is not working. When trying to write to the file, instead of my result, it writes "sh: php: command not found".This is weird, because in the command shell of the server, if i call the command "php myfile.php" it works fine and it writes to the file the output it was supposed. But when doing this process through the php script the message that appears in the file is that "sh: php: command not found". What might be going on? – Paulo Rodrigues Dec 07 '11 at 16:28
  • use the full path for php binary, like: `/usr/bin/php` or `/usr/local/bin/php` – Paulo H. Dec 07 '11 at 16:42
  • Ok it works like a charm! One more thing, what about if i want to send parameters to my php file? My command is like this "/usr/bin/php my_script.php > output.txt...." as you mentioned. I'm using the sprintf too. But if i want to send some variable like "word" to get it with $argv[1] in my script? – Paulo Rodrigues Dec 07 '11 at 17:12
  • `/usr/bin/php my_script.php yourarghere > output.txt` don't forget to `escapeshellarg($arg)` this argument and accept this answer =P – Paulo H. Dec 07 '11 at 17:16
  • Thks a lot for your help. I had tryed to put parameters that way but i was getting a error because of a silly mistake :p Many tanks, the problem is solved. You'd deserve a +5!!! – Paulo Rodrigues Dec 07 '11 at 18:49
0

By forking a process the child process will be in the same process group. Perhaps Apache, or whatever web server you are using is waiting for all processes in the group to end before detaching and ending the request.

Try calling setsid() in the child after forking which should put it as a leader in a new group.

http://www.php.net/manual/en/function.posix-setsid.php

SlappyTheFish
  • 2,344
  • 3
  • 34
  • 41
  • Hmm your solution worked!! but why? I have a simple example in another script, where i fork a child process, and then the child waits 60 seconds, and the parent redirects to google. If i run that script, the parent does not waits for the child to exit. But this effects isn't happening in the script i described here in this topic. Why have i to do setsid() in this case? – Paulo Rodrigues Dec 07 '11 at 12:38
  • After all, this solution did not worked :p When i put setsid() in the child, the parent does not waits anymore for the child to exit, but the child by some reason does not execute. In other words, i didn't received the sms he was suppose to send.. – Paulo Rodrigues Dec 07 '11 at 12:41
  • You'll need to check php.ini to find where the log file is, it's usually in somewhere like /var/log/php – SlappyTheFish Dec 07 '11 at 12:50
  • Also, check the return value of setsid(). – SlappyTheFish Dec 07 '11 at 12:53
  • my php_ini says: "error_log: no value". I can't seem to find it – Paulo Rodrigues Dec 07 '11 at 12:54
0

instead of

 exit();

try using this

posix_kill(getmypid(),9);

and please tell if this works.

noobie-php
  • 6,817
  • 15
  • 54
  • 101
  • Unfortunately it didn't work. I replaced the exit in the son with that instruction you gave me, but the parent still waits 60 seconds for the son till a new page is returned to the browser – Paulo Rodrigues Dec 07 '11 at 12:35