PHP port of stackblitz/alien-signals 3.1.2. Line-for-line, with the same algorithm, the same flags, and the same doubly-linked-list semantics.
alien-signals is a small reactive-signals library: signals, computed
values, effects, an automatic dependency graph with pull-on-read and
push-mark-dirty propagation. Until now it ran only on JavaScript runtimes,
through npm. This artifact ports the same algorithm to PHP 8.1+, so any PHP
consumer can use it without a JavaScript bridge.
src/
ReactiveFlags.php Bit-flag constants
ReactiveNode.php Base node (deps, depsTail, subs, subsTail, flags)
Link.php Doubly-linked-list edge between dep and sub
ReactiveSystem.php Algorithm core: link, unlink, propagate,
checkDirty, shallowPropagate, startTracking,
endTracking (port of system.ts)
SignalNode.php value + previousValue
ComputedNode.php value + getter
EffectNode.php fn
Signal.php User-facing get / set
Computed.php User-facing get
Effect.php User-facing stop
Signals.php Static facade: signal, computed, effect, batch,
untrack, effectScope, trigger (port of index.ts)
use Hbt\AlienSignals\Signals;
$a = Signals::signal(1);
$b = Signals::computed(fn($prev) => $a->get() * 2);
$e = Signals::effect(fn() => print($b->get() . "\n"));
$a->set(5); // prints 10
Signals::batch(function () use ($a) {
$a->set(7);
$a->set(9);
}); // prints 18 once
$e->stop();Through Composer, once published or through a path repository:
{
"require": {
"hbt/alien-signals": "^3.1.2"
}
}For local development, point composer at the path:
{
"repositories": [
{ "type": "path", "url": "../alien-signals-php" }
],
"require": {
"hbt/alien-signals": "*"
}
}21 PHPUnit tests cover the load-bearing cases ported from upstream's
Vitest spec files:
- signal read/write semantics, value-equality skip
- computed memoisation and chained propagation
- effect lifecycle: eager run, dispose, nested stop
- batch coalescing
- untrack through
Signals::untrackand through directsetActiveSub - diamond topology, both signal-only and computed-through-checkDirty
signal-revert: writing then restoring the old value must not recompute- effectScope dispose
vendor/bin/phpunit
OK (21 tests, 57 assertions)
Single-threaded per request, matching upstream. The shared Signals
static state stays request-scoped under ordinary fpm and cli usage:
activeSub, activeScope, batchDepth, and the notify queue. Call
Signals::reset() to clear state between tests or REPL evaluations
inside one process.
Tracks upstream npm. When stackblitz publishes a new version, this artifact
moves to that version, porting the system.ts and index.ts changes.
MIT, matching upstream.