PHP can't run concurrent code within a single request today. Fibers, added in PHP 8.1, let code pause and resume later, but PHP has no scheduler to decide what runs next. So each async library, like ReactPHP, Revolt and AMPHP, builds its own, and they don't work with each other. This RFC from Edmond adds a hook in the engine where a single scheduler can plug in and take charge.

What it adds

The RFC adds no new functions, classes, constants or syntax. It only changes engine internals.

  • Coroutines in the engine. A coroutine is a lightweight task that can pause at any call depth and resume later. Your whole script runs as the "main" coroutine.
  • A pluggable scheduler. An extension supplies the scheduler, and once it registers, PHP runs in concurrent mode. Only one scheduler can register.
  • Direct switching. A coroutine can hand control straight to another one. A Fiber has to go back through whatever resumed it, so this cuts the number of switches in half.
  • Fiber adoption. When a scheduler is active, it can take over the Fibers your code starts, which lets fiber-based libraries share one schedule.
  • Per-coroutine storage. Extensions get a place to keep state for each coroutine instead of in globals.

Everything still runs in one OS thread, and only one flow runs at a time. This isn't parallelism and it isn't threads.

User-facing APIs like spawn() and await() are left to extensions. The True Async RFC is one API built on top of this.

What it doesn't do

Non-blocking I/O, timers and DNS need their own RFCs. Until those land, a blocking call still blocks every coroutine. Making ob_start() and similar functions safe per coroutine is also left for future work.

What it means for existing code

With no scheduler registered, nothing changes and PHP behaves exactly as it does today.

With a scheduler active, three things change:

  • Calling Fiber::suspend() inside a destructor throws FiberError, because destructors run in their own coroutine, which isn't a fiber.
  • gc_collect_cycles() can return 0 in two new cases, even when there was garbage to collect.
  • pcntl_fork() throws unless the scheduler allows the fork. This part isn't implemented yet.

Existing Fiber code keeps working, and so do Xdebug and stack traces.

Where it stands

The RFC is under discussion on the internals mailing list. It was first published on July 23, 2026, as version 0.9, and targets the next PHP 8.x release, which is PHP 8.7 at the time of writing. It needs a two-thirds majority to pass, and no vote has happened yet.