Extending ReactPHP's Child Processes
react/child-process
is very flexible and can work a lot of ways but sometimes you don't want to be bothered with the details of how it works and just want a simpler API to do that.
wyrihaximus/react-child-process-promise
This package was initially created as the work horse behind wyrihaximus/cpu-core-detector
for getting CPU information quick and easily.
In a nutshell it is a 20 line wrapper around react/child-process
that buffers everything coming through STDOUT
and STDERR
from the process and resolves all of that through a promise once the process exits.
To use it pass it an instance of the event loop and a child process Process
instance.
\WyriHaximus\React\childProcessPromise($loop, new Process('nproc'))->done(function (ProcessOutcome $result) {
echo 'Found ', $result->getStdout(), ' CPU cores in this machine', PHP_EOL;
});
wyrihaximus/react-child-process-messenger
A few years ago I was doing a lot of similar looking projects that did RPC communication with child processes.
Which resulted in writing wyrihaximus/react-child-process-messenger
and wyrihaximus/react-child-process-pool
(we'll go over pool in the next article) so I wouldn't have to reimplement that code in each new project.
It evolved from a wrapper around react/child-process
that handled all communication between the parent and the child. But with a lot of hands on coding required to get it running. Into a package that takes a class name and takes care of the rest.
(The more hands on approach is still possible but you don't have to.)
For example a class that checks if a given number is a prime or not:
use React\EventLoop\LoopInterface;
use WyriHaximus\React\ChildProcess\Messenger\ChildInterface;
use WyriHaximus\React\ChildProcess\Messenger\Messages\Payload;
use WyriHaximus\React\ChildProcess\Messenger\Messenger;
class Optimus implements ChildInterface
{
public static function create(Messenger $messenger, LoopInterface $loop)
{
$messenger->registerRpc('isPrime', function (Payload $payload) {
return [
'isPrime' => self::isPrime($payload['number']),
];
});
}
private static function isPrime(int $number)
{
for($i=$n>>1;$i&&$n%$i--;);return!$i&&$n>1;
}
}
(Note: the returned resolve always has to be an array because the communication is JSON serialized and assumes arrays.)
All we need to do is tell the MessengerFactory
that we want to create a new parent from the Optimus
class and it takes care of creating a new parent messenger, spawn the child process and then resolve the promise notifying the user it is ready for use.
use React\EventLoop\Factory;
use WyriHaximus\React\ChildProcess\Messenger\Factory as MessengerFactory;
use WyriHaximus\React\ChildProcess\Messenger\Messages\Factory as MessageFactory;
use WyriHaximus\React\ChildProcess\Messenger\Messages\Payload;
use WyriHaximus\React\ChildProcess\Messenger\Messenger;
MessengerFactory::parentFromClass(\Optimus::class, $loop)->then(function (Messenger $messenger) {
return $messenger->rpc(
MessageFactory::rpc('isPrime', ['number' => 66])
)->always(function () use ($messenger) {
$messenger->softTerminate(); // Be sure to terminate the child when we're done
});
})->done(function (Payload $result) {
if ($result['isPrime']) {
echo 'Prime', PHP_EOL;
return;
}
echo 'Not a prime', PHP_EOL;
});
Conclusion
Child processes are very useful for a lot of different purposes, from image manipulation till gathering output from existing programs. These two packages are create to make such operations easier. In the next post will go over two other packages for more advanced and powerful uses.