Real-time PHP

PHP finally has
real-time superpowers.

php-via turns your PHP into a reactive, multiplayer-ready server. No JavaScript. No build step. No API layer. Just PHP and OpenSwoole.

3 people on this page right now

This is all the code.

A counter in ~15 lines of PHP. Switch the tab to see the only change needed to make it multiplayer — one line sets the scope. No JavaScript. No WebSockets. No API glue.

 1<?php
 2require __DIR__ . '/vendor/autoload.php';
 3
 4use Mbolli\PhpVia\Config;
 5use Mbolli\PhpVia\Context;
 6use Mbolli\PhpVia\Via;
 7
 8$app = new Via(new Config());
 9
10$app->page('/', function (Context $c): void {
11    // TAB scope (default) — each visitor has their own counter
12    $count = $c->signal(0);
13
14    $increment = $c->action(
15        fn() => $count->setValue($count->int() + 1)
16    );
17
18    $c->view(fn() => <<<HTML
19        <p>Count: <strong data-text="$count">
20            {$count->int()}
21        </strong></p>
22        <button data-on:click="@post('{$increment->url()}')">
23            +1
24        </button>
25    HTML);
26});
27
28$app->start();
   1<?php
   2require __DIR__ . '/vendor/autoload.php';
   3
   4use Mbolli\PhpVia\Config;
   5use Mbolli\PhpVia\Context;
 6 +use Mbolli\PhpVia\Scope;
   7use Mbolli\PhpVia\Via;
   8
   9$app = new Via(new Config());
  10
  11$app->page('/', function (Context $c): void {
12 -    // TAB scope (default) — each visitor has their own counter
13 +    // GLOBAL scope — one shared counter for every visitor
14 +    $c->scope(Scope::GLOBAL);
  15    $count = $c->signal(0);
  16
  17    $increment = $c->action(
  18        fn() => $count->setValue($count->int() + 1)
  19    );
  20
  21    $c->view(fn() => <<<HTML
  22        <p>Count: <strong data-text="$count">
  23            {$count->int()}
  24        </strong></p>
  25        <button data-on:click="@post('{$increment->url()}')">
  26            +1
  27        </button>
  28    HTML);
  29});
  30
  31$app->start();
Your counter — private to your tab
0
Only visible to you
Live — shared with everyone on this page
2378
Last click: Visitor #CE4B

PHP deserves real-time.

The PHP ecosystem bent itself into a pretzel trying to add real-time to the stack. WebSockets bolted onto traditional PHP-FPM. Livewire polling every second. Inertia.js adding a React layer on top of Laravel. All of these add complexity, JavaScript, and build tooling to solve a problem that should be solved at the server.

php-via takes a different approach. Built on OpenSwoole's persistent event loop, every page maintains a live SSE connection. When your PHP changes state, the browser updates instantly — no polling, no WebSocket handshake ceremony, no client-side state management. The server is the source of truth, and the browser is a live view into it.

The scope system is the killer feature. Set a signal to Scope::ROUTE and it automatically synchronizes across every browser on that route. Change it to Scope::SESSION and it follows the user across their tabs. Change it to Scope::GLOBAL and every user in the entire application sees the update. One line of code changes who sees what.

We built this because PHP developers deserve the same multiplayer-by-default experience that Elixir's LiveView gives to Elixir developers. Without needing to learn Elixir.

Everything you need. Nothing you don't.

🚫

No JavaScript

Write server-side PHP. Datastar handles client-side reactivity through HTML attributes. Zero JavaScript to author.

No build step

php app.php and you're running. No bundler, no transpiler, no npm install before you can see a page.

🎯

Scoped state

TAB, ROUTE, SESSION, and GLOBAL scopes. One line changes whether state is private to a visitor or shared across everyone.

🤝

Multiplayer by default

Route-scoped signals broadcast to every user on the page. Build collaborative apps without pub/sub infrastructure.

🔄

Single SSE stream

One persistent connection per client carries all updates. Compresses exceptionally well with Brotli over a reverse proxy.

🧩

Components

Compose complex pages from isolated sub-contexts. Each component gets its own signals, actions, and rendering scope.

Vote. Watch it update everywhere.

Cast a vote. Every browser on this page sees the bars shift in real-time. That's Scope::ROUTE broadcasting in action.

Live poll — vote updates for everyone instantly

What's your favorite php-via scope?

121
76
97
2960

Three primitives. That's the whole API.

Every php-via application is built from signals (reactive state), actions (event handlers), and views (rendering). The framework wires them together over SSE automatically.

1

Signal

Declare reactive state. The browser watches it and updates when it changes.

$c->signal(0, 'count')
2

Action

Handle browser events server-side. Modify signals in the handler.

$c->action(fn() => ...)
3

View

Render HTML with Twig or inline PHP. The framework pushes updates via SSE.

$c->view('page.twig')
🌐

Browser

Datastar applies the HTML patch. No page reload. No flash. Instant.

Honest comparison.

Every tool has trade-offs. Here's where php-via stands.

Feature php-via Livewire htmx + Alpine Inertia.js LiveView
Native real-time push ~Requires Laravel Reverb + Echo for server push; native Livewire uses HTTP round-trips. ~SSE extension available but requires manual wiring.
Multiplayer / shared state
Scoped state system ~PubSub topics achieve similar results without a declarative scope system.
No JS to author ~Alpine.js is bundled; complex interactions may need JS hooks. ~JS hooks needed for some client-side interactions.
No build step
File uploads
Form validation ~HTML5 constraint API only; no built-in server-side validation.
Auth / middleware coming soon
Ecosystem & community Early Large Medium Large Large
Multi-server scaling
Framework required No Laravel No AnyPrimarily targets Laravel but also supports Rails, Django, and Phoenix. Phoenix
Language PHP PHP PHP + JS PHP + JS Elixir
Runtime OpenSwoole PHP-FPM PHP-FPM PHP-FPM BEAM VM

Try it in under 5 minutes.

composer require mbolli/php-via