PHPFibers

Fibers

PHP has always executed a single script top to bottom, one statement after another, with no way for a function to pause itself partway through and let something else run before picking back up exactly where it left off. PHP 8.1's Fibers change that: they're a low-level primitive for cooperative multitasking, letting a block of code voluntarily suspend its own execution and resume later, without unwinding the call stack in between. Fibers are not something most application code calls directly — they're infrastructure that async libraries build on top of — but understanding what they are demystifies how those libraries manage to offer non-blocking behavior in a language that has no built-in event loop.

What "cooperative multitasking" means here

PHP itself is still single-threaded — Fibers don't give you parallelism, and they don't run code simultaneously on multiple CPU cores the way real threads or processes would. What they give you is the ability to pause one unit of work at a chosen point and switch to another, then come back later exactly where you left off, with all local variables and the internal execution position intact. The word "cooperative" matters: a Fiber only suspends when its own code explicitly calls Fiber::suspend() — nothing preempts it from the outside.

Fiber::start(), suspend(), and resume()

A Fiber wraps a callable. Calling start() begins running that callable, suspend() (called from inside the Fiber's own code) pauses it and hands control back to whatever started it, and resume() (called from outside, on the Fiber object) continues execution from exactly the point suspend() was called. Values can flow in both directions: suspend() returns whatever value was passed to the following resume() call, and start() / resume() themselves return whatever value the Fiber passed to its next suspend() call.

A minimal Fiber that pauses mid-execution

PHP
<?php
$fiber = new Fiber(function (): void {
    echo "Fiber: starting\n";

    $value = Fiber::suspend('paused-here');

    echo "Fiber: resumed with '{$value}'\n";
});

echo "Main: about to start the fiber\n";
$suspendedValue = $fiber->start();

echo "Main: fiber suspended and returned '{$suspendedValue}'\n";
echo "Main: doing other work...\n";

$fiber->resume('continue-please');

echo "Main: fiber finished, isTerminated = ";
var_dump($fiber->isTerminated());
Main: about to start the fiber
Fiber: starting
Main: fiber suspended and returned 'paused-here'
Main: doing other work...
Fiber: resumed with 'continue-please'
Main: fiber finished, isTerminated = bool(true)

Notice the order of output: execution genuinely bounces between the main script and the Fiber's callable. The Fiber's echo statements interleave with the main script's, proving that suspend() really does hand control back rather than just returning a value while continuing to run in the background.

Why this matters for async libraries

The typical reason a piece of code wants to "pause" is that it's waiting on something slow and external — a database query, an HTTP request, a file read. Without Fibers, PHP code that wants to avoid blocking on that wait has historically resorted to callback-based APIs or generator-based coroutines, both of which tend to spread across a codebase and make ordinary-looking sequential code hard to write. A library like Amp uses Fibers internally so that application code can await an asynchronous operation and read like ordinary, top-to-bottom synchronous code, while the library's event loop transparently suspends the current Fiber whenever it hits a slow operation and resumes it once the result is ready — juggling many such suspended Fibers at once behind the scenes.

The shape of code Fibers make possible (illustrative)

PHP
<?php
// This is the *style* of code Fiber-based libraries enable —
// it reads sequentially, but "await" suspends the current Fiber
// under the hood while other work proceeds.

function fetchUser(int $id): array
{
    $response = await(httpGet("https://api.example.com/users/{$id}"));

    return json_decode($response, true);
}

$user = fetchUser(42);
echo $user['name'];

The point of that example isn't the specific await()/httpGet() functions — they're stand-ins for whatever an async library provides — it's that Fibers let library authors offer this sequential-looking style at all, instead of forcing every caller into nested callbacks.

isRunning(), isSuspended(), isTerminated()

A Fiber instance exposes status-checking methods so calling code can inspect its current state without guessing: isRunning(), isSuspended(), isTerminated(), and isStarted(). These are mainly useful for the scheduling code inside an event-loop implementation, which needs to know which of many Fibers are currently suspended and safe to resume.

You will rarely write raw Fiber code yourself
Most PHP developers will interact with Fibers only indirectly, by using a library built on top of them (Amp and similar tooling in the ReactPHP ecosystem being the notable examples) rather than calling `Fiber::start()` and `Fiber::suspend()` directly in application code. Fibers exist at roughly the same level as generators or low-level concurrency primitives in other languages — a building block for library authors, not typically something a web application's business logic touches. Knowing how they work is what makes async libraries' behavior predictable rather than mysterious.
  • A Fiber lets code pause itself mid-execution with Fiber::suspend() and resume later from the exact same point via $fiber->resume().

  • Fibers provide cooperative multitasking, not parallelism — PHP still runs one thing at a time; a Fiber only pauses where its own code says to.

  • start() and resume() return the value passed to the Fiber's next suspend() call; suspend() returns the value passed to the following resume() call.

  • Async libraries such as Amp use Fibers internally so application code can look sequential while I/O actually happens without blocking.

  • Status methods (isRunning(), isSuspended(), isTerminated()) let scheduling code track many Fibers at once.

Tip
If you're evaluating whether to reach for Fibers directly in application code, that's usually a sign to look for an existing async library instead. Fibers are deliberately low-level — a primitive for building schedulers and event loops on top of, not an ergonomic API for everyday concurrency in application code.