PHP 8.1 Fibers
Pause and resume execution with native Fibers.
PHP 8.1 Fibers is a free PHP Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the PHP Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Fibers Give You
PHP 8.1 added Fibers: a core primitive that lets you pause a running call stack and resume it later, from somewhere completely different. A Fiber is a full, independent stack you can suspend at an arbitrary point and continue without unwinding. This is the missing piece that lets libraries replace promise-chains with synchronous-looking await — the value behind ReactPHP and Amp's modern APIs.
Your First Fiber
Create a Fiber with a callback, start() it, and let it suspend() itself. Control returns to the caller at the suspension point; resume() continues from exactly there.
<?php
$fiber = new Fiber(function (): void {
echo "A: inside fiber\n";
$received = Fiber::suspend('paused');
echo "C: resumed with '$received'\n";
});
$value = $fiber->start(); // prints A, returns 'paused'
echo "B: fiber suspended, got '$value'\n";
$fiber->resume('hello'); // prints C
echo "D: fiber done\n";The Four Core Methods
The API is small:
$fiber->start(...$args)— begins execution; returns the first suspend value.Fiber::suspend($value)— pauses the current fiber, handing$valueto the resumer.$fiber->resume($value)— continues;$valuebecomes the return ofsuspend().$fiber->getReturn()— the callback's return once terminated.
Values flow both ways across the suspension boundary.
Bidirectional Value Passing
A fiber can yield a request out and receive a response back in — the heart of how an event loop feeds I/O results into suspended tasks.
<?php
$fiber = new Fiber(function (): int {
$a = Fiber::suspend('need first number');
$b = Fiber::suspend('need second number');
return $a + $b;
});
$fiber->start(); // suspends: 'need first number'
$fiber->resume(10); // a = 10, suspends: 'need second number'
$fiber->resume(32); // b = 32, fiber returns
echo $fiber->getReturn(), PHP_EOL; // 42Fiber State and Introspection
You can ask a fiber about its lifecycle. Calling resume() on a terminated fiber throws FiberError, so guard with these checks when building a scheduler.
<?php
$fiber = new Fiber(function () { Fiber::suspend(); });
var_dump($fiber->isStarted()); // false
$fiber->start();
var_dump($fiber->isStarted()); // true
var_dump($fiber->isSuspended()); // true
var_dump($fiber->isRunning()); // false (we're outside it)
$fiber->resume();
var_dump($fiber->isTerminated()); // trueBuilding a Tiny Scheduler
Fibers plus a queue equals a cooperative scheduler. Each task suspends to yield control; the scheduler round-robins until all are done. This is the skeleton inside every async runtime.
<?php
$tasks = [];
foreach (['X', 'Y', 'Z'] as $name) {
$tasks[] = new Fiber(function () use ($name) {
for ($i = 1; $i <= 2; $i++) {
echo "$name step $i\n";
Fiber::suspend(); // give others a turn
}
});
}
foreach ($tasks as $t) { $t->start(); }
while ($tasks) {
foreach ($tasks as $k => $t) {
if ($t->isTerminated()) { unset($tasks[$k]); continue; }
$t->resume();
}
}Exceptions Across Boundaries
throwInto() (called throw() in the API as $fiber->throw()) injects an exception at the suspension point — how a loop reports a failed I/O operation back into the awaiting task. The exception surfaces exactly where the fiber was paused.
<?php
$fiber = new Fiber(function () {
try {
Fiber::suspend();
} catch (RuntimeException $e) {
echo 'caught: ' . $e->getMessage() . PHP_EOL;
}
});
$fiber->start();
$fiber->throw(new RuntimeException('io failed')); // prints: caught: io failedFibers vs Generators
Generators (yield) also suspend — but only the top function. To await from a nested helper with generators you must yield from all the way up the stack, coloring every function. Fibers suspend the entire stack from any depth, so a deeply nested call can Fiber::suspend() without its callers knowing. This is why await can be an ordinary function call rather than a yield.
<?php
function deep(): string {
// No yield needed in the call chain — suspends the whole fiber
return Fiber::suspend('awaiting from deep');
}
$f = new Fiber(fn() => print(deep() . PHP_EOL));
$f->start();
$f->resume('value injected at depth');The Suspension Restriction
One hard rule: you can only Fiber::suspend() from inside a fiber. Suspending from the main thread, or across a C-level callback that does not support fibers (some legacy usort comparators, certain internal callbacks), throws FiberError: Cannot suspend outside of a fiber. Libraries hide this by ensuring all user code runs inside a fiber driven by the loop.
<?php
try {
Fiber::suspend(); // main thread — illegal
} catch (FiberError $e) {
echo $e->getMessage(), PHP_EOL; // Cannot suspend outside of a fiber
}How Libraries Use Fibers
You rarely write raw Fibers in app code. Frameworks like Amp v3 and ReactPHP (via fibers) wrap them: await($promise) internally calls Fiber::suspend(), registers a continuation with the event loop, and resume()s your fiber when the I/O completes. Your code looks synchronous; the runtime handles parking and scheduling.
<?php
// Conceptual: what an await() helper does under the hood
function await(Promise $p): mixed {
$fiber = Fiber::getCurrent() ?? throw new Error('await() outside fiber');
$p->then(fn($v) => $fiber->resume($v),
fn($e) => $fiber->throw($e));
return Fiber::suspend(); // parked until the promise settles
}Memory and Lifecycle Caveats
Each fiber allocates its own stack, so spawning hundreds of thousands has a memory cost (though far lighter than OS threads). A fiber suspended forever is never garbage-collected if something still references it — a subtle leak in long-running servers. Also, a fiber's callback that throws an uncaught exception propagates out of the resume()/start() call, so the scheduler must wrap resumes in try/catch.
<?php
$fiber = new Fiber(function () {
throw new RuntimeException('boom inside fiber');
});
try {
$fiber->start(); // exception surfaces here, at the resumer
} catch (RuntimeException $e) {
echo 'scheduler caught: ' . $e->getMessage() . PHP_EOL;
}Quick Check
What key advantage do Fibers have over generators for async?
Recap
Fibers are PHP's low-level concurrency primitive:
start(),Fiber::suspend(),resume(),getReturn()pause and continue a full call stack, passing values both ways.- State methods (
isSuspended,isTerminated) andthrow()let you build a cooperative scheduler and inject I/O errors. - Unlike generators, a fiber suspends from any depth — enabling a transparent
await(). - You can only suspend inside a fiber; frameworks like Amp and ReactPHP wrap fibers so your async code reads synchronously.
Frequently asked questions
Is the “PHP 8.1 Fibers” lesson free?
Yes — the full text of “PHP 8.1 Fibers” is free to read here on the web, and the PHP Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the PHP Academy course, upgrade to CoddyKit PRO.
What will I learn in “PHP 8.1 Fibers”?
Pause and resume execution with native Fibers. You practise PHP Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start PHP Academy?
No prior experience is required. PHP Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “PHP 8.1 Fibers” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this PHP Academy lesson?
Yes. Every PHP Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.