PHPackages                             symplely/spawn - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Queues &amp; Workers](/categories/queues)
4. /
5. symplely/spawn

ActiveLibrary[Queues &amp; Workers](/categories/queues)

symplely/spawn
==============

An simply `uv\_spawn` or `proc-open` wrapper API to execute and manage a Pool of child-processes, achieving parallel/asynchronous PHP for Blocking I/O.

5.0.1(3y ago)52.5k2MITPHPPHP &gt;7.1CI failing

Since Mar 20Pushed 3y ago1 watchersCompare

[ Source](https://github.com/symplely/spawn)[ Packagist](https://packagist.org/packages/symplely/spawn)[ Docs](https://github.com/symplely/spawn)[ RSS](/packages/symplely-spawn/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (10)Dependencies (2)Versions (51)Used By (2)

Spawn
=====

[](#spawn)

[![Linux](https://github.com/symplely/spawn/workflows/Linux/badge.svg)](https://github.com/symplely/spawn/actions?query=workflow%3ALinux)[![Windows](https://github.com/symplely/spawn/workflows/Windows/badge.svg)](https://github.com/symplely/spawn/actions?query=workflow%3AWindows)[![macOS](https://github.com/symplely/spawn/workflows/macOS/badge.svg)](https://github.com/symplely/spawn/actions?query=workflow%3AmacOS)[![codecov](https://camo.githubusercontent.com/5dbd1fa3eb6a31498a46e41c2578edde399ff77261150e112d0efb7c27892f4a/68747470733a2f2f636f6465636f762e696f2f67682f73796d706c656c792f737061776e2f6272616e63682f6d61737465722f67726170682f62616467652e737667)](https://codecov.io/gh/symplely/spawn)[![Codacy Badge](https://camo.githubusercontent.com/6ce3b3e81ee38ec0c3c1817babeed97569fffc5b078c46b09a3fc464483b3fc9/68747470733a2f2f6170692e636f646163792e636f6d2f70726f6a6563742f62616467652f47726164652f3536613630333666613163383439633838623665353238323763616433326138)](https://www.codacy.com/gh/symplely/spawn?utm_source=github.com&utm_medium=referral&utm_content=symplely/spawn&utm_campaign=Badge_Grade)[![Maintainability](https://camo.githubusercontent.com/379a79b2099a5eae358e8d46382c89d67d22738533b45af211f825afd3bedc92/68747470733a2f2f6170692e636f6465636c696d6174652e636f6d2f76312f6261646765732f37363034623137623965626633313065633934622f6d61696e7461696e6162696c697479)](https://codeclimate.com/github/symplely/spawn/maintainability)

An simply **`uv_spawn`** or **`proc-open`** wrapper API to *execute* and *manage* a **Pool** of **child-processes**, achieving parallel/asynchronous PHP for Blocking I/O.

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Usage](#usage)
- [Channels Transfer messages between Child and Parent](#channels-transfer-messages-between-child-and-parent)
- [Event hooks](#event-hooks)
- [Parallel](#parallel)
- [Parallel Configuration](#parallel-configuration)
- [Behind the curtains](#behind-the-curtains)
- [Differences with original author's "Spatie/Async"](#differences-with-original-author's-%22Spatie/Async%22)
- [How to integrate into your project/package](#how-to-integrate-into-your-project/package)
- [Error handling](#error-handling)
- [Contributing](#contributing)
- [License](#license)

This package uses features of [`libuv`](https://github.com/libuv/libuv), the PHP extension [ext-uv](https://github.com/amphp/ext-uv) of the **Node.js** library. It's `uv_spawn` function is used to launch processes. The performance it a much better alternative to pcntl-extension, or the use of `proc_open`. This package will fallback to use \[symfony/process\], if `libuv` is not installed.

This package is part of our [symplely/coroutine](https://symplely.github.io/coroutine/) package for handling any **blocking i/o** process, that can not be handled by [**Coroutine**](https://symplely.github.io/coroutine/) natively.

To learn more about **libuv** features read the online tutorial [book](https://nikhilm.github.io/uvbook/index.html).

The terminology in versions **3x** and above was changed to be inline with [`ext-parallel`](https://www.php.net/manual/en/book.parallel.php) extension usage, and to behave as a `Thread`, but without many of that library extension's limitations.

The `Channeled` and `Future` classes are both designed in a way to be extend from to create your own **implementation** of a `Parallel` based library. Currently `libuv` will be required to get full benefits of the implementation.

Installation
------------

[](#installation)

```
composer require symplely/spawn
```

This package will use **libuv** features if available. Do one of the following to install.

For **Debian** like distributions, Ubuntu...

```
apt-get install libuv1-dev php-pear -y
```

For **RedHat** like distributions, CentOS...

```
yum install libuv-devel php-pear -y
```

Now have **Pecl** auto compile, install, and setup.

```
pecl channel-update pecl.php.net
pecl install uv-beta
```

For **Windows** there is good news, native *async* thru `libuv` has arrived.

Windows builds for stable PHP versions are available [from PECL](https://pecl.php.net/package/uv).

Directly download latest from

Extract `libuv.dll` to same directory as `PHP` binary executable, and extract `php_uv.dll` to `ext\` directory.

Enable extension `php_sockets.dll` and `php_uv.dll` in php.ini

```
cd C:\Php
Invoke-WebRequest "https://windows.php.net/downloads/pecl/releases/uv/0.2.4/php_uv-0.2.4-7.2-ts-vc15-x64.zip" -OutFile "php_uv-0.2.4.zip"
#Invoke-WebRequest "https://windows.php.net/downloads/pecl/releases/uv/0.2.4/php_uv-0.2.4-7.3-nts-vc15-x64.zip" -OutFile "php_uv-0.2.4.zip"
#Invoke-WebRequest "https://windows.php.net/downloads/pecl/releases/uv/0.2.4/php_uv-0.2.4-7.4-ts-vc15-x64.zip" -OutFile "php_uv-0.2.4.zip"
7z x -y php_uv-0.2.4.zip libuv.dll php_uv.dll
copy php_uv.dll ext\php_uv.dll
del php_uv.dll
del php_uv-0.2.4.zip
echo extension=php_sockets.dll >> php.ini
echo extension=php_uv.dll >> php.ini
```

Usage
-----

[](#usage)

```
include 'vendor/autoload.php';

use Async\Spawn\Spawn;

// Shows output by default and Channel instance is extracted from args.
$future = \parallel($function, ...$args)
// Shows output by default, turns on yield usage, can include additional file, and the Channel instance is extracted from args.
$future = \paralleling($function, $includeFile, ...$args)
// Or Does not show output by default and channel instance has to be explicitly passed in.
$future = \spawn($function, $timeout, $channel)
// Or Show output by default and channel instance has to be explicitly passed in.
$future = \spawning($function, $timeout, $channel)
// Or
$future = Spawn::create(function () use ($thing) {
    // Do a thing
    }, $timeout, $channel)
    ->then(function ($output) {
        // Handle success
    })->catch(function (\Throwable $exception) {
        // Handle exception
});

// Wait for `Future` to terminate. Note this should only be executed for local testing only.
// Use "How to integrate into your project/package" section instead.
// Second option can be used to set to display child output, default is false
\spawn_run($future, true);
// Or same as
$future->displayOn()->run();
// Or
$future->run();
```

Channels Transfer messages between Child and Parent
---------------------------------------------------

[](#channels-transfer-messages-between-child-and-parent)

The feature has been completely redesigned to behave similar to **PHP** [ext-parallel](https://www.php.net/manual/en/philosophy.parallel.php) extension.

See the [Channel](https://www.php.net/manual/en/class.parallel-channel.php) page for real examples.

```
include 'vendor/autoload.php';

use Async\Spawn\Channeled as Channel;

$channel = Channel::make("io");

// Shows output by default and Channel instance is extracted for args.
$future = parallel(function ($channel) {
  $channel = Channel::open($channel);

  for ($count = 0; $count send($count);
  }

  echo 'pingpangpong';
  $channel->send(false);

  return 'return whatever';
}, (string) $channel);

while (($value = $channel->recv()) !== false) {
  var_dump($value);
}

echo \spawn_output($future); // pingpangpong
// Or
echo \spawn_result($future); // return whatever
// Or
echo $future->getResult(); // return whatever
```

Event hooks
-----------

[](#event-hooks)

When creating asynchronous processes, you'll get an instance of `FutureInterface` returned. You can add the following event **callback** hooks on a `Future` process.

```
// Shows output by default and Channel instance is extracted for args.
$future = parallel($function, ...$args)
// Or
$future = spawn($function, $timeout, $channel)
// Or
$future = Spawn::create(function () {
        // The second argument is optional, Defaults no timeout,
        // it sets The maximum amount of time a process may take to finish in seconds
        // The third is the Channel instance pass to Future subprocess.

        return `whatever`|Object|Closure|; // `whatever` will be encoded, then decoded by parent.
    }, int $timeout = 0 , $input = null)
    ->then(function ($result) {
        // On success, `$result` is returned by the process.
    })
    ->catch(function ($exception) {
        // When an exception is thrown from within a process, it's caught and passed here.
    })
    ->timeout(function () {
        // When an timeout is reached, it's caught and passed here.
    })
    ->progress(function ($type, $data) {
        // Live progressing of output: `$type, $data` is returned by the Future process.
        // $type is `ERR` for stderr, or `OUT` for stdout.
    })
    ->signal($signal, function ($signal) {
        // The process will be sent termination `signal` and stopped.
        // When an signal is triggered, it's caught and passed here.
        // This feature is only available using `libuv`.
    });
```

```
->then(function ($result) {
    // On success, `$result` is returned by the Future process or callable you passed.
        //
    }, function ($catchException) {
        //
    }, function ($progressOutput) {
        //
    }
);

// To turn on displaying of child output.
->displayOn();

// Stop displaying child output.
->displayOff();

// A `Future` process can be retried.
->restart();

// Wait for `Future` to terminate. Note this should only be executed for local testing only.
// Use "How to integrate into your project/package" section instead.
->run();
```

Parallel
--------

[](#parallel)

The **Parallel** class is used to manage a Pool of `Future's`. The same *Event hooks* and *Error handling* are available.

```
include 'vendor/autoload.php';

use Async\Spawn\Parallel;

$parallel = new Parallel();

foreach ($things as $thing) {
        // the second argument `optional`, can set the maximum amount of time a process may take to finish in seconds.
    $parallel->add(function () use ($thing) {
        // Do a thing
    }, $optional)->then(function ($output) {
        // Handle success
        // On success, `$output` is returned by the process or callable you passed to the queue.
    })->catch(function (\Throwable $exception) {
        // Handle exception
        // When an exception is thrown from within a process, it's caught and passed here.
    });
}

// Wait for Parallel `Future` Pool to terminate. Note this should only be executed for local testing only.
// Use "How to integrate into your project/package" section instead.
$parallel->wait();
```

### Parallel Configuration

[](#parallel-configuration)

You're free to create as many parallel process pools as you want, each parallel pool has its own queue of processes it will handle.

A parallel pool is configurable by the developer:

```
use Async\Spawn\Parallel;

$parallel = (new Parallel())

// The maximum amount of processes which can run simultaneously.
    ->concurrency(20)

// Configure how long the loop should sleep before re-checking the process statuses in milliseconds.
    ->sleepTime(50000);
```

Behind the curtains
-------------------

[](#behind-the-curtains)

This package using `uv_spawn`, and `proc_open` as a fallback, to create and manage a **pool** of child processes in PHP. By creating child processes on the fly, we're able to execute PHP scripts in parallel. This parallelism can improve performance significantly when dealing with multiple **Synchronous I/O** tasks, which don't really need to wait for each other.

By giving these tasks a separate process to run on, the underlying operating system can take care of running them in parallel.

The `Parallel` class provided by this package takes care of handling as many processes as you want by scheduling and running them when it's possible. When multiple processes are spawned, each can have a separate time to completion.

Waiting for all processes is done by using `uv_run`, or basic child process `polling` which will monitor until all processes are finished.

When a process is **finished**, its *success* event is triggered, which you can hook into with the `->then()` function. When a process **fails**, an *error* event is triggered, which you can hook into with the `->catch()` function. When a process **times out**, an *timeout* event is triggered, which you can hook into with the `->timeout()` function.

Then the iterations will update that process's status and move on.

Differences with original author's "Spatie/Async"
-------------------------------------------------

[](#differences-with-original-authors-spatieasync)

This package differs from original author's [spatie/async](https://github.com/spatie/async) implementations:

- The `Runnable` class is **Future** with expanded capabilities.
- The `Pool` class is **Parallel** with some features extracted into another class **FutureHandler**.
- The `ParentRuntime` class is **Spawn** that can accept a `string` command line action to `execute`, returns a **Future**.
- The `async` function is **spawn** with additional **spawning** that will *display* any child process output.
- Removed output limit, no timeout unless set per `Future`, added all *Symfony* **Process** features.
- Not `Linux` or `CLI` only, runs the same in **Web** environment under **Windows** and **Apple macOS** too.
- Added a **Event Loop** library `libuv` support, it's now the main usage model, fallback to `proc-open` *Process* if not installed.
- **Libuv** allows more direct **Channel** message exchange, same is done with `proc-open` but is limited.

**Todo:** Move in all [`ext-parallel`](https://www.php.net/manual/en/book.parallel.php) like functionality from external `Coroutine` library.

> A previous [PR](https://github.com/spatie/async/pull/56) of a fork was submitted addressing real **Windows** support.

How to integrate into your project/package
------------------------------------------

[](#how-to-integrate-into-your-projectpackage)

When you include this library into your project, you can't execute functions/methods `spawn_wait()`, `spawn_run()`, `wait()` or `run()` directly. They are for mainly testing this library locally. You will need to adapt to or create a custom *event loop* routine.

The **Parallel** class has a `getFutureHandler()` method that returns a **FutureHandler** instance. The **FutureHandler** has two methods `processing()` and `isEmpty()` that you will need to call within your custom loop routine. These two calls are the same ones the `wait()` method calls onto within a `while` loop with additional `sleepingTime()`.

The `processing()` method will *monitor/check* the **Future's** *state* status and *execute* any appropriate **event callback** handler. The **FutureHandler** class can *accept/handle* a custom *Event Loop* that has **`executeTask(event callback, future)`** and **`isPcntl()`** methods defined. The custom **Event Loop** object should be supplied to **Parallel** instantiation.

***A basic setup to add to your Event Loop***

```
use Async\Spawn\Parallel;
use Async\Spawn\FutureHandler;
use Async\Spawn\FutureInterface;
use Async\Spawn\ParallelInterface;

class setupLoop
{
  /**
   * @var Parallel
   */
  protected $parallel;

  /**
   * @var FutureHandler
   */
  protected $future = null;

  public function __construct() {
    $this->parallel = new Parallel($this);
    $this->future = $this->parallel->getFutureHandler();
  }

  public function addFuture($callable, int $timeout = 0, bool $display = false, $channel = null): FutureInterface {
    $future = $this->parallel->add($callable, $timeout, $channel);
    return $display ? $future->displayOn() : $future;
  }

  public function getParallel(): ParallelInterface {
    return $this->parallel;
  }

  /**
   * Check for pending I/O events, signals, futures, streams/sockets/fd activity, timers or etc...
   */
  protected function hasEvents(): bool {
    return !$this->future->isEmpty() || !$this->ActionEventsCheckers->isEmpty();
  }

  public function runLoop() {
    while ($this->hasEvents()) {
      $this->future->processing();
      if ($this->waitForAction());
        $this->DoEventActions();
    }
  }

  public function executeTask($event, $parameters = null) {
    $this->DoEventActions($event, $parameters);
    // Or just
    // if (\is_callable($event))
       // $event($parameters);
  }

  public function isPcntl(): bool {}
}
```

This library uses [opis/closure](https://github.com/opis/closure) package for `closure/callable` serialization. For any *function* or *class* methods to be accessible in a `Future` child process you must make changes to your `composer.json` to insure it's picked up. The `composer.json` file should contain a pointer to some file with functions you always need, and insure all new classes/namespaces are within added. You can't just make local **named** `functions` or `classes` on the fly and expect them to be available.

```
// composer.json
"autoload": {
    "files": [
        "Extra/functions.php"
    ],
    "psr-4": {
        "Name\\Space\\": ["Folder/"],
        "Extra\\Name\\Spaces\\": ["Extra/"]
    }
},
```

```
// functions.php
if (!\function_exists('___marker')) {
  //
  // All additional extra functions needed in a `Future` process...
  //

  function ___marker()
  {
    return true;
  }
}
```

Error handling
--------------

[](#error-handling)

If an `Exception` or `Error` is thrown from within a child process, it can be caught per process by specifying a callback in the `->catch()` method.

If there's no error handler added, the error will be thrown in the parent process.

If the child process would unexpectedly stop without throwing an `Throwable`, the output written to `stderr` will be wrapped and thrown as `Async\Spawn\SpawnError` in the parent process.

Contributing
------------

[](#contributing)

Contributions are encouraged and welcome; I am always happy to get feedback or pull requests on Github :) Create [Github Issues](https://github.com/symplely/spawn/issues) for bugs and new features and comment on the ones you are interested in.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

32

—

LowBetter than 72% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity21

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~23 days

Recently: every ~0 days

Total

48

Last Release

1168d ago

Major Versions

1.1.10 → 2.0.02021-03-05

2.1.0 → 3.0.02021-04-29

3.1.1 → 4.0.02021-12-15

4.1.5 → 5.0.02023-03-06

4.1.8 → 5.0.12023-03-07

PHP version history (2 changes)1.0.0PHP &gt;7.2

1.1.6PHP &gt;7.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/b1a9d88c23f07f785e0358746ae2384950a6f8dac0c3bd4fbbc910e94f7eb637?d=identicon)[techno-express](/maintainers/techno-express)

---

Top Contributors

[![TheTechsTech](https://avatars.githubusercontent.com/u/29784725?v=4)](https://github.com/TheTechsTech "TheTechsTech (230 commits)")

---

Tags

asynccoroutinelibuvparallelpeclphpprocessspawnsubprocesswrapperasyncasynchronousprocessparallelfuturecommandtasklibuvproc\_openspawnuv\_spawnchild-process

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/symplely-spawn/health.svg)

```
[![Health](https://phpackages.com/badges/symplely-spawn/health.svg)](https://phpackages.com/packages/symplely-spawn)
```

###  Alternatives

[amphp/amp

A non-blocking concurrency framework for PHP applications.

4.4k123.4M323](/packages/amphp-amp)[jolicode/castor

A lightweight and modern task runner. Automate everything. In PHP.

53541.0k3](/packages/jolicode-castor)[symplely/coroutine

Cooperative multitasking using generators. The basics of coroutines, async and await!

621.6k2](/packages/symplely-coroutine)[revolt/event-loop

Rock-solid event loop for concurrent PHP applications.

92343.6M138](/packages/revolt-event-loop)[amphp/parallel

Parallel processing component for Amp.

85046.2M74](/packages/amphp-parallel)[amphp/sync

Non-blocking synchronization primitives for PHP based on Amp and Revolt.

19052.8M39](/packages/amphp-sync)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
