Fibers and async PHP

PHP · Expert · 15 min

Async frameworks like AMPHP and ReactPHP rely on Fibers to make concurrent I/O look like ordinary synchronous code.

A Fiber (PHP 8.1) is a function with its own call stack that can pause itself anywhere and be resumed later. Unlike a generator, it can suspend from deep inside nested calls, and the functions it calls need no special syntax.

$fiber = new Fiber(function (string $x): string {
    $y = Fiber::suspend($x . '!');   // pause, handing a value out
    return "got $y";
});
$out = $fiber->start('hi');   // runs until suspend: 'hi!'
$fiber->resume('data');       // suspend() returns 'data'
$fiber->getReturn();          // 'got data'

Fibers are cooperative and single-threaded. Only one runs at a time, and a fiber pauses only when it calls Fiber::suspend(). They give no parallelism for CPU work.

What they enable is async I/O. An event loop (the Revolt loop used by AMPHP v3 and ReactPHP's async package) starts many fibers. When one would block on a socket, the library suspends that fiber and registers interest in the socket; when data arrives, the loop resumes it. Application code just writes $response = $client->request($url);, while dozens of requests are in flight concurrently.

Most applications never touch Fiber directly; they use these libraries. Remember the constraints: blocking functions such as sleep(), file_get_contents() on URLs or PDO queries still block the whole process, so you need async-aware drivers, and state shared between fibers needs care just like in any concurrent program.

Example

<?php
// A tiny round-robin scheduler: two "tasks" interleave cooperatively
function task(string $name, int $steps): Fiber {
    return new Fiber(function () use ($name, $steps): string {
        for ($i = 1; $i <= $steps; $i++) {
            echo "$name step $i\n";
            Fiber::suspend();          // yield control back to the loop
        }
        return "$name finished";
    });
}

$queue = [task('A', 2), task('B', 3)];
foreach ($queue as $f) $f->start();   // A step 1, B step 1

while ($queue) {
    $f = array_shift($queue);
    $f->resume();
    if ($f->isTerminated()) echo $f->getReturn(), "\n";
    else $queue[] = $f;
}
// A step 2, B step 2, A finished, B step 3, B finished

Remember

  • A Fiber can suspend from anywhere in its call stack and be resumed later
  • Fibers are cooperative and single-threaded: concurrency, not parallelism
  • Event loops like Revolt use Fibers to make async I/O look synchronous

Watch out for

  • Calling blocking functions (sleep, PDO) inside an async app, which stalls every fiber.

Check yourself

  1. What does $fiber->start() return?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.php.net/manual/en/language.fibers.php

Builds on

Unlocks

Nothing builds on this yet.