Что такое Fiber?
Fiber — это легковесный поток выполнения с возможностью приостановки и возобновления. Fiber позволяет реализовать кооперативную многозадачность (cooperative multitasking) в PHP.
Обычная функция: start ──────────────────────► return
Fiber: start ──► suspend ──► resume ──► suspend ──► resume ──► return
↓ value ↑ value ↓ value ↑ value
(main code) (main code)
<?php
declare(strict_types=1);
// Basic Fiber example
$fiber = new Fiber(function (): string {
$value = Fiber::suspend('first'); // Pause, send 'first' to caller
echo "Received: {$value}\n"; // 'hello'
$value = Fiber::suspend('second'); // Pause again, send 'second'
echo "Received: {$value}\n"; // 'world'
return 'done'; // Final return value
});
// Start the fiber
$result = $fiber->start();
echo "Got: {$result}\n"; // 'first'
// Resume with a value
$result = $fiber->resume('hello');
echo "Got: {$result}\n"; // 'second'
// Resume again
$result = $fiber->resume('world');
// Output: "Received: hello", "Received: world"
echo $fiber->getReturn(); // 'done'
Запомни:
Fiber::suspend()приостанавливает fiber и возвращает значение в вызывающий код.$fiber->resume($value)возобновляет fiber, передавая значение вFiber::suspend(). Это двусторонний канал коммуникации.
API класса Fiber
<?php
declare(strict_types=1);
$fiber = new Fiber(function (string $initial): string {
echo "Started with: {$initial}\n";
$value = Fiber::suspend('paused');
return "finished with: {$value}";
});
// Fiber lifecycle methods
$fiber->start('init'); // Start fiber, pass initial argument
$fiber->resume('data'); // Resume suspended fiber
// State checking
$fiber->isStarted(); // bool — has start() been called?
$fiber->isRunning(); // bool — currently executing?
$fiber->isSuspended(); // bool — paused at suspend()?
$fiber->isTerminated(); // bool — completed or threw exception?
// Get return value (only after termination)
$fiber->getReturn(); // mixed — return value (throws if not terminated)
// Throw exception into fiber
$fiber = new Fiber(function (): void {
try {
Fiber::suspend();
} catch (RuntimeException $e) {
echo "Caught in fiber: {$e->getMessage()}\n";
}
});
$fiber->start();
$fiber->throw(new RuntimeException('Error from outside'));
| Метод | Описание |
|---|---|
start(mixed ...$args) |
Запускает fiber с аргументами |
resume(mixed $value) |
Возобновляет приостановленный fiber |
throw(Throwable $e) |
Бросает исключение в точку suspend |
isStarted() |
Был ли fiber запущен |
isRunning() |
Выполняется ли сейчас |
isSuspended() |
Приостановлен ли |
isTerminated() |
Завершён ли |
getReturn() |
Возвращаемое значение |
Fiber::suspend(mixed $value) |
Статический — приостановка изнутри |
Ловушка экзамена:
Fiber::suspend()— это СТАТИЧЕСКИЙ метод, вызывается изнутри fiber.$fiber->resume()— это метод экземпляра, вызывается из основного кода. Нельзя вызватьresume()на fiber, который не приостановлен.
Состояния Fiber
<?php
declare(strict_types=1);
$fiber = new Fiber(function (): void {
Fiber::suspend();
});
// State transitions:
// Created → start() → Running → suspend() → Suspended → resume() → Running → return → Terminated
// → throw() → Running
var_dump($fiber->isStarted()); // false
var_dump($fiber->isSuspended()); // false
var_dump($fiber->isTerminated()); // false
$fiber->start();
var_dump($fiber->isStarted()); // true
var_dump($fiber->isSuspended()); // true
var_dump($fiber->isTerminated()); // false
$fiber->resume();
var_dump($fiber->isStarted()); // true
var_dump($fiber->isSuspended()); // false
var_dump($fiber->isTerminated()); // true
FiberError
<?php
declare(strict_types=1);
// FiberError is thrown for invalid fiber operations
$fiber = new Fiber(function (): void {
Fiber::suspend();
});
// Cannot resume fiber that hasn't started
try {
$fiber->resume(); // FiberError
} catch (FiberError $e) {
echo $e->getMessage(); // "Cannot resume a fiber that is not suspended"
}
$fiber->start();
$fiber->resume(); // Fiber terminates
// Cannot resume terminated fiber
try {
$fiber->resume(); // FiberError
} catch (FiberError $e) {
echo $e->getMessage(); // "Cannot resume a terminated fiber"
}
// Cannot get return from non-terminated fiber
$fiber2 = new Fiber(function (): string {
Fiber::suspend();
return 'result';
});
$fiber2->start();
try {
$fiber2->getReturn(); // FiberError
} catch (FiberError $e) {
echo $e->getMessage(); // "Cannot get return value of a fiber that hasn't terminated"
}
// Cannot call suspend() outside of a fiber
try {
Fiber::suspend(); // FiberError
} catch (FiberError $e) {
echo $e->getMessage(); // "Cannot suspend outside of a fiber"
}
Ловушка экзамена:
Fiber::suspend()вне fiber выброситFiberError.getReturn()до завершения fiber — тожеFiberError. Нельзяresume()на не-приостановленный fiber и нельзяstart()повторно.
Практические паттерны
Генератор значений (аналог Generator)
<?php
declare(strict_types=1);
// Fiber-based value producer
$counter = new Fiber(function (): void {
$i = 0;
while (true) {
Fiber::suspend($i++);
}
});
$counter->start(); // returns 0
echo $counter->resume(); // 1
echo $counter->resume(); // 2
echo $counter->resume(); // 3
Scheduler (планировщик задач)
<?php
declare(strict_types=1);
class Scheduler
{
/** @var SplQueue<Fiber> */
private SplQueue $queue;
public function __construct()
{
$this->queue = new SplQueue();
}
public function schedule(Fiber $fiber): void
{
$this->queue->enqueue($fiber);
}
public function run(): void
{
while (!$this->queue->isEmpty()) {
$fiber = $this->queue->dequeue();
if (!$fiber->isStarted()) {
$fiber->start();
} elseif ($fiber->isSuspended()) {
$fiber->resume();
}
// Re-schedule if not terminated
if (!$fiber->isTerminated()) {
$this->queue->enqueue($fiber);
}
}
}
}
// Usage
$scheduler = new Scheduler();
$scheduler->schedule(new Fiber(function (): void {
echo "Task A - step 1\n";
Fiber::suspend();
echo "Task A - step 2\n";
}));
$scheduler->schedule(new Fiber(function (): void {
echo "Task B - step 1\n";
Fiber::suspend();
echo "Task B - step 2\n";
}));
$scheduler->run();
// Output:
// Task A - step 1
// Task B - step 1
// Task A - step 2
// Task B - step 2
Async I/O simulation
<?php
declare(strict_types=1);
// Simulating non-blocking I/O with Fibers
function asyncFetch(string $url): Fiber
{
return new Fiber(function () use ($url): string {
// In real code, this would start a non-blocking HTTP request
echo "Starting fetch: {$url}\n";
Fiber::suspend(); // Yield control while "waiting"
// "Response received"
return "Response from {$url}";
});
}
$fibers = [
asyncFetch('https://api.example.com/users'),
asyncFetch('https://api.example.com/posts'),
asyncFetch('https://api.example.com/comments'),
];
// Start all
foreach ($fibers as $fiber) {
$fiber->start();
}
// Resume all (simulate responses arriving)
$results = [];
foreach ($fibers as $fiber) {
$fiber->resume();
$results[] = $fiber->getReturn();
}
print_r($results);
Fiber vs Generator
| Аспект | Generator | Fiber |
|---|---|---|
| Создание | function* / yield |
new Fiber(callable) |
| Приостановка | yield $value |
Fiber::suspend($value) |
| Возобновление | $gen->send($val) |
$fiber->resume($val) |
| Глубина вызовов | Только в самой функции | В любом месте стека |
| Итерация | Реализует Iterator |
Нет |
| Двусторонний обмен | Да (yield/send) |
Да (suspend/resume) |
| Основное назначение | Ленивые последовательности | Кооперативная многозадачность |
<?php
declare(strict_types=1);
// KEY difference: Fiber::suspend() works from ANY depth
function innerFunction(): void
{
// This works in Fiber!
Fiber::suspend('from deep inside');
}
function middleFunction(): void
{
innerFunction();
}
$fiber = new Fiber(function (): void {
middleFunction(); // suspend() called from 3 levels deep — OK!
});
$result = $fiber->start();
echo $result; // 'from deep inside'
// Generators CANNOT do this!
// yield only works directly in the generator function
Запомни: Главное преимущество Fiber перед Generator —
Fiber::suspend()можно вызвать на ЛЮБОЙ глубине стека вызовов.yieldработает только непосредственно в функции-генераторе. Это делает Fiber идеальным для библиотек async I/O.
Fiber в реальных проектах
<?php
declare(strict_types=1);
// Fibers are the foundation for async frameworks:
// - ReactPHP (event loop)
// - Amp v3 (uses Fibers natively)
// - Revolt (event loop for PHP)
// - AMPHP (concurrent HTTP requests)
// Example concept: non-blocking delay
class EventLoop
{
/** @var array<array{fiber: Fiber, readyAt: float}> */
private array $timers = [];
public function delay(float $seconds): void
{
$fiber = Fiber::this(); // Get current fiber (PHP 8.2+: fictional)
// In reality, the event loop handles this internally
$this->timers[] = [
'fiber' => $fiber,
'readyAt' => microtime(true) + $seconds,
];
Fiber::suspend();
}
public function run(): void
{
while (!empty($this->timers)) {
$now = microtime(true);
foreach ($this->timers as $key => $timer) {
if ($timer['readyAt'] <= $now) {
unset($this->timers[$key]);
$timer['fiber']->resume();
}
}
usleep(1000); // Prevent busy-waiting
}
}
}
// Note: In production, use Amp v3 or ReactPHP
// which provide proper event loop implementations
Обработка ошибок в Fiber
<?php
declare(strict_types=1);
// Exception propagation
$fiber = new Fiber(function (): void {
throw new RuntimeException('Error inside fiber');
});
try {
$fiber->start(); // Exception propagates to caller
} catch (RuntimeException $e) {
echo "Caught: {$e->getMessage()}\n"; // "Error inside fiber"
}
// Injecting exceptions via throw()
$fiber = new Fiber(function (): string {
try {
$value = Fiber::suspend('waiting');
return "Got: {$value}";
} catch (RuntimeException $e) {
return "Error handled: {$e->getMessage()}";
}
});
$fiber->start();
$fiber->throw(new RuntimeException('Timeout'));
echo $fiber->getReturn(); // "Error handled: Timeout"