April 14, 2010

Running a Background Process from PHP on Linux

While PHP is capable of handling just about anything, there are times when you must take the load away from PHP and hand it over to another program. Processing huge amount of data is one case; examples include sending thousands of emails, moving thousands of files from one location to another, batch processing megabytes of image data or converting an uploaded video from one format to another.

Scenarios like these pose a few challenges. You cannot expect the user to wait for too long. You cannot handover the program control to a time consuming script. And you will have to deal with time limits both on the server and client sides.

A solution to these problems is to spawn a background process and transfer the processing load to it. A background process returns control immediately to the calling program and continues.

Running a Process

PHP provides a variety of functions for executing processes; examples include exec, passthru and system. The system function can be used to execute a process as follows:

<?php system('sh test.sh'); ?>

This example invokes the sh process, the resulting output is sent directly to the browser and PHP waits until the process is finished. In order to force this process to run asynchronously (without PHP having to wait for it to finish), we must use control operators that run the process in background and redirect its output.

Running a Background Process and Redirecting Standard Output

The > operator can be used to redirect the standard output generated by the process to a suitable location, while the & operator sends the process to the background and returns the control back to the calling process. You can discard the output by sending it to /dev/null:

<?php system('sh test.sh >/dev/null &'); ?>

or send it to a file instead:

<?php system('sh test.sh >test-out.txt &'); ?>

Redirecting Standard Error

It is a good practice to log error messages generated by the process, so that you'll always know if something goes wrong. You can have separate files for standard and error streams:

<?php system('sh test.sh >test-out.txt 2>test-err.txt &'); ?>

or just one:

<?php system('sh test.sh >test-out.txt 2>&1 &'); ?>

Capturing Process ID

It is also a good practice to keep a track of id of the process you execute in the background. You can retrieve it via the $! operator and store it in a variable:

<?php $pid = system('sh test.sh >test-out.txt 2>&1 & echo $!'); ?>

Monitoring Process

Assuming that you record id of your background process as described above, it is fairly simple to monitor its status at any time using shell commands. You can use the ps command for this purpose:

<?php $status = system('ps ' . $pid); ?>

If the process is running, the above command will return an output similar to:

 PID TTY STAT TIME COMMAND
2044 ?   S    0:29 sh test.sh

If the process goes rogue, you can terminate it via the kill command (be cautious).

<?php system('kill ' . $pid); ?>

Notes

If you are wondering if it is possible to run PHP as a background process, the answer is YES. You can spawn an instance of PHP CLI from your PHP webpages.

References