sj-i / ffi-zts-parallel
PHP-side wrapper around pecl/parallel for use through sj-i/ffi-zts (NTS host -> embedded ZTS interpreter -> real OS threads).
Package info
github.com/sj-i/ffi-zts-parallel
Type:composer-plugin
pkg:composer/sj-i/ffi-zts-parallel
Requires
- php: >=8.5,<8.6
- composer-plugin-api: ^2.0
- ext-ffi: *
- sj-i/ffi-zts: ^2.0
Requires (Dev)
- composer/composer: ^2.0
- phpunit/phpunit: ^11.0
- dev-main
- v2.0.0
- v1.1.0
- v1.0.0
- dev-claude/php-ffi-zend-string-e6Hga
- dev-claude/readme-fixes
- dev-claude/php-8.5
- dev-claude/composer-plugin
- dev-claude/align-libphp-prefix
- dev-claude/trace-build-parallel
- dev-claude/pin-pecl-parallel-url
- dev-claude/fix-fetch-libphp-sigpipe
- dev-claude/implement-design-spec-e3hgF
This package is auto-updated.
Last update: 2026-04-20 14:06:35 UTC
README
NTS-hosted wrapper around
pecl/parallel
that runs through sj-i/ffi-zts.
Gives a plain non-thread-safe PHP CLI access to real OS-thread
parallelism via parallel\Runtime, without replacing the system
PHP build. See docs/DESIGN.md
in the core package for the full design.
Install
composer require sj-i/ffi-zts-parallel
Both sj-i/ffi-zts and sj-i/ffi-zts-parallel ship as Composer
plugins. On install, each downloads the pre-built binary matching
your host's PHP minor / CPU arch / libc:
vendor/sj-i/ffi-zts/bin/libphp.sovendor/sj-i/ffi-zts-parallel/bin/extensions/ffi-zts/parallel.so
Trusting the plugins
Composer 2.2+ asks you to trust a new plugin before running it.
In interactive shells you get a (y/N) prompt on first install.
In CI / non-interactive environments, whitelist both up front:
composer config allow-plugins.sj-i/ffi-zts true composer config allow-plugins.sj-i/ffi-zts-parallel true composer require sj-i/ffi-zts-parallel
Manual install / retry
If either plugin was skipped (--no-plugins, network outage, binary
not yet published for a new PHP minor), retry the binary fetch on
demand from the root project:
vendor/bin/ffi-zts install # fetches libphp.so # parallel.so is re-fetched on the next `composer update sj-i/ffi-zts-parallel`.
Usage
<?php require __DIR__ . '/vendor/autoload.php'; use SjI\FfiZts\Parallel\Parallel; Parallel::boot() ->runScript(__DIR__ . '/worker.php');
A minimal worker.php that fans out four parallel\Runtimes inside
the embed:
<?php $futures = []; for ($i = 0; $i < 4; $i++) { $rt = new parallel\Runtime(); $futures[] = $rt->run(function (int $id): array { $sum = 0; for ($j = 0; $j < 2_000_000; $j++) $sum += $j; return ['id' => $id, 'pid' => getmypid(), 'zts' => PHP_ZTS, 'sum' => $sum]; }, [$i]); } foreach ($futures as $f) { print_r($f->value()); }
All four workers report the same pid (same process) and
zts=true (running in the embedded ZTS interpreter).
opcache.preload (2.x / PHP 8.5)
PHP 8.5 links opcache statically into libphp.so, so
opcache.preload works under the embed out of the box and the
preloaded classes / functions propagate into every
parallel\Runtime worker thread:
Parallel::boot() ->withIniEntry('opcache.enable_cli', '1') ->withIniEntry('opcache.preload', __DIR__ . '/preload.php') ->withIniEntry('opcache.preload_user', get_current_user()) ->runScript(__DIR__ . '/worker.php');
See docs/PERFORMANCE.md
in the core repo for fork-vs-embed measurements.
Read-only / containerised environments
If the vendor directory is not writable at runtime (baked into a
container image, mounted read-only, etc.), Parallel::bootInMemory()
skips the disk cache and patches + loads parallel.so via
memfd_create(2):
Parallel::bootInMemory()->runScript('worker.php');
Versioning
Major tracks the host PHP minor:
ffi-zts-parallel1.x targets PHP 8.4 (host NTS / embedded ZTS).ffi-zts-parallel2.x targets PHP 8.5 and picks up upstream's static opcache for out-of-boxopcache.preload.
Minor / patch tracks upstream parallel releases plus wrapper
fixes; bumping just the parallel version is
composer update sj-i/ffi-zts-parallel.
Requirements
- Linux x86_64 or aarch64, glibc 2.31+
- Host NTS PHP matching the major you install -- 1.x needs
PHP 8.4, 2.x needs PHP 8.5 -- with
ext-ffienabled - Composer 2.2+
macOS, Windows, and musl are out of scope for the currently supported major lines; see the design doc for why.