This library provides a better API to work with processes on Unix-like systems using PHP.
The package is available on Packagist. You can install it using Composer.
composer require arara/process
Along with this document, there are many usage examples in the "examples/" directory which may be used for reference.
All examples within this document assume you have the following statement at the beginning of the file:
declare(ticks=1);
Without this statement, there is no guarantee PHP will handle signals; this is very important for PCNTL to work properly. It has been required since version 4.3.0 of PHP, so this is not a request of the library but of the PHP language itself.
If you want to know more about ticks, we recommend you read http://php.net/declare#control-structures.declare.ticks.
Forks may be encapsulated using Arara\Process\Action\Action
interface.
All classes that implements this interface must implement two methods:
execute(..)
: may contain the action performed in the backgroundtrigger(...)
: may contain specific actions to events
Using this interface you can create your own actions and run them in the background.
The Arara\Process\Action\Action::trigger(..)
method, as it is written, associates specific actions with events.
Those events can be:
Action::EVENT_START
: triggered before the execute() method is executedAction::EVENT_SUCCESS
: triggered when the action is finished with success, that is:- When the action does not encounter a PHP error
- When the action does not throw an exception
- When the action does not return any value
- When the action returns an
Action::EVENT_SUCCESS
value
Action::EVENT_ERROR
: triggered when the action is encounters an error, that is:- When the action encounters a PHP error
- When the action returns an
Action:EVENT_ERROR
value
Action::EVENT_FAILURE
: triggered after the action has finished and failed, that is:- When the action throws an exception
- When the action returns an
Action::EVENT_FAILURE
value
Action::EVENT_TIMEOUT
: triggered when the action experiences a timeoutAction::EVENT_FINISH
: triggered after the execute() method has executed.
In order to make it easy to execute forks with no need to create a specific class to execute something in the background, there is a generic implementation that allows a callback to be run in the background; the only thing one must do is pass the callback to the constructor of this class.
use Arara\Process\Action\Callback;
$action = new Callback(function () {
echo "This will be executed in the background!" . PHP_EOL;
});
The Callback action provides a way to bind callbacks to be triggered by specific events:
$action->bind(Callback::EVENT_SUCCESS, function () {
echo "This will be executed if the action callback was successful!" . PHP_EOL;
});
Also, one can bind a callback to multiple events:
$action->bind(Callback::EVENT_ERROR | Callback::EVENT_FAILURE, function () {
echo "It is going to be executed if the action fails or get an error" . PHP_EOL;
});
The class Arara\Process\Child
allows you to execute any action in the background.
$child = new Child(
new Callback(function (Control $control) {
echo 'PID ' . $control->info()->getId() . ' is running in the background' . PHP_EOL;
}),
new Control()
);
$child->start(); // Runs the callback in the background
The above example runs the Callback action in the background, but one can use any class which implements
the Arara\Process\Action\Action
interface.
Checking to see if a process is running is a very common routine; to perform this using this library you may call:
$child->isRunning(); // Returns TRUE if it is running or FALSE if it is not
This method not only checks the state of the object, but also checks to see if the process is already running on the system.
If the process has already started, this tells the process to terminate, but does not force it.
$child->terminate(); // Sends a SIGTERM to the process
If it has already started, this forces the process to terminate immediately.
$child->kill(); // Sends a SIGKILL to the process
If you want to wait on the process to finish, instead of just starting the process in the background, you can call:
$child->wait();
The next line of code will be executed after the process finishes.
It is possible to get the status of a process after waiting for it finish.
The Arara\Process\Child
class has a method getStatus()
which allows you to check the status of a process.
$child->getStatus(); // Returns an Arara\Process\Control\Status instance
Internally, this calls the wait()
method, in order to wait for the process to finish - and then get its status.
$child->getStatus()->getExitStatus();
$child->getStatus()->getStopSignal();
$child->getStatus()->getTerminateSignal();
$child->getStatus()->isExited();
$child->getStatus()->isSignaled();
$child->getStatus()->isStopped();
$child->getStatus()->isSuccessful();
Since you are working with forks you are able work with spawn as well. The Arara\Process\Pool
class provides a simple
way to work with it.
This class handles the queue of process dynamically, the only thing you have to do is provide the limit of children you want in the constructor and then attach the children.
$maxConcurrentChildren = 2;
$pool = new Pool($maxConcurrentChildren);
$pool->start();
$pool->attach(new Child(/* ... */));
$pool->attach(new Child(/* ... */));
$pool->attach(new Child(/* ... */));
$pool->attach(new Child(/* ... */));
// ...
The number of children it has does not matter; it will only run 2 process simultaneously; when one of those process is finished, it is removed from the queue and a new slot is opened.
The Arara\Process\Pool
class contains most of the methods of Arara\Process\Child
class:
isRunning()
kill()
start()
terminate()
wait()
This behaves similarly for all methods.
You can also handle processes without using Pool, Child or the Action classes.
We provide a simple API to work with the pcntl_*
and posix_*
functions. You can learn more by reading the
code of Arara\Process\Control
and its dependencies, but here is an example:
$control = new Control();
$pid = $control->fork();// Throws RuntimeException when pcntl_fork() returns -1
if ($pid > 0) {
echo 'Waiting on child...' . PHP_EOL;
$control->waitProcessId($pid);
echo 'Child finished' . PHP_EOL;
$control->quit();
}
echo 'Child process has PID ' . $control->info()->getId() . PHP_EOL;
echo 'Child process has parent PID ' . $control->info()->getParentId() . PHP_EOL;
$control->signal()->send('kill'); // Will send SIGKILL to the current process (the child)
If you are working with background tasks you may want to create a lock to avoid people running your script twice. For this
purpose there is the class Arara\Process\Pidfile
.
$control = new Control();
$applicationName = 'my_app';
$pidfile = new Pidfile($control, $applicationName);
$pidfile->initialize();
// Whatever you need here...
$pidfile->finalize();
The second time someone runs it an exception is thrown. We recommend you put this code into a try..catch
statement.