Monday, April 25, 2011

Abandon Long Processes in PHP (But let them complete)

I have an HTML form that submits to a PHP page which initiates a script. The script can take anywhere from 3 seconds to 30 seconds to run - the user doesn't need to be around for this script to complete.

Is it possible to initiate a PHP script, immediately print "Thanks" to the user (or whatever) and let them go on their merry way while your script continues to work?

In my particular case, I am sending form-data to a php script that then posts the data to numerous other locations. Waiting for all of the posts to succeed is not in my interest at the moment. I would just like to let the script run, allow the user to go and do whatever else they like, and that's it.

From stackoverflow
  • Place your long term work in another php script, for example

    background.php:

    sleep(10);
    file_put_contents('foo.txt',mktime());
    

    foreground.php

    $unused_but_required = array();
    proc_close(proc_open ("php background.php &", array(), $unused_but_required));
    echo("Done);
    

    You'll see "Done" immediately, and the file will get written 10 seconds later.

    I think proc_close works because we've giving proc_open no pipes, and no file descriptors.

    Jonathan Sampson : Could you be a bit more descriptive in your answer?
    thomasrutter : Could you explain how it works? Looks like it tries to close the process immediately after it opens. According to proc_close documentation, it will wait for the process to terminate - isn't this what we wanted to avoid?
    Gabriel Sosa : I use this method to do threading and works really well
    Daniel Von Fange : Updated with more sample code.
    Jonathan Sampson : Daniel, how would you pass the $_POST/$_GET values to the background.php script?
    Daniel Von Fange : You could write them into a database table, and pass it the id as a argument, or you could serialize() the $_REQUEST and pass it as an argument (make sure to have it escaped)
    Bob Somers : I'd say you should avoid passing parameters to the script over the command line at all costs. All it takes is one poorly sanitized input and you've seriously compromised your system.
  • You could try the flush and related output buffer functions to immediately send the whatever is in the buffer to the browser:

  • In the script you can set:

    <?php
     ignore_user_abort(true);
    

    That way the script will not terminate when the user leaves the page. However be very carefull when combining this whith

     set_time_limit(0);
    

    Since then the script could execute forever.

  • You can use set_time_limit and ignore_user_abort, but generally speaking, I would recommend that you put the job in a queue and use an asynchronous script to process it. It's a much simpler and durable design.

  • Theres an API wrapper around pcntl_fork() called php_fork.

    But also, this question was on the Daily WTF... don't pound a nail with a glass bottle.

  • I ended up with the following.

    <?php
    
      // Ignore User-Requests to Abort
      ignore_user_abort(true);
      // Maximum Execution Time In Seconds
      set_time_limit(30);
    
      header("Content-Length: 0");
      flush();
    
      /*
    
        Loooooooong process
    
      */
    
    ?>
    

0 comments:

Post a Comment