PHP Academy · Lektion

Event loops med ReactPHP

Kør non-blocking I/O med ReactPHP-event-loopet.

Lektion 3 af 413 trin

Event loops med ReactPHP er en gratis PHP Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i PHP Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. PHP Academy-kurset indeholder 4 lektioner i alt.

ReactPHP i én sætning

ReactPHP er et rent PHP-værktøjssæt uden afhængigheder til ikke-blokerende I/O, bygget omkring et enkelt event-loop. Der kræves ingen udvidelse — det fungerer med standard-PHP ved hjælp af stream_select (eller ext-event/ext-ev, når de er tilgængelige). Med det kan du skrive langvarigt kørende servere, samtidige HTTP-klienter og streaming-forløb i ren PHP.

Det globale event-loop

Moderne ReactPHP (v1.2+) viser et standard-loop via den statiske facade React\EventLoop\Loop — du behøver ikke sende en loop-instans rundt overalt. Planlæg et callback med en timer; loopet kører, indtil der ikke er mere arbejde.

<?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+

Timere og periodisk arbejde

Ikke-blokerende tidsstyring erstatter sleep(). addTimer udløses én gang; addPeriodicTimer gentages. Annuller en af dem for at stoppe den. Fordi de styres af loopet, fortsætter andre opgaver med at køre, mens en timer venter.

<?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 til asynkrone resultater

ReactPHP returnerer et promise for enhver operation, der bliver færdig senere. Tilknyt then() for succes og et andet callback for fejl. Promises kan sammensættes, så du kan omforme og kæde resultater uden at indlejre callbacks dybt.

<?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()));

Parallel HTTP med den asynkrone klient

Den store fordel er, at du kan starte mange HTTP-forespørgsler samtidigt fra én tråd. React\Http\Browser returnerer promises; React\Promise\all() venter på hele gruppen. Ti forespørgsler på ét sekund bliver færdige på cirka ét sekund, ikke ti.

<?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 får koden til at se synkron ud

Callback-kæder bliver hurtigt uhåndterlige. ReactPHP integreres med react/async, som bruger PHP 8.1 Fibers, så du kan bruge await() på et promise og skrive kode trin for trin. Wrapperen coroutine()/async() kører din funktion inde i en fiber.

<?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();

En ikke-blokerende TCP-server

ReactPHP er særligt velegnet til servere. En SocketServer accepterer forbindelser og udsender hændelser; hver forbindelse er selv en ikke-blokerende stream. Én proces håndterer tusindvis af samtidige klienter.

<?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";

En HTTP-server på få linjer

Serveren react/http håndterer forespørgsler med et callback, der returnerer en Response (eller et promise, der indeholder en). Da det er en vedvarende proces, initialiserer du afhængigheder én gang — men du har også selv ansvaret for hukommelseshåndteringen mellem forespørgsler.

<?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 uden buffering

ReactPHP-streams implementerer flowkontrol: Du kan føre en læsbar stream ind i en skrivbar stream med pipe(), og kørselsmiljøet begrænser automatisk hastigheden. Det lader dig flytte gigabytes med et konstant, lille hukommelsesforbrug i stedet for at indlæse filer i RAM.

<?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

Dødssynden: blokering af event-loopet

Alt går i stykker, så snart du kalder en blokerende funktion i en behandler — sleep(), blokerende PDO, curl_exec eller file_get_contents på en URL. De fryser den enkelte tråd og standser alle forbindelser. Brug Reacts alternativer: Loop::addTimer til forsinkelser, clue/reactphp-* eller asynkrone database-drivere til forespørgsler og Browser til 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

Begrænsning af samtidighed

Ubegrænset forgrening kan opbruge fildeskriptorer eller overbelaste en underliggende API. ReactPHPs clue/mq-react sætter arbejde i kø med en grænse for samtidigheden og behandler N promises ad gangen, mens resten venter. Det begrænser belastningen fra dine egne udgående kald.

<?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";
});

Hurtigt tjek

Hvordan holder du pause i 2 sekunder i en ReactPHP-behandler uden at påvirke samtidigheden?

Opsummering

ReactPHP bringer ikke-blokerende I/O til almindelig PHP:

  • En enkelt event loop (Loop-facaden) driver timere, sockets og streams – ingen udvidelse er nødvendig.
  • Operationer returnerer promises; all() kører forespørgsler parallelt, og react/async + Fibers lader dig bruge await() til kode, der ser synkron ud.
  • Byg persistente TCP/HTTP-servere og streams med backpressure, der håndterer tusindvis af forbindelser pr. proces.
  • Blokér aldrig event loopet – erstat sleep/blokerende I/O med React-timere og asynkrone drivere.
Gratis at komme i gang

Lær PHP med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
49
Lektioner
195

Ofte stillede spørgsmål

Er lektionen “Event loops med ReactPHP” gratis?

Ja — hele teksten til “Event loops med ReactPHP” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af PHP Academy-kurset, skal du opgradere til CoddyKit PRO. PHP Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Event loops med ReactPHP”?

Kør non-blocking I/O med ReactPHP-event-loopet. Du øver dig i PHP Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på PHP Academy?

Der kræves ingen tidligere erfaring. PHP Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Event loops med ReactPHP”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne PHP Academy-lektion?

Ja. Alle PHP Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. PHP's concurrency-model
  2. PHP 8.1 Fibers
  3. Event loops med ReactPHP
  4. Højtydende servere med Swoole
← Tilbage til PHP Academy