Coroutines — Cooperative Multitasking
debt(d7/e5/b7/t7)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints list Blackfire and PHPStan, but note automated: no — blocking I/O in event loops or mixing sync/async code without proper bridging is not automatically caught by these tools. You typically discover issues through runtime performance problems or code review. Blackfire can show where time is spent but won't flag coroutine misuse directly.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix suggests using async frameworks (Amp v3, ReactPHP) rather than raw Fibers, but retrofitting blocking I/O calls across a codebase to use async alternatives requires touching multiple files and understanding the async runtime model. It's not a one-line fix but also not architectural rework.
Closest to 'strong gravitational pull' (b7). Coroutines apply to web, cli, and queue-worker contexts per applies_to, and once adopted, every I/O operation must be async-aware. The choice of async framework (Amp, ReactPHP) shapes how all code interacts with I/O, database, HTTP clients, etc. Every future maintainer must understand the cooperative scheduling model and ensure no blocking calls leak in.
Closest to 'serious trap' (t7). The misconception states developers believe coroutines provide parallelism when they only provide concurrency — this directly contradicts how threads/processes work elsewhere. Developers coming from thread-based concurrency expect simultaneous execution on multiple CPUs but get interleaved execution on one core. The common_mistakes reinforce this: expecting CPU parallelism, blocking I/O freezing the entire loop, deeply recursive stack overflows.
Also Known As
TL;DR
Explanation
Coroutines pause at explicit yield points and resume from the same point later. Unlike threads (preemptive — OS interrupts at any time), coroutines yield explicitly (cooperative — code decides when to pause). PHP 8.1 Fibers implement stackful coroutines. Coroutines enable concurrent I/O without threads: Fiber A waits for a DB query → suspends → Fiber B makes an HTTP request → suspends → Fiber A resumes when the DB responds. The event loop schedules which coroutine runs next.
Common Misconception
Why It Matters
Common Mistakes
- Blocking I/O inside coroutines — blocks the entire event loop
- Expecting CPU parallelism from coroutines — use processes for that
- Not propagating cancellation when parent coroutine is cancelled
- Stack overflows in deeply recursive stackful coroutines
Code Examples
// Blocking I/O — all other Fibers wait:
$fiber = new Fiber(function(): void {
$data = file_get_contents('https://api.example.com'); // BLOCKS!
// During this HTTP wait, no other Fiber can run
echo $data;
});
// Non-blocking with explicit yield:
$fiber = new Fiber(function() use ($httpClient): void {
$promise = $httpClient->getAsync('https://api.example.com');
$result = Fiber::suspend($promise); // Yield control — event loop runs others
echo $result; // Resume here when the response arrives
});