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
0
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);