PHP 8.1 — Fibers (green threads)
Concept
Fibers (PHP 8.1) are "green threads" — a form of cooperative multitasking where execution can be suspended and resumed manually. Unlike OS threads, Fibers don't run concurrently; only one Fiber runs at a time. They enable asynchronous programming patterns without callbacks or generators when combined with an event loop.
How Fibers work: A Fiber has its own call stack (unlike generators, which piggyback on the caller's stack). $fiber->start(...$args) begins execution. Inside the Fiber, Fiber::suspend($value) pauses execution and yields a value to the caller. Back in the caller, $fiber->resume($value) passes a value back into the Fiber and continues it. The Fiber maintains its own stack — you can call regular functions, nest function calls, and suspend() from deep inside.
Difference from generators: Generators must yield from the function itself — you can't yield from a function called by the generator. Fibers can Fiber::suspend() from any depth in the call stack. This makes Fibers suitable for implementing async I/O abstractions where suspending happens deep in a call chain.
Not concurrent: Fibers are cooperative, not preemptive. An event loop (like ReactPHP or Revolt) schedules Fibers by resuming them when I/O is ready. Without an event loop, Fibers are purely a flow-control mechanism.
Practical use: Most developers won't use Fibers directly — they're the foundation on which async PHP libraries (ReactPHP, Revolt, Amp 3) build their async/await abstractions. Understanding them explains how await-like syntax works in PHP.
Code Example
<?php
declare(strict_types=1);
// Basic Fiber — produce values, receive values
$fiber = new Fiber(function(): void {
echo "Fiber start\n";
$value = Fiber::suspend('first yield'); // suspend, yield 'first yield' to caller
echo "Fiber resumed with: $value\n";
$value2 = Fiber::suspend('second yield');
echo "Fiber resumed again with: $value2\n";
echo "Fiber done\n";
});
$yielded1 = $fiber->start(); // runs until first suspend
echo "Caller got: $yielded1\n"; // "first yield"
$yielded2 = $fiber->resume('hello'); // resumes, runs until second suspend
echo "Caller got: $yielded2\n"; // "second yield"
$fiber->resume('world'); // resumes, runs to completion
// Output:
// Fiber start
// Caller got: first yield
// Fiber resumed with: hello
// Caller got: second yield
// Fiber resumed again with: world
// Fiber done
// Simulating concurrent I/O (conceptual — not actual parallelism)
function simulateAsyncRequest(string $url, float $delay): string
{
echo "Starting request to $url\n";
Fiber::suspend(); // "waiting for I/O" — suspend to let other fibers run
// After resume, I/O is complete
return "Response from $url";
}
$fibers = [
new Fiber(fn() => simulateAsyncRequest('https://api1.com', 0.1)),
new Fiber(fn() => simulateAsyncRequest('https://api2.com', 0.2)),
];
// Simple round-robin scheduler (normally handled by an event loop library)
foreach ($fibers as $f) $f->start(); // start all
foreach ($fibers as $f) {
if (!$f->isTerminated()) {
$result = $f->resume(); // resume each once I/O "completes"
echo "Got: $result\n";
}
}
// Check fiber state
$f = new Fiber(fn() => Fiber::suspend());
var_dump($f->isStarted()); // false
$f->start();
var_dump($f->isSuspended()); // true
$f->resume();
var_dump($f->isTerminated()); // true