编程

PHP Fiber: PHP 如何最终向异步编程靠拢

50 2025-04-18 22:50:00

多年来,PHP 一直是 web 开发的首选语言,为从小型博客网站到脸书等大型平台(至少在早期)的一切提供动力。但是,尽管 PHP 发展迅速,但直到现在,它在异步编程方面一直有点保守。随着 PHP Fiber 在 PHP 8.1 中的出现(以及 PHP 8.4 中急切的调整),PHP 似乎终于拥抱了并发的。

那么,让我们深入了解一下 PHP Fiber:它是什么,它如何工作的。

Fiber 到底是什么,你为什么要关心?

将纤程(Fiber)视为轻量级的线程——它允许你暂停和恢复函数,而不会阻塞整个应用。想象一下,你在咖啡店排队,点了一杯拿铁。当你等待的时候,你可以暂停订单,查看电子邮件或与朋友聊天,而不是只是站在那里什么都不做。当你的拿铁准备好了,你会毫不犹豫地重新开始点餐。

在代码中,纤程允许你的应用在等待耗时的任务完成的同时继续做有用的工作。与传统的同步 PHP 代码不同,在传统的同步 PHP 代码中,所有内容都是线性运行(一个接一个命令,不能跳过前面),Fiber 允许你进出任务——非常适合那些负担不起等待的实时应用程序。

Important Note on Versions: Fibers were introduced in PHP 8.1, but they’re fully functional and more stable in PHP 8.4. So if you’re still on PHP 7.x or 8.0, now might be the time for an upgrade.

为什么使用 Fiber? PHP 还没有异步选项吗?

你可能会想,“不是有像 pcntl_fork 和 ReactPHP 这样的并发选项吗?”对的。但这些方法要么很复杂,要么需要额外的扩展和库。此外,它们不允许你像纤程那样自然地编写异步代码。

使用纤程,你不必跳过环或安装额外的扩展。只需编写代码,加入一些纤程,就可以开始了。

开始使用 PHP 纤程(Fiber)

现在,让我们尝试一些代码,了解一下纤程是如何工作的。首先,让我们检查一下你的 PHP 版本是否已经为纤程革命做好了准备:

php -v

如果是 PHP 8.1 及以上版本,那么一切就绪。

Fiber 基础

PHP Fiber 的魅力在于其简洁性。以下是如何创建 Fiber 并使其启动和运行:

$fiber = new Fiber(function() {
  echo "Fiber started…\n";
  Fiber::suspend(); // Pause the fiber
  echo "Fiber resumed…\n";
});
echo "Fiber starting…\n";
$fiber->start(); // Start the fiber
echo "Main script running…\n";
$fiber->resume(); // Resume the fiber

以下将解释发生了什么:

1. 我们创建了一个新的纤程,其中有一个函数做了两件事:打印一条消息,暂停自己,然后打印另一条消息。

2. 启动纤程,使之开始执行直至遇到 Fiber::suspend()

3. 纤程在遇到 Fiber::suspend() 时暂停自己,允许 main 脚本继续执行。

4. 稍后,我们调用 $fiber->resume(),在其暂停位置拾起纤程并完成其任务。

就是这样。我们在 PHP 中创建了一个异步代码。

实例:使用 Fiber 并发获取数据

假设您想从多个 API 获取数据,而无需等待每个请求完成,然后再继续下一个请求。纤程允许你开启每个请求,暂停它,然后稍后恢复它——让你的主脚本在此期间执行其他任务。

<?php

// Function to create a fiber that simulates fetching data from an API
function fetchData(string $url, int $delay) {
    return new Fiber(function() use ($url, $delay) {
        echo "Starting request to $url...\n";
        sleep($delay); // Simulate network delay
        Fiber::suspend("Data from $url"); // Pause fiber and return simulated data
    });
}

// Initialize fibers for each "API request"
$fiber1 = fetchData('https://jsonplaceholder.typicode.com/posts/1', 2);
$fiber2 = fetchData('https://jsonplaceholder.typicode.com/posts/2', 3);

// Start fibers (this initiates each "request" and pauses at Fiber::suspend)
$fiber1->start();
$fiber2->start();

echo "Performing other tasks while waiting for data...\n";

// Collect data once fibers have completed their simulated requests
$data1 = $fiber1->resume();
$data2 = $fiber2->resume();

echo "$data1\n";
echo "$data2\n";
echo "All data fetched successfully.\n";

解析

  1. 创建纤程: fetchData 函数为每个 API 请求创建一个纤程。每个纤程模拟一个延迟、暂停并当数据准备好时返回一条消息。
  2. 启动纤程: start() 初始化每个纤程,纤程在启动“请求”后挂起,允许其他代码在它“等待”的同时运行。
  3. 非阻塞执行:启动两个纤程后,主脚本继续执行其他任务(由 echo 语句指示)
  4. 恢复纤程:一旦结果准备好后,在每个纤程上调用 resume() 以检索数据。当调用 resume() 时,将返回 Fiber::suspend() 中挂起的数据,从而完成“请求”。

起初,这可能看起来有点奇怪,但想象一下……在后台获取数据的同时,你可能正在处理应用的其他部分(如渲染页面或处理用户输入)

真实世界用例:异步图像处理

让我们更进一步。想象一下,你有一个用户可以上传图像的应用,你想对这些图像应用几个过滤器。处理每个过滤器可能需要一些时间,所以让我们在后台使用纤程来处理所有过滤器:

<?php

function applyFilter($image, $filter) {
    return new Fiber(function() use ($image, $filter) {
        echo "Applying $filter to $image...\n";
        sleep(2); // Simulate filter processing delay
        Fiber::suspend("$filter applied to $image."); // Suspend fiber and provide completion message
    });
}

$image = 'user-uploaded-image.jpg';
$filters = ['grayscale', 'blur', 'sharpen'];

// Create a fiber for each filter
$fibers = array_map(fn($filter) => applyFilter($image, $filter), $filters);

// Start all fibers
foreach ($fibers as $fiber) {
    $fiber->start();
}

echo "Processing other tasks while filters are being applied...\n";

// Resume each fiber to complete processing and collect results
foreach ($fibers as $fiber) {
    if ($fiber->isSuspended()) {
        $result = $fiber->resume();
        echo "$result\n";
    }
}

echo "All filters have been applied.\n";

此示例使用纤程同时对图像应用三个不同的滤波器。在应用过滤器的同时,脚本可以继续并“处理其他任务”。当我们准备就绪时,我们恢复每条纤程以完成过滤过程。

PHP 8.4 及以后:纤程的下一步是什么?

虽然 PHP 8.1 中的纤程是一个令人兴奋的开始,但 PHP 8.4 带来了一些改进,使其在生产使用中更加稳定和高效。版本 8.4 修复了纤程内存管理和并发处理方面的一些问题,使纤程更具可预测性和鲁棒性。

如果你正在深入 PHP 异步编程,使用 PHP 8.4 将提供更流畅的体验。值得一提的是,纤程是一个底层的功能——如果你正在构建复杂的应用,可以考虑使用一个更高级的库(如 ReactPHP),它可以抽象一些细节,并提供额外的工具来管理并发性。

提醒

纤程是一种强大的工具,但强大的力量带来了巨大的责任。很容易有点疯狂,开始暂停并恢复一切。但是过度使用会使你的代码难以阅读和调试,所以要明智地使用纤程。记住:纤程的使用就像杂耍燃烧的剑——令人兴奋,但最好掌握基础知识。

小结

PHP 纤程是 PHP 开发者的一个很好的补充,他们长期以来一直希望有一种更简单的方法来处理异步编程。它们提供了一种清晰、可管理的并发工作方式,没有线程或外部库的复杂性。从异步 API 调用到后台处理,纤程为 PHP 打开了一个可能性的世界——使其更加现代、功能更加强大,而且更加有趣。

因此,如果你使用的是 PHP 8.1 或更高版本,请在下一个项目中尝试使用 Fiber。它们可能会成为你最喜欢的新功能!