← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

PHP Fibers — Internals & Scheduler Patterns

PHP PHP 8.1+ Advanced
debt(d7/e5/b5/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). PHPStan is listed but cannot reliably detect semantic misuse like blocking I/O inside fibers or improper pooling. The detection_hints explicitly state automated=no. Blocking calls (sleep, PDO) inside fibers look syntactically valid and only manifest as performance issues at runtime under load.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix says 'use non-blocking async equivalents from Revolt or ReactPHP' — this requires replacing PDO with async database drivers, file_get_contents with async HTTP clients, sleep() with async timers. This is not a one-line fix; it's a component-level refactor to swap blocking for non-blocking equivalents throughout the fiber-based code.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). Fibers apply only to cli/queue-worker contexts per applies_to, limiting reach. However, once adopted for async patterns, every I/O call must use async-aware libraries, every new developer must understand suspension semantics, and debugging async stack traces requires fiber-aware tooling. This is a persistent tax on the async portions of the codebase.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field explicitly states developers wrongly believe 'PHP Fibers enable true parallel execution' when they're actually cooperative single-threaded concurrency. This contradicts how concurrency primitives work in other languages (Go goroutines with preemption, threads in Java/Python). Additionally, common_mistakes reveal multiple non-obvious behaviors: suspend() outside fiber throws, exceptions can be thrown into fibers, each fiber consumes 2MB stack.

About DEBT scoring →

Also Known As

fibers internals PHP coroutines stackful coroutines cooperative multitasking PHP

TL;DR

How PHP Fibers work under the hood — stack allocation, suspension mechanics, and how to build a cooperative multitasking scheduler on top of the Fiber API introduced in PHP 8.1.

Explanation

A PHP Fiber is a stackful coroutine — it gets its own call stack (unlike generators which resume from a single yield point). Internally, the Zend Engine allocates a separate C stack for each fiber (configurable via `fiber.stack_size` in php.ini, default 2MB) and saves/restores CPU registers and the stack pointer on suspend/resume. `Fiber::suspend($value)` saves the current stack frame, returns control to the caller, and passes `$value` out. `$fiber->resume($value)` restores the stack frame and passes `$value` back in as the return value of `Fiber::suspend()`. Unlike threads, fibers are cooperative — only one fiber runs at a time, switching only at explicit `Fiber::suspend()` calls, so no mutexes are needed for shared state. A scheduler is a loop that tracks pending fibers and resumes them when their awaited condition is met (I/O ready, timer elapsed). Libraries like ReactPHP and Revolt PHP use fibers to implement `async/await`-style concurrency on top of an event loop.

Diagram

sequenceDiagram
    participant S as Scheduler
    participant FA as Fiber A
    participant FB as Fiber B
    S->>FA: start()
    FA->>FA: step 1
    FA->>S: Fiber::suspend()
    S->>FB: start()
    FB->>FB: step 1
    FB->>S: Fiber::suspend()
    S->>FA: resume()
    FA->>FA: step 2
    FA->>S: terminated
    S->>FB: resume()
    FB->>FB: step 2
    FB->>S: terminated

Common Misconception

PHP Fibers enable true parallel execution — Fibers are cooperative, single-threaded concurrency. Only one fiber runs at a time; they interleave by voluntarily suspending, not by running on separate CPU cores. For true parallelism use `parallel` extension or separate processes.

Why It Matters

Fibers are the foundation of async PHP — frameworks like Revolt, ReactPHP 3+, and Amp v3 use them to write I/O-concurrent code that reads like synchronous PHP but suspends at I/O boundaries. Understanding fibers lets you debug async PHP code, tune stack size, and build lightweight schedulers for CLI tools.

Common Mistakes

  • Not handling exceptions thrown into a fiber — `$fiber->throw(new Exception)` resumes the fiber with an exception at the suspend point; uncaught, it propagates to the caller.
  • Blocking inside a fiber (sleep(), PDO query without async driver) — blocks the entire PHP process, defeating the purpose of cooperative concurrency.
  • Creating thousands of fibers simultaneously — each fiber allocates a full stack (default 2MB); 1000 fibers = 2GB RAM. Pool and reuse fibers for high-concurrency workloads.
  • Calling `Fiber::suspend()` outside a fiber — throws a `FiberError`; always check `Fiber::getCurrent() !== null`.

Code Examples

💡 Note
The scheduler round-robins between fibers — each runs until it suspends, then the next gets a turn, showing cooperative interleaving without threads.
✗ Vulnerable
// Blocking inside a fiber — defeats cooperative concurrency:
$fiber = new Fiber(function(): void {
    sleep(2);           // blocks entire process — no other fiber runs
    $result = file_get_contents('https://example.com'); // blocking I/O
    Fiber::suspend($result);
});
$fiber->start();
✓ Fixed
// Simple cooperative scheduler using Fibers + non-blocking I/O:
class Scheduler {
    private array $fibers = [];

    public function add(Fiber $fiber): void {
        $this->fibers[] = $fiber;
    }

    public function run(): void {
        while ($this->fibers) {
            foreach ($this->fibers as $key => $fiber) {
                if (!$fiber->isStarted()) $fiber->start();
                elseif ($fiber->isSuspended()) $fiber->resume();

                if ($fiber->isTerminated()) {
                    unset($this->fibers[$key]);
                }
            }
        }
    }
}

$scheduler = new Scheduler();
$scheduler->add(new Fiber(function(): void {
    echo "Task A: step 1\n";
    Fiber::suspend(); // yield control
    echo "Task A: step 2\n";
}));
$scheduler->add(new Fiber(function(): void {
    echo "Task B: step 1\n";
    Fiber::suspend();
    echo "Task B: step 2\n";
}));
$scheduler->run();
// Output: Task A: step 1 / Task B: step 1 / Task A: step 2 / Task B: step 2

Added 24 Mar 2026
Views 55
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 1 ping T 0 pings F 3 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 0 pings S 2 pings M 0 pings T 0 pings W 0 pings T 2 pings F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
ChatGPT 10 Amazonbot 7 Perplexity 7 Google 6 Unknown AI 3 Ahrefs 3 SEMrush 3 Scrapy 3 Meta AI 2 Claude 2 PetalBot 1
crawler 41 crawler_json 6
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: High
⚡ Quick Fix
Never call blocking functions (sleep, PDO, file_get_contents) inside a fiber — use non-blocking async equivalents from Revolt or ReactPHP
📦 Applies To
PHP 8.1+ cli queue-worker Revolt ReactPHP Amp
🔗 Prerequisites
🔍 Detection Hints
sleep() or blocking I/O calls inside a Fiber callback; new Fiber() in a loop without pooling
Auto-detectable: ✗ No phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: High Context: Function Tests: Update


✓ schema.org compliant