作为一个写了多年 PHP 的老开发者,我一直有个执念:能不能只用 PHP就做出那种丝滑的实时应用?不用写一行 JavaScript,不用搞什么 npm、webpack,不用搭 WebSocket 服务器,也不用在前后端之间来回写 API 接口。
以前我觉得这是天方夜谭。为了做个实时聊天,得去学 Node.js 和 Socket.io;为了做个响应式表单,得引入 Alpine.js;为了做个多人协作看板,得搭个 Laravel Echo 加 Redis。好好的 PHP 项目,最后变成了一个大杂烩,前端代码比后端还多。
直到我发现了 php-via 这个框架,我才知道原来 PHP 也能拥有真正的实时超能力。
什么是 php-via?
简单来说,php-via 是一个基于 OpenSwoole 的实时响应式 Web 框架。它让你只用纯 PHP 就能写出和 Elixir LiveView 一样体验的应用——零 JavaScript、零构建步骤、零 API 层,所有状态都在服务端管理,浏览器只是一个实时的视图窗口。
它的核心思想非常简单:服务器是唯一的真相来源。当服务器上的状态改变时,它会通过 SSE(服务器发送事件)自动把更新推送给浏览器,浏览器再通过 Datastar 这个库把 HTML 补丁应用到 DOM 上,整个过程没有页面刷新,没有闪烁,和前端框架一样丝滑。
最惊艳的:一行代码变多人
php-via 最让我眼前一亮的是它的作用域状态系统。这是其他所有 PHP 实时框架都没有的功能。
你只需要加一行代码,就能把一个只能自己用的私有功能,变成所有人都能看到的多人功能。
比如一个最简单的计数器:
// 每个用户有自己的计数器(默认 TAB 作用域)
$count = $c->signal(0);
只要加上这一行:
$c->scope(Scope::GLOBAL);
它就立刻变成了一个全局共享的计数器——任何一个人点击按钮,所有打开这个页面的人都会看到数字变化。
没有 pub/sub,没有广播事件,没有数据库轮询,就这么一行代码。
php-via 提供了四种内置作用域,覆盖了几乎所有常见场景:
- TAB:每个浏览器标签页独立(默认),适合个人表单和设置
- ROUTE:同一路由下的所有用户共享,适合多人协作看板、在线投票
- SESSION:同一个用户的所有标签页共享,适合购物车、用户状态
- GLOBAL:全局所有用户共享,适合系统通知、全局公告
你还可以创建自定义作用域,比如 room:lobby 或者 board:123,用来实现聊天室、游戏房间等功能。
三个概念,学会整个框架
php-via 的 API 设计得极其简洁,整个框架只有三个核心概念:信号、动作、视图。
1. 信号(Signal):响应式状态
信号就是你需要在页面上显示的、会变化的数据。当你修改信号的值时,所有订阅了这个信号的浏览器都会自动更新。
// 定义一个信号,初始值为 0
$count = $c->signal(0, 'count');
// 读取信号值
echo $count->int();
// 修改信号值,自动推送到所有客户端
$count->setValue(10);
在模板里,你只需要用 HTML 属性绑定信号即可:
<!-- 显示信号值 -->
<spandata-text="${{ count.id }}">{{ count.int }}</span>
<!-- 双向绑定输入框 -->
<inputtype="number"data-bind="{{ step.id }}">
2. 动作(Action):服务端事件处理
动作就是当用户点击按钮、提交表单时,在服务端执行的函数。所有用户交互都直接触发服务端代码,不需要写任何前端事件处理。
// 定义一个动作
$increment = $c->action(function()use($count){
$count->setValue($count->int() + 1);
}, 'increment');
在模板里绑定动作:
<buttondata-on:click="@post('{{ increment.url }}')">+1</button>
3. 视图(View):HTML 渲染
php-via 支持原生 PHP 字符串和 Twig 模板两种渲染方式,和你平时写 PHP 模板没有任何区别。
// 内联 HTML
$c->view(fn() => <<<HTML
<p>Count: {$count->int()}</p>
HTML);
// Twig 模板
$c->view('counter.html.twig', [
'count' => $count,
'increment' => $increment,
]);
就是这么简单。没有复杂的生命周期,没有状态管理库,没有组件通信问题。所有逻辑都在服务端,所有状态都在服务端,所有更新都自动同步。
它是怎么工作的?
很多人会好奇,不用 WebSocket 怎么实现实时通信?其实 php-via 用的是 SSE(Server-Sent Events) 技术。
整个工作流程非常清晰:
- 浏览器请求页面,服务器渲染完整的 HTML 并建立一个持久的 SSE 连接
- 用户点击按钮,Datastar 自动把当前所有信号值和动作 ID POST 到服务器
- 服务器通过 SSE 流把 HTML 补丁和信号更新推送给浏览器
- Datastar 在客户端应用 DOM 变形,UI 无刷新更新
和 WebSocket 相比,SSE 有很多优势:它是基于 HTTP 的,更容易穿越防火墙和代理;它是单向的,更适合服务器向客户端推送更新;它的连接开销更低,一个 SSE 流可以承载所有更新。
能用来做什么?
php-via 已经有非常多成熟的示例,你可以在官网直接体验:
这些示例全部都是用纯 PHP 写的,没有一行自定义 JavaScript。
快速上手,5 分钟跑起来
php-via 的安装和使用非常简单,不需要任何复杂的配置。
系统要求
安装
composer require mbolli/php-via
创建你的第一个应用
新建一个 app.php 文件:
<?php
require'vendor/autoload.php';
useMbolli\PhpVia\Via;
useMbolli\PhpVia\Config;
useMbolli\PhpVia\Context;
useMbolli\PhpVia\Scope;
$app = new Via(new Config());
$app->page('/', function(Context $c): void{
// 全局共享计数器
$c->scope(Scope::GLOBAL);
$count = $c->signal(0);
$increment = $c->action(fn() => $count->setValue($count->int() + 1));
$c->view(fn() => <<<HTML
<!DOCTYPE html>
<html>
<head>
<title>php-via 计数器</title>
</head>
<body style="text-align: center; margin-top: 100px;">
<h1>全局共享计数器</h1>
<p style="font-size: 48px;">Count: <strong data-text="$count">{$count->int()}</strong></p>
<button style="font-size: 24px; padding: 10px 20px;" data-on:click="@post('{$increment->url()}')">+1</button>
<p>打开多个浏览器标签页试试,点击按钮所有页面都会同步更新!</p>
</body>
</html>
HTML);
});
$app->start();
运行
php app.php
然后打开浏览器访问 http://localhost:3000,你就拥有了一个实时多人计数器!
写在最后
php-via 目前还处于早期阶段,生态和社区肯定不如 Laravel 那么成熟。但它给 PHP 开发带来了一种全新的可能性——不用再为了实时性而被迫学习前端技术栈。
对于很多中小型项目来说,php-via 简直是神器。你可以用它快速开发出体验极佳的实时应用,而不用在前后端之间来回切换,不用维护两套代码,不用处理复杂的状态同步问题。
如果你和我一样,是一个热爱 PHP 的开发者,厌倦了现在前端的复杂生态,一定要去试试 php-via。它可能会改变你对 PHP 能做什么的认知。