Library / SDK
reactphp/event-loop avatar
reactphp/event-loop

ReactPHP EventLoop: one reactor so async PHP libraries can be used together

ReactPHP's core reactor event loop that libraries can use for evented I/O.

1,336 stars130 forksPHPMIT

At a glance

What is it?
react/event-loop is ReactPHP's core reactor, and its reason for existing is interoperability, since async libraries can only be combined if they share the same event loop. It exposes a LoopInterface that any library can target, a static Loop class as the default accessor, and four loop implementations behind it, with a v3 in development alongside the supported v1 line.
Who is it for?
react/event-loop suits a PHP developer building asynchronous I/O who wants to combine libraries that were written independently, and who wants the same loop instance shared between them rather than each library running its own.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 114 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why a shared loop exists at all

The README states the reason in one paragraph, and it is the reason to read the project at all. In order for async based libraries to be interoperable, they need to use the same event loop. This component provides a common LoopInterface that any library can target, which allows them to be used in the same loop with one single run() call that is controlled by the user. The phrase controlled by the user is the important part. Two libraries each running their own reactor means two loops, two sets of timers and a stream registered with one loop while another loop is trying to service it, which is a class of bug that is very hard to diagnose. The licence is MIT, the primary language is PHP, the homepage is reactphp.org/event-loop, and the last push to the repository was on 2026-07-01.

The static Loop class, and when to reach for the explicit instance instead

The typical usage is through the Loop class, which is described as a convenient global accessor for the event loop and provides all the LoopInterface methods as static methods. The alternative is to explicitly create an event loop instance at the beginning, reuse it throughout the program and run it at the end, with React\EventLoop\Loop::get() returning the instance and the instance methods invoked on it. The README is fair about the trade, noting that while the former is more concise, the latter is more explicit, and that in both cases the program performs the exact same steps. The three steps are the same in either style, the event loop instance is created at the beginning, the loop is used directly or passed as an instance to library and application code, and the loop is run at the end of the program.

php
use React\EventLoop\Loop;

$timer = Loop::addPeriodicTimer(0.1, function () {
    echo 'Tick' . PHP_EOL;
});

Loop::addTimer(1.0, function () use ($timer) {
    Loop::cancelTimer($timer);
    echo 'Done' . PHP_EOL;
});

The Loop class autoruns, and that is a deliberate design decision

There is a behavioural difference between the two styles that the README calls out, and it is the one thing to be careful about. When using the Loop class it will automatically execute the loop at the end of the program, whereas the explicit style requires a single run() call at the end. The documentation also states that as of v1.2.0 the project highly recommends using the Loop class, and that the explicit loop instructions are still valid and may still be useful in some applications, especially for a transition period towards the more concise style. The autorun is convenient, because it means a script that registers a timer and does nothing else still works, but it also means that a program which registers work and then wants to do something before the loop starts has to get the instance explicitly with Loop::get() instead, since the static style has already begun the loop by the time control returns.

Dependency injection, which is the other way to use the loop

The README addresses the object-oriented and dependency injection audience directly. If you are familiar with OOP and DI, you may want to inject an event loop instance and invoke instance methods on the LoopInterface, and the example shows a Greeter class that takes a LoopInterface in its constructor, stores it, and uses it to schedule a timer that prints a greeting. The instance is created with new Greeter(Loop::get()), so the global accessor is used at the composition root and the class itself stays decoupled. Each static method call on the Loop class is forwarded as-is to the underlying event loop instance by using the Loop::get() call internally, which means the static style and the injected style are the same loop rather than two loops, and that is what keeps the interoperability promise intact even in a mixed codebase.

php
use React\EventLoop\Loop;
use React\EventLoop\LoopInterface;

class Greeter
{
    private $loop;

    public function __construct(LoopInterface $loop)
    {
        $this->loop = $loop;
    }

    public function greet(string $name)
    {
        $this->loop->addTimer(1.0, function () use ($name) {
            echo 'Hello ' . $name . '!' . PHP_EOL;
        });
    }
}

$greeter = new Greeter(Loop::get());
$greeter->greet('Alice');
$greeter->greet('Bob');

Four loop implementations behind one interface

The table of contents lists four loop implementations, and they differ in what they delegate to. StreamSelectLoop is the pure PHP implementation built on the stream_select family. ExtEventLoop uses the ext-event extension. ExtEvLoop uses ext-ev. And ExtUvLoop uses libuv through the ext-uv extension. The interface is what the ecosystem codes against, and the implementations are what you choose per environment. The practical consequence for a library author is that targeting LoopInterface means your library works whichever reactor the application ends up with, which is the whole point of the project. For an application, the choice is between a portable pure PHP loop and one that delegates to a native event library, and the README's example section links to an examples directory in the repository that includes benchmark files, so the trade-off is documented as something you can measure rather than argue about.

The interface surface: timers, ticks, signals and streams

The LoopInterface section of the table of contents is the method list, and it is short enough to hold in your head. run and stop control the loop itself. addTimer and addPeriodicTimer schedule one-shot and repeating callbacks, and cancelTimer removes one. futureTick queues work for the next tick of the loop, which is the primitive behind a lot of the higher level scheduling in the ecosystem. addSignal and removeSignal handle process signals such as SIGTERM or SIGINT, which is what makes a graceful shutdown possible. And addReadStream, addWriteStream, removeReadStream and removeWriteStream are the I/O half, where you register a stream and the loop calls you when it is readable or writable. The quickstart example is built on exactly these, with a server socket registered as a read stream and the accepted connection registered as a write stream.

The quickstart server, and the periodic timer that reports memory

The quickstart example is an async HTTP server built with just the event loop, and it is short enough to read as the canonical example of the API. A TCP server socket is created on 127.0.0.1:8080 with stream_socket_server and set to non-blocking with stream_set_blocking. Loop::addReadStream registers the accept callback, which calls stream_socket_accept and then registers the new connection as a write stream with a fixed HTTP response of HTTP/1.1 200 OK, a Content-Length of 3 and a body of Hi. The write callback tracks how many bytes were written and, if the write was complete, closes the connection and removes the write stream, and otherwise trims the data with substr so the remainder is written on the next pass. The example also registers a periodic timer every five seconds that prints the current memory usage, which is a small demonstration of the timer side of the interface in the same program.

php
<?php

use React\EventLoop\Loop;

require __DIR__ . '/vendor/autoload.php';

$server = stream_socket_server('tcp://127.0.0.1:8080');
stream_set_blocking($server, false);

Loop::addReadStream($server, function ($server) {
    $conn = stream_socket_accept($server);
    $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n";
    Loop::addWriteStream($conn, function ($conn) use (&$data) {
        $written = fwrite($conn, $data);
        if ($written === strlen($data)) {
            fclose($conn);
            Loop::removeWriteStream($conn);
        } else {
            $data = substr($data, $written);
        }
    });
});

The v3 branch, and the examples directory that documents the API by example

The README opens with a development version notice. This branch contains the code for the upcoming v3 release, and the current stable v1 code is on the 1.x branch. The project says the upcoming v3 release will be the way forward for this package, and that v1 will still be actively supported for those not yet on the latest version. So a new adopter has a choice to make, and the README points to the installation instructions for the details. The releases confirm the cadence, v1.4.0 in May 2023, v1.5.0 in November 2023 and v1.6.0 in November 2025, with the default branch here being 3.x. The examples directory is the other documentation, with numbered files covering timers, periodic timers, ticks, signals, consuming standard input, a yes generator, blocking and async HTTP clients, an HTTP server, and a set of benchmark files for ticks, timers, timer delays and memory.

Editorial conclusion

react/event-loop suits a PHP developer building asynchronous I/O who wants to combine libraries that were written independently, and who wants the same loop instance shared between them rather than each library running its own. It is the wrong choice if you need one specific loop implementation's characteristics and are willing to standardise on it for the whole ecosystem, since the interface lets you pick but also lets everyone pick differently, and a poor fit if your application is a request-response script with no concurrent I/O, where the loop is overhead. Before adopting it, read the v3 notice in the README, because the development branch is the upcoming major release and v1 is still actively supported, so which branch you depend on changes what you get.

Frequently asked questions

What is react/event-loop?

It is ReactPHP's core reactor event loop, the component that libraries use for evented I/O. It provides a common LoopInterface that any library can target, so that async libraries can be used in the same loop with a single run() call controlled by the user. The licence is MIT.

Which loop implementations does ReactPHP EventLoop provide?

Four, all behind the LoopInterface: StreamSelectLoop, the pure PHP implementation, ExtEventLoop using the ext-event extension, ExtEvLoop using ext-ev, and ExtUvLoop using libuv through ext-uv. Libraries target the interface so they work with whichever reactor the application uses.

When should I use the static Loop class instead of an explicit loop instance?

The README recommends the static Loop class as of v1.2.0, since it is more concise, and says the explicit instructions are still valid and may be useful in some applications, especially for a transition period. Note that the Loop class automatically executes the loop at the end of the program, while the explicit style requires a run() call.

How do I share one event loop between my code and a library?

Pass the loop instance into the library, using the LoopInterface type for dependency injection, and obtain it with React\EventLoop\Loop::get() at the composition root. Each static method call on the Loop class is forwarded as-is to that same underlying instance, so the static and injected styles share one loop.

Is the ReactPHP event-loop v3 release out yet?

Not according to the README, which says the development branch contains the code for the upcoming v3 release, with the stable v1 code on the 1.x branch. The project says v3 will be the way forward and that v1 will still be actively supported for those not yet on the latest version.

Official sources

  1. License: MIT
  2. Project website
  3. reactphp/event-loop on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/reactphp-event-loop.svg)](https://hysenlabs.com/projects/reactphp-event-loop)

Was this useful?