当大多数人谈到 PHP 时,脑海中往往浮现的是同步的请求-响应模型:一段 PHP 脚本自上而下顺序执行,每一步都会阻塞等待前一项操作完成。这套模型在很长时间里支撑起了网页和中小型应用。但如今的业务往往需要并发处理:长时间的数据库查询、流式数据处理、调用第三方 API、甚至同时维护成千上万条 WebSocket 连接。事件循环正是让 PHP 具备异步 I/O 能力的核心机制。
事件循环本质上就像一位交通管制员。它不会让程序一次只做一件事,而是将“任务”(例如文件读写、网络请求、定时器等 I/O 操作)登记到队列中,持续检测哪些任务已经就绪。
整个流程可以概括为:
小趣闻:Facebook 的 HHVM 曾尝试在 PHP 中引入原生的 async/await,但主流 PHP 暂未原生支持。像 Amp 这样的库通过生成器“模拟”出类似能力。
传统的 PHP 并没有像 Node.js 那样的内建事件循环。但 ReactPHP 和 Amp 等社区库已经提供了这一能力,它们使用纯 PHP 实现事件循环,让开发者在不改动 PHP 运行时的前提下编写异步程序。在中国大陆的生产环境中,像 Swoole、Workerman、Hyperf 以及基于 Swoole 的 Laravel Octane 也都实现了事件循环模型,它们与 ReactPHP、Amp 并不冲突:ReactPHP/Amp 偏向经典 PHP-FPM 环境的快速接入,而 Swoole、Workerman 更适合常驻内存的长连接服务。
以下示例演示如何使用 ReactPHP 创建一个简单的定时器:
<?php
require 'vendor/autoload.php';
use React\EventLoop\Factory;
$loop = Factory::create();
// 添加一个 2 秒后执行一次的定时器
$loop->addTimer(2, function () {
echo "2 seconds have passed!\n";
});
// 添加一个每秒执行一次的周期性定时器
$loop->addPeriodicTimer(1, function () {
echo "Tick: " . time() . "\n";
});
// 启动事件循环
$loop->run();
这里发生了什么?
如果你在国内用 ReactPHP 构建 IM 或 WebSocket 服务,可以结合 Redis、Kafka 等常见中间件,快速实现消息推送、数据监控看板等场景。
假设我们希望并行请求两个 API。在传统 PHP 中会先请求第一个,等待返回后再请求第二个。而借助事件循环,可以同时发起两个请求。
<?php
require 'vendor/autoload.php';
use React\EventLoop\Factory;
use React\Http\Browser;
$loop = Factory::create();
$browser = new Browser($loop);
$promises = [
$browser->get('https://jsonplaceholder.typicode.com/posts/1'),
$browser->get('https://jsonplaceholder.typicode.com/posts/2'),
];
foreach ($promises as $promise) {
$promise->then(
function (Psr\Http\Message\ResponseInterface $response) {
echo "Response: " . substr((string) $response->getBody(), 0, 50) . "...\n";
},
function (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
);
}
$loop->run();
要点:
<?php
require 'vendor/autoload.php';
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use function Amp\async;
Amp\Loop::run(function () {
$client = HttpClientBuilder::buildDefault();
$tasks = [
async(fn () => $client->request(new Request('https://jsonplaceholder.typicode.com/posts/1'))),
async(fn () => $client->request(new Request('https://jsonplaceholder.typicode.com/posts/2'))),
];
foreach ($tasks as $task) {
$response = yield $task;
$body = yield $response->getBody()->buffer();
echo "Response: " . substr($body, 0, 50) . "...\n";
}
});
要点:
无论选择哪一个,它们都经受了生产考验。若你已经在使用 Swoole/Workerman,亦可将 ReactPHP 或 Amp 作为工具库,用于快速实现并发调用或后台任务调度。
PHP 虽然起步时是同步脚本语言,但随着 ReactPHP、Amp 等事件循环库的普及,已经逐渐进化为能够处理现代并发、事件驱动业务的工具。
下次当你构建实时应用或需要优化 API 调用时,不妨尝试事件循环。PHP 不再只适用于经典的请求-响应模式,它已经可以运行异步、长驻进程,满足今天对“高并发 + 低延迟”的严苛要求。作为入门建议,可以先试试 ReactPHP 的事件循环,再体验 Amp 的协程语法,探索属于你的 PHP 新玩法。