A multi-process container. It looks like multi-thread-ish.

0.13.0 2019-03-17 09:05 UTC


A multi-process container. It looks like multi-thread-ish.

Latest Stable Version License Build Status Scrutinizer Code Quality Coverage Status Minimum PHP Version

Please consider donating to this project's author, Akihito Nakano, to show your ❤️ and support.

What Snidel solves?


Not a few people, start their programming carrier with PHP, and go on. Parallel processing, they are not familiar with it, and may be a hurdle for them.

Or else, people who limited to develop with a language that is not PHP (e.g. A language that has superior feature for parallel processing). (It's me in past.)

To make parallel processing more easily and instinctively to them to use, I started developing Snidel.

Snidel can be one of your options when you are considering "How to do it parallelly?". It's an honer for me.




そのような方が、手軽に・直感的に並列処理を使って問題解決できることを目的として Snidel の開発をはじめました。

"この処理を並列に実行したいんだけどどうしよう?" といった場合に Snidel がみなさんの選択肢のひとつになれたら幸いです。

Installing Snidel via Composer

$ composer require ackintosh/snidel:~0.11.0


Master - Worker Architecture


It is also possible parallel processing via build-in functions (e.g. exec):


exec('php slow_job1.php &');
exec('php slow_job2.php &');

For the developers who feels "pain" with the above, Snidel can provides pretty good experience and will streamline their PHP programming.

We will walk through usage to show how Snidel melt parallel processing into your programming. The experience using Snidel should resolve your pain. Let's get started!


Basic Usage

use Ackintosh\Snidel;

$f = function ($s) {
    echo 'echo: ' . $s
    return 'return: ' . $s;

$s = time();
$snidel = new Snidel();
$snidel->process($f, 'foo');
$snidel->process($f, 'bar');
$snidel->process($f, 'baz');

// `Snidel::results()` returns `\Generator`
foreach ($snidel->results() as $r) {
    // string(9) "echo: foo"
    // string(11) "return: foo"

// If you don't need the results, let's use `Snidel::wait()` instead of `Snidel::results()`
// $snidel->wait();

echo (time() - $s) . 'sec elapsed' . PHP_EOL;
// 3sec elapsed.

Constructor parameters

All parameters are optional.

new Snidel([
    'concurrency' => 3,
    // Please refer to `Logging`
    'logger' => $monolog,
    // Please refer to `Using custom queue`
    'driver' => $driver,
    // a polling duration(in seconds) of queueing
    'pollingDuration' => 1,

Same arguments as call_user_func_array

// multiple arguments
$snidel->process($f, ['arg1', 'arg2']);

// global function

// instance method
$snidel->process([$instance, 'method']);

Tagging the task

$f = function ($arg) {
    return $arg;

$snidel->process($f, 'arg-A_tag1', 'tag1');
$snidel->process($f, 'arg-B_tag1', 'tag1');
$snidel->process($f, 'arg_tag2', 'tag2');

foreach ($snidel->results as $r) {
    // `Task::getTag()` returns the tag passed as 3rd parameter of `Snidel::process()`
    switch ($r->getTask()->getTag()) {
        case 'tag1':
            $r->getReturn(); // arg-A_tag1 | arg-B_tag1
        case 'tag2':
            $r->getReturn(); // arg_tag2


Snidel supports logging with logger which implements PSR-3: Logger Interface.

// e.g. MonoLog
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$monolog = new Logger('sample');
$stream = new StreamHandler('php://stdout', Logger::DEBUG);
$stream->setFormatter(new LineFormatter("%datetime% > %level_name% > %message% %context%\n"));

$snidel = new Snidel(['logger' => $monolog]);

// 2017-03-22 13:13:43 > DEBUG > forked worker. pid: 60018 {"role":"master","pid":60017}
// 2017-03-22 13:13:43 > DEBUG > forked worker. pid: 60019 {"role":"master","pid":60017}
// 2017-03-22 13:13:43 > DEBUG > has forked. pid: 60018 {"role":"worker","pid":60018}
// 2017-03-22 13:13:43 > DEBUG > has forked. pid: 60019 {"role":"worker","pid":60019}
// 2017-03-22 13:13:44 > DEBUG > ----> started the function. {"role":"worker","pid":60018}
// 2017-03-22 13:13:44 > DEBUG > ----> started the function. {"role":"worker","pid":60019}
// ...

Error informations of children

$snidel->process(function ($arg1, $arg2) {
}, ['foo', 'bar']);

// class Ackintosh\Snidel\Error#4244 (1) {
// ...
// }

foreach ($snidel->getError() as $pid => $e) {
    var_dump($pid, $e);
// int(51813)
// array(5) {
//   'status' =>  int(256)
//   'message' => string(50) "an error has occurred in child process.
//   'callable' => string(9) "*Closure*"
//   'args' =>
//     array(2) {
//       [0] => string(3) "foo"
//       [1] => string(3) "bar"
//     }
//   'return' => NULL
//   }
// }

Using custom queue

Snidel depends on Bernard as a queue abstraction layer. Bernard is a multi-backend PHP library for creating background jobs for later processing.
By default Snidel builds the flatfile driver, but from a race condition perspective, we recommend using a more reliable queue in production.

Amazon SQS
$connection = Aws\Sqs\SqsClient::factory([
    'key'    => 'your-aws-access-key',
    'secret' => 'your-aws-secret-key',
    'region' => 'the-aws-region-you-choose'
$driver = new Bernard\Driver\SqsDriver($connection);

new Snidel([
    'driver' => $driver,

For details on the driver, please see here.


Version Guidance

Snidel PHP
0.1 ~ 0.8 >= 5.3
0.9 ~ >= 5.6
0.13 >= 7.1


We suggest you give it a try with Docker as Snidel requires some php extensions shown in Requirements.

Run unit tests in docker container

curl -Ss | php
docker build -t snidel .
docker run --rm -v ${PWD}:/snidel snidel php composer.phar install
docker run --rm -v ${PWD}:/snidel snidel vendor/bin/phpunit


Snidel © ackintosh, Released under the MIT License.
Authored and maintained by ackintosh

GitHub @ackintosh / Twitter @NAKANO_Akihito / Blog (ja)

Blog entries by author about Snidel (ja):


Thanks to JetBrains for supporting us with a Free Open Source License.