Documentation menu

Signals

Signals are php-via's reactive state primitive. They hold a value, and when that value changes, every browser subscribed to the context sees the update via SSE.

Creating a signal

$count = $c->signal(0);           // anonymous signal
$step  = $c->signal(1, 'step');   // named signal (better Datastar binding)

The first argument is the initial value. Signals can hold any JSON-serializable value: integers, strings, booleans, arrays.

Reading and writing

$count->int();        // read as int
$count->string();     // read as string
$count->getValue();   // raw value

$count->setValue(42); // update — queues a patch to every subscribed client

Binding in templates

In a Twig template, use Datastar attributes to bind signals to the DOM:

<!-- Display value (one-way, signal named 'count') -->
<span data-text="${count}"></span>

<!-- Two-way binding for input (signal named 'step') -->
<input data-bind="step" type="number">

<!-- Conditional visibility -->
<div data-show="$visible">...</div>

<!-- Use signal.id() when the name is auto-generated -->
<span data-text="{{ count.id() }}"></span>

Scopes & sharing

By default, signals are TAB-scoped: each visitor has their own independent copy. You can change the scope to share state across visitors:

use Mbolli\PhpVia\Scope;

// Only this browser tab sees this
$private = $c->signal(0, 'count');

// Everyone on this URL sees the same value
$shared = $c->signal(0, 'count', Scope::routeScope('/'));
$c->scope(Scope::ROUTE);  // equivalent shorthand
▶ TAB scope vs ROUTE scope — live comparison

Open another browser tab with this page open, then increment both counters. The TAB counter is yours alone; the ROUTE counter is shared.

TAB scope
0
Only you see this counter.
ROUTE scope
104
Everyone on this page shares this.

Syncing

When signals change inside an action, the framework automatically queues patches for connected clients. If you update signals outside an action (e.g., in a timer callback), call $c->sync() or $app->broadcast($scope):

$c->setInterval(function () use ($count, $c): void {
    $count->setValue($count->int() + 1);
    $c->sync(); // push the update
}, 1000);