0Pricing
PHP Academy · Lektion

Event Loops mit ReactPHP

Führen Sie nicht blockierendes I/O mit dem ReactPHP-Event-Loop aus.

Event Loops mit ReactPHP ist eine kostenlose PHP Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des PHP Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.

ReactPHP in einem Satz

ReactPHP ist ein reines PHP-Toolkit ohne Abhängigkeiten für nicht blockierende E/A, das um eine einzelne Event-Schleife herum aufgebaut ist. Es ist keine Erweiterung erforderlich — es funktioniert mit Standard-PHP über stream_select (oder bei Verfügbarkeit über ext-event/ext-ev). Damit schreiben Sie langlebige Server, parallele HTTP-Clients und Streaming-Pipelines in einfachem PHP.

Die globale Event-Schleife

Modernes ReactPHP (v1.2+) stellt über die statische Fassade React\EventLoop\Loop eine Standardschleife bereit — Sie müssen eine Schleifeninstanz nicht mehr überall übergeben. Planen Sie mit einem Timer einen Callback ein; die Schleife läuft, bis keine Arbeit mehr vorhanden ist.

<?php
require 'vendor/autoload.php';
use React\EventLoop\Loop;

Loop::addTimer(1.0, function () {
    echo "fired after 1 second\n";
});
echo "scheduled\n";
// Loop::run() is auto-invoked at script shutdown in v1.2+

Timer und periodische Aufgaben

Nicht blockierendes Timing ersetzt sleep(). addTimer wird einmal ausgelöst; addPeriodicTimer wird wiederholt ausgelöst. Sie können beide abbrechen, um sie zu stoppen. Da sie von der Schleife gesteuert werden, laufen andere Aufgaben weiter, während ein Timer wartet.

<?php
use React\EventLoop\Loop;

$count = 0;
$timer = Loop::addPeriodicTimer(0.5, function () use (&$count, &$timer) {
    echo 'tick ' . (++$count) . "\n";
    if ($count === 4) {
        Loop::cancelTimer($timer); // stop after 4 ticks
    }
});

Promises für asynchrone Ergebnisse

ReactPHP gibt für jeden Vorgang, der später abgeschlossen wird, ein Promise zurück. Hängen Sie mit then() einen Callback für den Erfolg und einen zweiten für den Fehler an. Promises lassen sich kombinieren, sodass Sie Ergebnisse umwandeln und verketten können, ohne Callbacks tief zu verschachteln.

<?php
use React\Promise\Promise;

function asyncDouble(int $n): Promise {
    return new Promise(function ($resolve) use ($n) {
        \React\EventLoop\Loop::addTimer(0.1, fn() => $resolve($n * 2));
    });
}

asyncDouble(21)
    ->then(fn($r) => printf("result: %d\n", $r))   // 42
    ->catch(fn(\Throwable $e) => printf("err: %s\n", $e->getMessage()));

Paralleles HTTP mit dem Async-Client

Der große Vorteil: Sie starten viele HTTP-Anfragen gleichzeitig aus einem Thread. React\Http\Browser gibt Promises zurück; React\Promise\all() wartet auf den Abschluss des gesamten Stapels. Zehn Anfragen mit einer Dauer von jeweils einer Sekunde sind in etwa einer Sekunde abgeschlossen, nicht in zehn.

<?php
use React\Http\Browser;
use function React\Promise\all;

$browser = new Browser();
$urls = [
    'https://httpbin.org/delay/1',
    'https://httpbin.org/delay/1',
    'https://httpbin.org/delay/1',
];
$requests = array_map(fn($u) => $browser->get($u), $urls);

all($requests)->then(function (array $responses) {
    echo 'all done: ' . count($responses) . " responses\n";
});

Fibers sorgen für synchron lesbaren Code

Callback-Ketten werden schnell unübersichtlich. ReactPHP lässt sich in react/async integrieren, das PHP 8.1 Fibers verwendet, sodass Sie ein Promise mit await() abwarten und Code in geradliniger Form schreiben können. Der Wrapper coroutine()/async() führt Ihre Funktion innerhalb eines Fibers aus.

<?php
use function React\Async\async;
use function React\Async\await;
use React\Http\Browser;

$main = async(function () {
    $browser = new Browser();
    $a = await($browser->get('https://httpbin.org/uuid'));
    $b = await($browser->get('https://httpbin.org/uuid'));
    echo (string) $a->getBody();
    echo (string) $b->getBody();
});
$main();

Ein nicht blockierender TCP-Server

ReactPHP glänzt bei Servern. Ein SocketServer akzeptiert Verbindungen und löst Ereignisse aus; jede Verbindung ist selbst ein nicht blockierender Stream. Ein Prozess verarbeitet Tausende gleichzeitig verbundener Clients.

<?php
use React\Socket\SocketServer;
use React\Socket\ConnectionInterface;

$socket = new SocketServer('127.0.0.1:8080');
$socket->on('connection', function (ConnectionInterface $conn) {
    $conn->write("Welcome\n");
    $conn->on('data', function ($data) use ($conn) {
        $conn->write('echo: ' . $data); // streamed back, non-blocking
    });
});
echo "listening on 127.0.0.1:8080\n";

Ein HTTP-Server in wenigen Zeilen

Der react/http-Server verarbeitet Anfragen mit einem Callback, das eine Response (oder ein Promise dafür) zurückgibt. Da es sich um einen persistenten Prozess handelt, initialisieren Sie Abhängigkeiten einmal — dafür sind Sie auch für die Speicherverwaltung über mehrere Anfragen hinweg verantwortlich.

<?php
use React\Http\HttpServer;
use React\Http\Message\Response;
use Psr\Http\Message\ServerRequestInterface;
use React\Socket\SocketServer;

$http = new HttpServer(function (ServerRequestInterface $request) {
    return Response::json(['path' => $request->getUri()->getPath()]);
});
$http->listen(new SocketServer('0.0.0.0:8000'));
echo "http://0.0.0.0:8000\n";

Streaming ohne Pufferung

ReactPHP-Streams implementieren Backpressure: Sie können einen lesbaren Stream mit pipe() in einen beschreibbaren Stream leiten, und die Laufzeitumgebung drosselt automatisch. So können Sie Gigabytes mit einem konstant geringen Speicherbedarf übertragen, statt Dateien in den Arbeitsspeicher zu laden.

<?php
use React\Stream\ReadableResourceStream;
use React\Stream\WritableResourceStream;

$source = new ReadableResourceStream(fopen('big.log', 'r'));
$dest   = new WritableResourceStream(fopen('php://stdout', 'w'));
$source->pipe($dest); // back-pressure aware, never buffers whole file

Die Todsünde: die Event-Schleife blockieren

Alles bricht zusammen, sobald Sie innerhalb eines Handlers eine blockierende Funktion aufrufen — sleep(), blockierendes PDO, curl_exec, file_get_contents für eine URL. Diese Funktionen frieren den einzelnen Thread ein und bringen jede Verbindung zum Stillstand. Verwenden Sie die React-Entsprechungen: Loop::addTimer für Verzögerungen, clue/reactphp-* oder asynchrone DB-Treiber für Abfragen und den Browser für HTTP.

<?php
use React\EventLoop\Loop;
use function React\Async\await;
use React\Promise\Deferred;

// BAD:  sleep(2);  // freezes the entire loop
// GOOD: async delay that yields control
function delay(float $s): \React\Promise\PromiseInterface {
    $d = new Deferred();
    Loop::addTimer($s, fn() => $d->resolve(null));
    return $d->promise();
}
// await(delay(2.0)); // inside a fiber/coroutine

Nebenläufigkeit begrenzen

Unbegrenztes Fan-out kann Dateideskriptoren erschöpfen oder eine nachgelagerte API überlasten. clue/mq-react von ReactPHP reiht Aufgaben mit einer Nebenläufigkeitsgrenze ein und verarbeitet jeweils N Promises, während der Rest wartet. Dadurch wird die eigene ausgehende Last durch Backpressure begrenzt.

<?php
use Clue\React\Mq\Queue;
use React\Http\Browser;

$browser = new Browser();
// At most 10 in-flight requests across 1000 URLs
$q = new Queue(10, null, fn($url) => $browser->get($url));

$promises = array_map(fn($u) => $q($u), $thousandUrls ?? []);
\React\Promise\all($promises)->then(function ($responses) {
    echo 'completed ' . count($responses) . " with cap of 10\n";
});

Kurzer Test

Wie pausieren Sie innerhalb eines ReactPHP-Handlers zwei Sekunden, ohne die Nebenläufigkeit zu beeinträchtigen?

Zusammenfassung

ReactPHP bringt nicht blockierende I/O in Vanilla-PHP:

  • Eine einzige Event Loop (Loop-Fassade) steuert Timer, Sockets und Streams – ganz ohne benötigte Erweiterung.
  • Operationen liefern Promises; all() führt Anforderungen parallel aus, und react/async + Fibers ermöglichen mit await() Code, der wie synchroner Code aussieht.
  • Erstellen Sie persistente TCP/HTTP-Server und Streams mit Backpressure, die Tausende von Verbindungen pro Prozess verarbeiten.
  • Blockieren Sie niemals die Event Loop – ersetzen Sie sleep/blockierende I/O durch React-Timer und asynchrone Treiber.

Häufig gestellte Fragen

Ist die Lektion „Event Loops mit ReactPHP“ kostenlos?

Ja — der vollständige Text von „Event Loops mit ReactPHP“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des PHP Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Event Loops mit ReactPHP“?

Führen Sie nicht blockierendes I/O mit dem ReactPHP-Event-Loop aus. Du übst PHP Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um PHP Academy zu starten?

Keine Vorkenntnisse erforderlich. PHP Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Event Loops mit ReactPHP“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser PHP Academy-Lektion Code schreiben und ausführen?

Ja. Jede PHP Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Das PHP-Konkurrenzmodell
  2. PHP 8.1 Fibers
  3. Event Loops mit ReactPHP
  4. Hochleistungsserver mit Swoole
← Zurück zu PHP Academy