0Pricing
PHP Academy · Lekcja

Fibers w PHP 8.1

Proszę wstrzymywać i wznawiać wykonywanie za pomocą natywnych Fibers.

Fibers w PHP 8.1 to bezpłatna lekcja PHP Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej PHP Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs PHP Academy zawiera 4 lekcji w sumie.

Co dają Fibers

PHP 8.1 dodał Fibers: niskopoziomowy prymityw rdzenia, który pozwala wstrzymać wykonywany stos wywołań i wznowić go później z zupełnie innego miejsca. Fiber to pełny, niezależny stos, który można zawiesić w dowolnym punkcie i kontynuować bez rozwijania stosu. To brakujący element, dzięki któremu biblioteki mogą zastąpić łańcuchy promisów synchronicznie wyglądającym await — mechanizm ten stanowi podstawę współczesnych API ReactPHP i Amp.

Pierwszy Fiber

Należy utworzyć Fiber za pomocą callbacku, uruchomić go przez start() i pozwolić mu samodzielnie wywołać suspend(). W punkcie zawieszenia sterowanie wraca do wywołującego; resume() wznawia wykonanie dokładnie w tym miejscu.

<?php
$fiber = new Fiber(function (): void {
    echo "A: inside fiber\n";
    $received = Fiber::suspend('paused');
    echo "C: resumed with '$received'\n";
});

$value = $fiber->start();          // prints A, returns 'paused'
echo "B: fiber suspended, got '$value'\n";
$fiber->resume('hello');           // prints C
echo "D: fiber done\n";

Cztery podstawowe metody

API jest niewielkie:

  • $fiber->start(...$args) — rozpoczyna wykonanie; zwraca pierwszą wartość zawieszenia.
  • Fiber::suspend($value) — wstrzymuje bieżący Fiber, przekazując $value wznawiającemu wykonanie.
  • $fiber->resume($value) — wznawia wykonanie; $value staje się wartością zwracaną przez suspend().
  • $fiber->getReturn() — zwraca wartość callbacku po jego zakończeniu.

Wartości przepływają przez granicę zawieszenia w obu kierunkach.

Dwukierunkowe przekazywanie wartości

Fiber może przekazać żądanie na zewnątrz i odebrać odpowiedź z powrotem — to podstawa sposobu, w jaki pętla zdarzeń dostarcza wyniki operacji wejścia-wyjścia zawieszonym zadaniom.

<?php
$fiber = new Fiber(function (): int {
    $a = Fiber::suspend('need first number');
    $b = Fiber::suspend('need second number');
    return $a + $b;
});

$fiber->start();        // suspends: 'need first number'
$fiber->resume(10);     // a = 10, suspends: 'need second number'
$fiber->resume(32);     // b = 32, fiber returns
echo $fiber->getReturn(), PHP_EOL; // 42

Stan i introspekcja Fiberów

Można sprawdzać cykl życia Fiberu. Wywołanie resume() na zakończonym Fiberze zgłasza FiberError, dlatego podczas budowania planisty należy zabezpieczyć się za pomocą tych sprawdzeń.

<?php
$fiber = new Fiber(function () { Fiber::suspend(); });
var_dump($fiber->isStarted());     // false
$fiber->start();
var_dump($fiber->isStarted());     // true
var_dump($fiber->isSuspended());   // true
var_dump($fiber->isRunning());     // false (we're outside it)
$fiber->resume();
var_dump($fiber->isTerminated());  // true

Budowa małego planisty

Fibers i kolejka dają kooperatywny planista. Każde zadanie zawiesza się, aby oddać sterowanie; planista wykonuje zadania cyklicznie, metodą round-robin, aż wszystkie zostaną ukończone. To szkielet każdej asynchronicznej platformy wykonawczej.

<?php
$tasks = [];
foreach (['X', 'Y', 'Z'] as $name) {
    $tasks[] = new Fiber(function () use ($name) {
        for ($i = 1; $i <= 2; $i++) {
            echo "$name step $i\n";
            Fiber::suspend();   // give others a turn
        }
    });
}
foreach ($tasks as $t) { $t->start(); }
while ($tasks) {
    foreach ($tasks as $k => $t) {
        if ($t->isTerminated()) { unset($tasks[$k]); continue; }
        $t->resume();
    }
}

Wyjątki na granicach

throwInto() (w API nazywane throw(), czyli $fiber->throw()) wstrzykuje wyjątek w punkcie zawieszenia — w ten sposób pętla zgłasza nieudaną operację wejścia-wyjścia oczekującemu zadaniu. Wyjątek pojawia się dokładnie tam, gdzie Fiber został wstrzymany.

<?php
$fiber = new Fiber(function () {
    try {
        Fiber::suspend();
    } catch (RuntimeException $e) {
        echo 'caught: ' . $e->getMessage() . PHP_EOL;
    }
});
$fiber->start();
$fiber->throw(new RuntimeException('io failed')); // prints: caught: io failed

Fibers a generatory

Generatory (yield) również zawieszają wykonanie — ale tylko zewnętrzną funkcję. Aby oczekiwać w zagnieżdżonym pomocniku za pomocą generatorów, trzeba stosować yield from aż do samego szczytu stosu, oznaczając każdą funkcję jako generator. Fibers zawieszają cały stos z dowolnej głębokości, więc głęboko zagnieżdżone wywołanie może użyć Fiber::suspend(), a jego wywołujący nie muszą o tym wiedzieć. Dzięki temu await może być zwykłym wywołaniem funkcji, a nie yield.

<?php
function deep(): string {
    // No yield needed in the call chain — suspends the whole fiber
    return Fiber::suspend('awaiting from deep');
}
$f = new Fiber(fn() => print(deep() . PHP_EOL));
$f->start();
$f->resume('value injected at depth');

Ograniczenie zawieszania

Obowiązuje jedna niepodważalna zasada: Fiber::suspend() można wywołać wyłącznie wewnątrz Fiberu. Zawieszenie z głównego wątku lub w ramach wywołania zwrotnego na poziomie C, które nie obsługuje Fibers (na przykład w niektórych starszych komparatorach usort i określonych callbackach wewnętrznych), powoduje wyjątek FiberError: Cannot suspend outside of a fiber. Biblioteki ukrywają ten szczegół, zapewniając, że cały kod użytkownika działa wewnątrz Fiberu sterowanego przez pętlę.

<?php
try {
    Fiber::suspend(); // main thread — illegal
} catch (FiberError $e) {
    echo $e->getMessage(), PHP_EOL; // Cannot suspend outside of a fiber
}

Jak biblioteki używają Fibers

W kodzie aplikacji rzadko pisze się surowe Fibers. Frameworki takie jak Amp v3 i ReactPHP (za pośrednictwem Fibers) opakowują je: await($promise) wewnętrznie wywołuje Fiber::suspend(), rejestruje kontynuację w pętli zdarzeń i wywołuje resume() Fiberu po ukończeniu operacji wejścia-wyjścia. Kod wygląda synchronicznie, a środowisko wykonawcze zajmuje się zawieszaniem i planowaniem.

<?php
// Conceptual: what an await() helper does under the hood
function await(Promise $p): mixed {
    $fiber = Fiber::getCurrent() ?? throw new Error('await() outside fiber');
    $p->then(fn($v) => $fiber->resume($v),
             fn($e) => $fiber->throw($e));
    return Fiber::suspend(); // parked until the promise settles
}

Uwagi dotyczące pamięci i cyklu życia

Każdy Fiber przydziela własny stos, więc uruchomienie setek tysięcy Fiberów ma koszt pamięciowy, choć jest on znacznie mniejszy niż w przypadku wątków systemu operacyjnego. Fiber zawieszony na zawsze nie zostanie usunięty przez mechanizm zbierania śmieci, jeśli coś nadal przechowuje do niego odwołanie — to subtelny wyciek w długo działających serwerach. Ponadto callback Fiberu, który zgłasza nieprzechwycony wyjątek, propaguje go poza wywołanie resume()/start(), dlatego planista musi opakowywać wznowienia w try/catch.

<?php
$fiber = new Fiber(function () {
    throw new RuntimeException('boom inside fiber');
});
try {
    $fiber->start(); // exception surfaces here, at the resumer
} catch (RuntimeException $e) {
    echo 'scheduler caught: ' . $e->getMessage() . PHP_EOL;
}

Szybki test

Jaka jest kluczowa przewaga Fibers nad generatorami w zastosowaniach asynchronicznych?

Podsumowanie

Fibers to niskopoziomowy prymityw współbieżności w PHP:

  • start(), Fiber::suspend(), resume() i getReturn() wstrzymują i wznawiają cały stos wywołań, przekazując wartości w obu kierunkach.
  • Metody stanu (isSuspended, isTerminated) oraz throw() pozwalają budować kooperatywny planista i wstrzykiwać błędy operacji wejścia-wyjścia.
  • W przeciwieństwie do generatorów Fiber można zawiesić z dowolnej głębokości, co umożliwia przejrzyste await().
  • Zawieszanie jest dozwolone wyłącznie wewnątrz Fiberu; frameworki takie jak Amp i ReactPHP opakowują Fibers, dzięki czemu kod asynchroniczny wygląda synchronicznie.

Często zadawane pytania

Czy lekcja „Fibers w PHP 8.1” jest bezpłatna?

Tak — pełny tekst „Fibers w PHP 8.1” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu PHP Academy, przejdź na CoddyKit PRO. Kurs PHP Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Fibers w PHP 8.1”?

Proszę wstrzymywać i wznawiać wykonywanie za pomocą natywnych Fibers. Ćwiczysz PHP Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć PHP Academy?

Nie wymagamy żadnego doświadczenia. PHP Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Fibers w PHP 8.1”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji PHP Academy?

Tak. Każda lekcja PHP Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Model współbieżności PHP
  2. Fibers w PHP 8.1
  3. Pętle zdarzeń z ReactPHP
  4. Wydajne serwery z Swoole
← Powrót do PHP Academy