Copy
Trading Bots
Events
More

Polling API + Fibers: Async PHP in Practice

2026/07/16 02:25Browse 0

The PHP Polling API RFC passed overwhelmingly on June 3, and the implementation is already merged to master. With alpha 1 scheduled for July 2 and GA expected November 19, developers can now compile the feature from source and test it. This marks a fundamental shift for async PHP: the Polling API provides the native I/O multiplexing primitive that was always missing, while Fibers handle cooperative multitasking.

Fibers and the Polling API: Two Distinct Tools

A Fiber is PHP's cooperative concurrency primitive, available since 8.1. It allows a function to pause itself with `Fiber::suspend()` and resume later, preserving its stack and local state. That's all it does—Fibers know nothing about sockets, timers, or I/O. They are purely a control-flow mechanism.

An event loop decides when to resume a suspended fiber. Historically, PHP relied on `stream_select()` or PECL extensions like ext-uv or ext-event to access native epoll or kqueue. ReactPHP ships four separate loop implementations precisely because there was no single reliable native primitive. AMPHP built Revolt for the same reason.

The Polling API fills that gap. It is not an event loop itself, nor does it replace Fibers. It provides a fast, native way to ask the operating system which file descriptors are ready for I/O, eliminating the need for multiple backend implementations. Together, Fibers give pausable functions, and the Polling API tells you when to resume them.

Building a Minimal Async Scheduler

To understand how Amp v3 and ReactPHP work internally, you can build a toy scheduler. The following code creates a `MiniScheduler` class that runs multiple fibers concurrently, each fetching a URL over a raw non-blocking socket, resumed by a single `Io\Poll\Context`.

The scheduler's `spawn()` method starts a fiber and stores it if not terminated. The `run()` method loops, calling `wait()` on the poll context, which blocks until a watched stream is ready. It then resumes the fiber waiting on that stream. The `awaitReadable()` and `awaitWritable()` methods add a watcher for the stream, suspend the current fiber, and remove the watcher upon resume.

A key detail when writing to non-blocking streams: `fwrite()` may short-write, so the request must be written in a loop that awaits writability whenever the kernel send buffer pushes back. The `writeAll()` function handles this correctly.

For reading, the code relies on `feof()` flipping true after a read that hits end-of-stream. The Polling API automatically monitors `Event::Error` and `Event::HangUp` regardless of what you request, so `wait()` returns the watcher when the server closes the connection. `fread()` on a closed non-blocking stream returns an empty string without blocking.

Real-World Concurrency Example

Wire five fibers into the scheduler and run them concurrently:

```php

$scheduler = new MiniScheduler();

$hosts = [

'example.com',

'httpbin.org',

'jsonplaceholder.typicode.com',

];

foreach ($hosts as $host) {

$scheduler->spawn(function () use ($scheduler, $host) {

$body = fetch($scheduler, $host, '/');

echo "{$host}: " . strlen($body) . " bytes\n";

});

}

$scheduler->run();

```

Three fibers, each blocked on its own socket, are all resumed independently by one `Io\Poll\Context`. This demonstrates the core pattern that libraries like Amp v3 will use: Fibers for concurrency, the Polling API for efficient I/O readiness notification. The result is truly asynchronous PHP without the overhead of userland polling or multiple event loop backends.

Disclaimer: This page may contain third-party information and does not necessarily reflect BYDFi's views or opinions. This content is for general reference only and does not constitute any representation, warranty, financial advice, or investment advice. BYDFi is not responsible for any errors, omissions, or any results arising from the use of such information. Virtual asset investments involve risks. Please carefully evaluate the risks of the product and your risk tolerance based on your financial situation. For more information, please refer to our Terms of Use and Risk Disclosure.