php runtime and server environment

Fibers, Coroutines, And Non-Blocking I/O

Fibers let PHP pause a piece of code and resume it later. Async libraries can use fibers to make non-blocking code look more like normal sequential PHP.

The important point is that fibers are not magic parallel threads. A fiber only pauses and resumes execution. Non-blocking I/O still requires an event loop, compatible libraries, and operations that actually give control back while waiting.

A small fiber example

PHP example
<?php

declare(strict_types=1);

$fiber = new Fiber(function (): string {
    echo 'Fiber started' . PHP_EOL;

    $value = Fiber::suspend('waiting');

    echo 'Fiber resumed with ' . $value . PHP_EOL;

    return 'done';
});

echo $fiber->start() . PHP_EOL;
echo $fiber->resume('data') . PHP_EOL;

// Prints:
// Fiber started
// waiting
// Fiber resumed with data
// done

The fiber starts, suspends with the value waiting, then resumes later with data.

Coroutines

A coroutine is a routine that can pause and continue later. In PHP conversations, "coroutine" is often used when talking about Swoole/OpenSwoole, Amp, or async-style code.

Different ecosystems use the word differently, so focus on the behaviour: code can yield control while waiting, then continue from the same point when the result is ready.

Non-blocking I/O

Blocking I/O waits until the operation finishes:

PHP example
<?php

declare(strict_types=1);

$contents = file_get_contents(__FILE__);

echo is_string($contents) ? 'read complete' : 'read failed';
echo PHP_EOL;

// Prints:
// read complete

That is fine in many scripts. In an event loop, blocking reads, sleeps, HTTP calls, DNS lookups, and database calls can stop other tasks from progressing.

Non-blocking I/O means the operation can start, the process can do other work while waiting, and a callback, promise, awaitable, or coroutine resumes when the operation is ready.

Fibers do not make blocking code safe

Wrapping blocking code in a fiber does not make it non-blocking:

PHP example
<?php

declare(strict_types=1);

$fiber = new Fiber(function (): void {
    sleep(1);
    echo 'Blocking work finished' . PHP_EOL;
});

$fiber->start();

echo 'After fiber' . PHP_EOL;

// Prints:
// Blocking work finished
// After fiber

The process still waits during sleep(1). The fiber did not move the work to another thread or make the operating system call non-blocking.

What async libraries add

Libraries such as Amp and ReactPHP provide the machinery around fibers or event loops:

  • scheduling work
  • waiting for sockets or timers
  • representing future results
  • handling cancellation and timeouts
  • coordinating many I/O tasks
  • surfacing errors from async operations

Swoole/OpenSwoole provide coroutine features through an extension and runtime support. The exact APIs differ, but the professional concern is the same: do not mix blocking libraries into code that expects non-blocking behaviour.

What you should be able to do

After this lesson, you should be able to explain what a fiber does, why fibers are not threads, how coroutine terminology relates to pausing and resuming work, and why non-blocking I/O requires compatible libraries rather than just different syntax.

Practice

Task: Demonstrate A Fiber

Create a small PHP script that demonstrates pausing and resuming a fiber.

Requirements

  • Start a fiber.
  • Suspend it with a value.
  • Resume it with another value.
  • Print the output in order.
  • Add a short note explaining why this does not automatically make blocking I/O non-blocking.

Check your work

The script should run without installing an async framework. The explanation should distinguish fibers from event loops and threads.

Show solution
PHP example
<?php

declare(strict_types=1);

$fiber = new Fiber(function (): string {
    echo 'Inside fiber before suspend' . PHP_EOL;

    $received = Fiber::suspend('value from suspend');

    echo 'Inside fiber after resume: ' . $received . PHP_EOL;

    return 'fiber return value';
});

echo $fiber->start() . PHP_EOL;
echo $fiber->resume('value from resume') . PHP_EOL;

// Prints:
// Inside fiber before suspend
// value from suspend
// Inside fiber after resume: value from resume
// fiber return value

The fiber pauses and resumes, but it does not create a second thread. If the code inside the fiber calls a blocking function, the process still waits. Non-blocking I/O needs an event loop and compatible I/O libraries, not only fiber syntax.