Download the PHP package mbolli/php-via without Composer
On this page you can find all versions of the php package mbolli/php-via. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download mbolli/php-via
More information about mbolli/php-via
Files in mbolli/php-via
Package php-via
Short Description Real-time engine for building reactive web applications in PHP
License MIT
Informations about the package php-via
php-via
Real-time reactive web framework for PHP. Server-side reactive UIs with zero JavaScript, using OpenSwoole for async PHP, Datastar for SSE + DOM morphing, and optionally Twig for templates.
Documentation & Live Examples · Upgrading from 0.13? See Upgrading to 0.14.
Why php-via?
- No JavaScript to write: Datastar handles client-side reactivity, SSE, and DOM morphing
- Closures or Twig: views are closures that return HTML, or Twig templates once
twig/twigis installed - No build step: no transpilation, no bundling, no node_modules
- Real-time by default: every page gets a live SSE connection
- Scoped state: TAB, ROUTE, SESSION, GLOBAL, and custom scopes control who shares what
- Single SSE stream: extremely efficient with Brotli compression
- Web components: Rocket and Starbase components bound to your signals, with an opt-in Datastar build
Requirements
- PHP 8.4+
- OpenSwoole PHP extension 26+
- Composer
- Brotli PHP extension (optional: static files get Brotli level 11 once it is loaded, pages and SSE with
Config::withBrotli())
Installation
Quick Start
counter.html.twig:
Core Concepts
Full documentation at via.zweiundeins.gmbh/docs
Signals: reactive state that syncs between server and client
A signal is private to its tab unless you pass a scope: $c->signal(0, 'votes', Scope::ROUTE).
Actions: server-side functions triggered by client events
Important: Always trigger actions with
@post()(or@patch/@put/@delete).@get()is blocked on/_action/…: GET requests return 405 Method Not Allowed, because allowing actions over GET enables top-level cross-site navigation CSRF.
Scopes: control who shares state and receives broadcasts
| Scope | Sharing | Use Case |
|---|---|---|
Scope::TAB |
Isolated per tab (default) | Personal forms, settings |
Scope::ROUTE |
All users on same route | Shared boards, multiplayer |
Scope::SESSION |
All tabs in same session | Cross-tab state |
Scope::GLOBAL |
All users everywhere | Notifications, announcements |
Custom ("room:lobby") |
All contexts in that scope | Chat rooms, game lobbies |
$c->scope() sets the scope a context's view and $c->broadcast() belong to. Signals take their scope as an
argument, and a view shares its update render only with shareRender: true.
Views: closures or Twig templates
A string is always a template name. Pass the template data as a closure when it changes after the page load, and
shareRender: true only for a view that is the same for every tab in its scope.
Path Parameters: auto-injected by name
Components: reusable sub-contexts with isolated state
Lifecycle Hooks
onCleanupfires after a grace period (default: 5 seconds) that tolerates page navigation and brief reconnects without tearing down state. Tune it withConfig::withContextTimeouts(cleanupDelayMs: ...).If a tab is backgrounded long enough that its context is destroyed, the returning tab revives: the server rebuilds an equivalent context (same ID) and re-seeds the signal values the client still holds, instead of hard-reloading and losing local signals, scroll, and focus. On by default (10-minute window); tune or disable with
Config::withContextTimeouts(revivalWindowMs: ...). Revival re-runs the page handler, so server-only#[Persist]state resets and lifecycle hooks re-fire, just as on a reload. Server-owned TAB signals (clientWritable: false, or all of them underConfig::withStrictTabSignals()) start from the handler's initial value, and values kept with$c->setTabState()come back. The route's middleware runs again on a revival, so an auth gate applies. WithwithWorkerNum()above 1 a tab lives on the worker of its SSE stream, and actions that reach another worker are passed there; when the stream reconnects to another worker, the context is rebuilt the same way and takes the TAB signal values the old worker still holds.
Route Groups: shared prefix and/or middleware
Broadcasting: push updates to other connected clients
Multi-node broadcasting: Redis and NATS brokers
One worker needs no broker. Several workers on one machine (withWorkerNum()) use SwooleBroker
unless you pass another, with no external service. To fan out broadcast() calls across multiple
servers or containers, swap in RedisBroker or NatsBroker:
Both brokers reconnect automatically with exponential backoff (1 s → 30 s cap).
A GET /_health endpoint is available on every php-via server (no configuration needed):
Returns HTTP 503 when the broker is in the reconnect backoff window.
Testing: TestApp runs your pages without a server
How it Works
Development
Hot PHP reload: edit a file in website/src/ or a template, and the workers restart (~1 s) without dropping other connections. See docs/development for the full workflow and how to replicate this pattern in your own project.
Credits
- Datastar: SSE + DOM morphing
- OpenSwoole: Async PHP
- Twig: Templating, optional
- go-via/via: Original Go inspiration
License
MIT
Maintainer
Maintained by Michael Bolli at zwei und eins gmbh, Aarberg, Switzerland.
All versions of php-via with dependencies
ext-openswoole Version ^26.0
starfederation/datastar-php Version ^1.0
psr/http-server-middleware Version ^1.0
nyholm/psr7 Version ^1.8
openswoole/core Version ^26.2
