Fibers em PHP 8.1+
Guia completo sobre a classe Fiber nativa do PHP 8.1+: API, estados, comunicação bidirecional e exemplos práticos de concorrência cooperativa
🧵 Fibers em PHP 8.1+
Fibers são uma funcionalidade nativa introduzida no PHP 8.1 que permite pausar e retomar a execução de código sem bloquear todo o processo. Elas são a base para implementar concorrência cooperativa e frameworks assíncronos em PHP puro, sem necessidade de extensões externas.
🎯 Importante: Este guia cobre apenas a classe
Fibernativa do PHP 8.1+. Para o conceito mais amplo de coroutines, veja Coroutines em PHP.
📚 Índice
- O que são Fibers?
- Estados de uma Fiber
- API Completa
- Criando e Executando Fibers
- Comunicação Bidirecional
- Tratamento de Exceções
- Construindo um Agendador
- Exemplos Práticos
- Boas Práticas
- Limitações e Quando Usar
🎓 O que são Fibers?
O Problema: Código Bloqueante
<?php
// ❌ Código bloqueante tradicional
function task1() {
echo "Task 1: Início\n";
sleep(2); // BLOQUEIA todo o programa por 2s
echo "Task 1: Fim\n";
}
function task2() {
echo "Task 2: Início\n";
sleep(1); // BLOQUEIA todo o programa por 1s
echo "Task 2: Fim\n";
}
$start = microtime(true);
task1(); // Espera 2s
task2(); // Espera 1s
$duration = microtime(true) - $start;
echo "Tempo total: " . round($duration) . "s\n";
// Saída esperada
// Task 1: Início
// Task 1: Fim
// Task 2: Início
// Task 2: Fim
// Tempo total: 3s
Problema: Enquanto task1() dorme, não podemos executar task2(). Desperdiçamos 3 segundos quando poderíamos usar apenas 2s.
A Solução: Fibers
<?php
// ✅ Com Fibers - execução intercalada
$fiber1 = new Fiber(function() {
echo "Fiber 1: Passo 1\n";
Fiber::suspend(); // Pausa e libera controle
echo "Fiber 1: Passo 2\n";
});
$fiber2 = new Fiber(function() {
echo "Fiber 2: Passo 1\n";
Fiber::suspend(); // Pausa e libera controle
echo "Fiber 2: Passo 2\n";
});
// Execução intercalada
$fiber1->start(); // "Fiber 1: Passo 1" → pausa
$fiber2->start(); // "Fiber 2: Passo 1" → pausa
$fiber1->resume(); // "Fiber 1: Passo 2" → termina
$fiber2->resume(); // "Fiber 2: Passo 2" → termina
// Saída esperada
// Fiber 1: Passo 1
// Fiber 2: Passo 1
// Fiber 1: Passo 2
// Fiber 2: Passo 2
Vantagem: Podemos pausar fiber1, executar fiber2, e retomar fiber1 depois, aproveitando melhor o tempo.
🔄 Estados de uma Fiber
Uma Fiber passa por diferentes estados durante seu ciclo de vida:
┌─────────────────┐
│ NÃO INICIADA │
│ isStarted()=false│
└────────┬────────┘
│ start()
↓
┌─────────────────┐
│ EXECUTANDO │
│ isRunning()=true │
└────────┬────────┘
│ Fiber::suspend()
↓
┌─────────────────┐
│ SUSPENSA │
│isSuspended()=true│
└────────┬────────┘
│ resume()
↓
┌─────────────────┐
│ EXECUTANDO │
└────────┬────────┘
│ return / exceção / fim
↓
┌─────────────────┐
│ TERMINADA │
│isTerminated()=true│
└─────────────────┘
Exemplo Completo de Estados
<?php
$fiber = new Fiber(function() {
echo "Executando...\n";
Fiber::suspend();
echo "Retomado...\n";
return "Finalizado!";
});
// Estado inicial: NÃO INICIADA
echo "Estado inicial:\n";
var_dump($fiber->isStarted()); // false
var_dump($fiber->isRunning()); // false
var_dump($fiber->isSuspended()); // false
var_dump($fiber->isTerminated()); // false
// Inicia a Fiber
$fiber->start();
// Estado: SUSPENSA (pausou em Fiber::suspend())
echo "\nApós start():\n";
var_dump($fiber->isStarted()); // true
var_dump($fiber->isSuspended()); // true
var_dump($fiber->isTerminated()); // false
// Retoma a Fiber
$result = $fiber->resume();
// Estado: TERMINADA
echo "\nApós resume():\n";
var_dump($fiber->isTerminated()); // true
var_dump($result); // string(11) "Finalizado!"
// Saída esperada
// Estado inicial:
// bool(false)
// bool(false)
// bool(false)
// bool(false)
// Executando...
//
// Após start():
// bool(true)
// bool(true)
// bool(false)
// Retomado...
//
// Após resume():
// bool(true)
// NULL
📋 API Completa
Métodos de Instância
<?php
$fiber = new Fiber(callable $callback);
// Iniciar execução
$fiber->start(mixed ...$args): mixed;
// Retomar execução (quando suspensa)
$fiber->resume(mixed $value = null): mixed;
// Lançar exceção dentro da Fiber
$fiber->throw(Throwable $exception): mixed;
// Verificar estados
$fiber->isStarted(): bool; // Já foi iniciada?
$fiber->isSuspended(): bool; // Está pausada?
$fiber->isRunning(): bool; // Está executando AGORA?
$fiber->isTerminated(): bool; // Já terminou?
Métodos Estáticos (dentro de uma Fiber)
<?php
// Obter a Fiber atualmente em execução
Fiber::getCurrent(): ?Fiber;
// Suspender a Fiber atual
Fiber::suspend(mixed $value = null): mixed;
Exemplo da API
<?php
$fiber = new Fiber(function(string $name): string {
echo "Olá, $name!\n";
$received = Fiber::suspend("Primeira pausa");
echo "Recebi: $received\n";
$received = Fiber::suspend("Segunda pausa");
echo "Recebi: $received\n";
return "Tchau, $name!";
});
// start() - inicia com argumentos
$value = $fiber->start("João");
echo "Fiber retornou: $value\n";
### Exemplo 1: Fiber Simples
```php
<?php
// Criar uma Fiber
$fiber = new Fiber(function(): void {
echo "Linha 1\n";
echo "Linha 2\n";
echo "Linha 3\n";
});
// Iniciar execução
$fiber->start();
echo "Fiber terminou!\n";
// Saída esperada
// Linha 1
// Linha 2
// Linha 3
// Fiber terminou!
Exemplo 2: Passando Argumentos
<?php
$fiber = new Fiber(function(int $a, int $b): int {
$sum = $a + $b;
echo "Somando $a + $b = $sum\n";
return $sum;
});
// Passa argumentos via start()
$fiber->start(5, 3);
echo "Concluído\n";
// Saída esperada
// Somando 5 + 3 = 8
// Concluído
Exemplo 3: Múltiplas Pausas
<?php
$fiber = new Fiber(function(): void {
for ($i = 1; $i <= 5; $i++) {
echo "Contador: $i\n";
// Pausa após cada número (exceto o último)
if ($i < 5) {
Fiber::suspend();
}
}
});
$fiber->start(); // Imprime 1 e pausa
echo "--- Fazendo outras coisas ---\n";
// Retoma 4 vezes para completar
while (!$fiber->isTerminated()) {
$fiber->resume();
}
echo "Concluído!\n";
// Saída esperada
// Contador: 1
// --- Fazendo outras coisas ---
// Contador: 2
// Contador: 3
// Contador: 4
// Contador: 5
// Concluído!
Exemplo 4: Retornando Valores
<?php
$fiber = new Fiber(function(): array {
echo "Processando...\n";
Fiber::suspend();
echo "Finalizando...\n";
return ['status' => 'success', 'data' => [1, 2, 3]];
});
$fiber->start();
echo "Fiber pausada\n";
$fiber->resume();
echo "Concluído\n";
// Saída esperada
// Processando...
// Fiber pausada
// Finalizando...
// Concluído
🔄 Comunicação Bidirecional
A verdadeira potência das Fibers está na comunicação bidirecional: podemos enviar valores PARA a Fiber e receber valores DE VOLTA.
Enviando para a Fiber
<?php
$fiber = new Fiber(function(): void {
// suspend() retorna o valor passado em resume()
$value1 = Fiber::suspend();
echo "Recebi: $value1\n";
$value2 = Fiber::suspend();
echo "Recebi: $value2\n";
});
$fiber->start();
// Envia valores ao retomar
$fiber->resume("Olá");
$fiber->resume("Mundo");
// Saída esperada
// Recebi: Olá
// Recebi: Mundo
Recebendo da Fiber
<?php
$fiber = new Fiber(function(): void {
// suspend() ENVIA um valor para quem chamou resume()
Fiber::suspend("Mensagem 1");
Fiber::suspend("Mensagem 2");
Fiber::suspend("Mensagem 3");
});
$fiber->start();
$fiber->resume();
echo "Recebi: Mensagem 1\n";
$fiber->resume();
echo "Recebi: Mensagem 2\n";
$fiber->resume();
echo "Recebi: Mensagem 3\n";
// Saída esperada
// Recebi: Mensagem 1
// Recebi: Mensagem 2
// Recebi: Mensagem 3
Comunicação Completa (Bidirecional)
<?php
$echo = new Fiber(function(): void {
for ($i = 0; $i < 2; $i++) {
// Recebe mensagem e ecoa de volta em maiúsculas
$message = Fiber::suspend("Pronto para receber");
$response = strtoupper($message);
Fiber::suspend($response);
}
});
$echo->start();
// Primeira mensagem
$echo->resume();
echo "Pronto para receber\n";
$echo->resume("hello");
echo "Resposta: HELLO\n";
// Segunda mensagem
$echo->resume();
echo "Pronto para receber\n";
$echo->resume("world");
echo "Resposta: WORLD\n";
// Saída esperada
// Pronto para receber
// Resposta: HELLO
// Pronto para receber
// Resposta: WORLD
Exemplo: Processador Simples
<?php
$processor = new Fiber(function(): void {
// Operação 1
$op = Fiber::suspend();
$a = Fiber::suspend();
$b = Fiber::suspend();
$result = match($op) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
'/' => $b != 0 ? $a / $b : 0,
default => 0
};
Fiber::suspend("Resultado: $result");
// Operação 2
$op = Fiber::suspend();
$a = Fiber::suspend();
$b = Fiber::suspend();
$result = match($op) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
'/' => $b != 0 ? $a / $b : 0,
default => 0
};
Fiber::suspend("Resultado: $result");
});
$processor->start();
// Operação 1: 10 + 5
$processor->resume('+');
$processor->resume(10);
$processor->resume(5);
$processor->resume();
echo "Resultado: 15\n";
// Operação 2: 20 / 4
$processor->resume('/');
$processor->resume(20);
$processor->resume(4);
$processor->resume();
echo "Resultado: 5\n";
// Saída esperada
// Resultado: 15
// Resultado: 5
⚠️ Tratamento de Exceções
Exceções Dentro da Fiber
<?php
$fiber = new Fiber(function() {
echo "Iniciando...\n";
throw new Exception("Erro dentro da Fiber!");
echo "Esta linha nunca executa\n";
});
try {
$fiber->start();
} catch (Exception $e) {
echo "Capturei: {$e->getMessage()}\n";
}
echo "Programa continua normalmente\n";
// Saída esperada
// Iniciando...
// Capturei: Erro dentro da Fiber!
// Programa continua normalmente
Lançando Exceções NA Fiber
O método throw() permite lançar uma exceção dentro de uma Fiber suspensa:
<?php
$fiber = new Fiber(function() {
echo "Passo 1\n";
try {
Fiber::suspend();
echo "Passo 2 (não executado)\n";
} catch (Exception $e) {
echo "Capturei dentro da Fiber: {$e->getMessage()}\n";
}
echo "Passo 3\n";
});
$fiber->start(); // Executa até suspend()
// Lança exceção DENTRO da Fiber
$fiber->throw(new Exception("Erro externo!"));
// Saída esperada
// Passo 1
// Capturei dentro da Fiber: Erro externo!
// Passo 3
Exemplo: Tratamento Robusto
<?php
class Task {
private Fiber $fiber;
private string $name;
public function __construct(string $name, callable $callback) {
$this->name = $name;
$this->fiber = new Fiber($callback);
}
public function run(): void {
try {
if (!$this->fiber->isStarted()) {
echo "[{$this->name}] Iniciando...\n";
$this->fiber->start();
} else if ($this->fiber->isSuspended()) {
echo "[{$this->name}] Retomando...\n";
$this->fiber->resume();
}
} catch (Throwable $e) {
echo "[{$this->name}] ERRO: {$e->getMessage()}\n";
}
if ($this->fiber->isTerminated()) {
echo "[{$this->name}] ✓ Concluída\n";
}
}
}
$task = new Task("MinhaTask", function() {
echo "Executando passo 1\n";
Fiber::suspend();
echo "Executando passo 2\n";
throw new RuntimeException("Algo deu errado!");
echo "Passo 3 (nunca executado)\n";
});
$task->run(); // Passo 1
$task->run(); // Passo 2 + erro
// Saída esperada
// [MinhaTask] Iniciando...
// Executando passo 1
// [MinhaTask] Retomando...
// Executando passo 2
// [MinhaTask] ERRO: Algo deu errado!
// [MinhaTask] ✓ Concluída
🎯 Construindo um Agendador
Para gerenciar múltiplas Fibers de forma eficiente, precisamos de um Scheduler (agendador):
Agendador Básico
<?php
class BasicScheduler {
private array $fibers = [];
public function add(Fiber $fiber): void {
$this->fibers[] = $fiber;
}
public function run(): void {
// Inicia todas as Fibers
foreach ($this->fibers as $fiber) {
if (!$fiber->isStarted()) {
$fiber->start();
}
}
// Loop principal: retoma Fibers suspensas
while ($this->hasActiveFibers()) {
foreach ($this->fibers as $fiber) {
if ($fiber->isSuspended()) {
$fiber->resume();
}
}
}
}
private function hasActiveFibers(): bool {
foreach ($this->fibers as $fiber) {
if (!$fiber->isTerminated()) {
return true;
}
}
return false;
}
}
// Exemplo de uso
$scheduler = new BasicScheduler();
$scheduler->add(new Fiber(function() {
for ($i = 1; $i <= 3; $i++) {
echo "Task A - $i\n";
Fiber::suspend();
}
}));
$scheduler->add(new Fiber(function() {
for ($i = 1; $i <= 3; $i++) {
echo "Task B - $i\n";
Fiber::suspend();
}
}));
$scheduler->run();
// Saída esperada
// Task A - 1
// Task B - 1
// Task A - 2
// Task B - 2
// Task A - 3
// Task B - 3
Agendador com Sleep Assíncrono
<?php
class AsyncScheduler {
private array $fibers = [];
private array $sleeping = []; // ['fiber' => Fiber, 'wakeTime' => float]
public function spawn(callable $callback): void {
$this->fibers[] = new Fiber($callback);
}
public function sleep(float $seconds): void {
$fiber = Fiber::getCurrent();
if ($fiber === null) {
throw new RuntimeException("sleep() deve ser chamado dentro de uma Fiber");
}
$this->sleeping[] = [
'fiber' => $fiber,
'wakeTime' => microtime(true) + $seconds
];
Fiber::suspend();
}
public function run(): void {
// Inicia todas
foreach ($this->fibers as $fiber) {
$fiber->start();
}
// Loop principal
while ($this->hasActiveFibers()) {
$this->wakeUpFibers();
foreach ($this->fibers as $fiber) {
if ($fiber->isSuspended() && !$this->isSleeping($fiber)) {
$fiber->resume();
}
}
usleep(1000); // 1ms - evita 100% CPU
}
}
private function wakeUpFibers(): void {
$now = microtime(true);
foreach ($this->sleeping as $i => $sleep) {
if ($now >= $sleep['wakeTime']) {
$sleep['fiber']->resume();
unset($this->sleeping[$i]);
}
}
$this->sleeping = array_values($this->sleeping);
}
private function isSleeping(Fiber $fiber): bool {
foreach ($this->sleeping as $sleep) {
if ($sleep['fiber'] === $fiber) {
return true;
}
}
return false;
}
private function hasActiveFibers(): bool {
foreach ($this->fibers as $fiber) {
if (!$fiber->isTerminated()) {
return true;
}
}
return count($this->sleeping) > 0;
}
}
// Exemplo de uso
$scheduler = new AsyncScheduler();
$scheduler->spawn(function() use ($scheduler) {
echo "[Task 1] Início\n";
$scheduler->sleep(1);
echo "[Task 1] Após 1s\n";
$scheduler->sleep(0.5);
echo "[Task 1] Fim\n";
});
$scheduler->spawn(function() use ($scheduler) {
echo "[Task 2] Início\n";
$scheduler->sleep(0.5);
echo "[Task 2] Após 0.5s\n";
$scheduler->sleep(1);
echo "[Task 2] Fim\n";
});
$start = microtime(true);
$scheduler->run();
$duration = microtime(true) - $start;
echo "\nConcluído em " . round($duration, 2) . "s\n";
// Saída esperada (horários aproximados)
// [Task 1] Início
// [Task 2] Início
// [Task 2] Após 0.5s
// [Task 1] Após 1s
// [Task 1] Fim
// [Task 2] Fim
//
// Concluído em 1.5s
Observe: As tarefas executam concorrentemente! Total de 1.5s ao invés de 3s.
💡 Exemplos Práticos
1. Gerador de Fibonacci
<?php
function fibonacci(): Fiber {
return new Fiber(function(): void {
$a = 0;
$b = 1;
Fiber::suspend($a);
Fiber::suspend($b);
for ($i = 0; $i < 8; $i++) {
$next = $a + $b;
$a = $b;
$b = $next;
Fiber::suspend($next);
}
});
}
$fib = fibonacci();
$fib->start();
echo "Fibonacci: ";
$fib->resume();
echo "0 ";
while (!$fib->isTerminated()) {
$value = $fib->resume();
if ($value !== null) {
echo "$value ";
}
}
echo "\n";
//Saída esperada
// Fibonacci: 0 1 1 2 3 5 8 13 21 34
2. Processador de Tarefas em Lote
<?php
function batchProcessor(array $items, int $batchSize): Fiber {
return new Fiber(function() use ($items, $batchSize): void {
$batches = array_chunk($items, $batchSize);
foreach ($batches as $i => $batch) {
echo "Processando lote " . ($i + 1) . " de " . count($batches) . "\n";
foreach ($batch as $item) {
echo " - Processando: $item\n";
}
// Pausa após cada lote
if ($i < count($batches) - 1) {
Fiber::suspend("Lote " . ($i + 1) . " completo");
}
}
});
}
$items = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
$processor = batchProcessor($items, 3);
$processor->start();
while (!$processor->isTerminated()) {
class HttpSimulator {
private AsyncScheduler $scheduler;
public function __construct(AsyncScheduler $scheduler) {
$this->scheduler = $scheduler;
}
public function fetch(string $url): array {
echo " → GET $url\n";
// Simula latência de rede (100-300ms)
$latency = rand(100, 300) / 1000;
$this->scheduler->sleep($latency);
echo " ← 200 OK $url\n";
return [
'url' => $url,
'status' => 200,
'body' => "Conteúdo de $url"
];
}
}
$scheduler = new AsyncScheduler();
$http = new HttpSimulator($scheduler);
$urls = [
'https://api.exemplo.com/users',
'https://api.exemplo.com/posts',
'https://api.exemplo.com/comments',
];
$results = [];
foreach ($urls as $url) {
$scheduler->spawn(function() use ($http, $url, &$results) {
$response = $http->fetch($url);
$results[] = $response;
echo "[✓] Concluído: {$response['url']}\n";
});
}
echo "Buscando " . count($urls) . " URLs...\n";
$start = microtime(true);
$scheduler->run();
$duration = microtime(true) - $start;
echo "\n✅ " . count($results) . " requisições em " . round($duration, 2) . "s\n";
// Saída esperada (ordem pode variar)
// Buscando 3 URLs...
// → GET https://api.exemplo.com/users
// → GET https://api.exemplo.com/posts
// → GET https://api.exemplo.com/comments
// ← 200 OK https://api.exemplo.com/users
// [✓] Concluído: https://api.exemplo.com/users
// ← 200 OK https://api.exemplo.com/posts
// [✓] Concluído: https://api.exemplo.com/posts
// ← 200 OK https://api.exemplo.com/comments
// [✓] Concluído: https://api.exemplo.com/comments
//
// ✅ 3 requisições em 0.3s
✅ Boas Práticas
1. Sempre Verifique o Estado
<?php
$fiber = new Fiber(function() {
Fiber::suspend();
});
$fiber->start();
// ✅ BOM: Verificar antes de resumir
if ($fiber->isSuspended()) {
$fiber->resume();
}
// ❌ RUIM: Pode causar FiberError
// $fiber->resume(); // Erro se já terminou!
2. Use Type Hints
<?php
// ✅ BOM: Type hints claros
function createCounter(): Fiber {
return new Fiber(function(): Generator {
$count = 0;
while (true) {
yield Fiber::suspend(++$count);
}
});
}
3. Documente Pontos de Suspensão
<?php
/**
* Busca dados do usuário
*
* @param int $userId
* @return array
*
* @suspends Suspende durante a requisição HTTP
*/
function fetchUser(int $userId): array {
Fiber::suspend(); // Ponto de suspensão documentado
return ['id' => $userId, 'name' => 'User'];
}
4. Evite Estado Compartilhado
<?php
// ❌ RUIM: Estado compartilhado
$shared = 0;
$fiber1 = new Fiber(function() use (&$shared) {
$shared++; // Pode dar problema!
});
// ✅ BOM: Comunicação explícita
$fiber2 = new Fiber(function(): int {
$count = 0;
while (true) {
$count++;
Fiber::suspend($count);
}
});
5. Trate Exceções
<?php
function safeRun(Fiber $fiber): void {
try {
if (!$fiber->isStarted()) {
$fiber->start();
} else if ($fiber->isSuspended()) {
$fiber->resume();
}
} catch (Throwable $e) {
echo "Erro: {$e->getMessage()}\n";
}
}
⚠️ Limitações e Quando Usar
Fibers NÃO São Threads
<?php
// ❌ Fibers NÃO executam em paralelo real
// Apenas intercalam execução em um único thread
$fiber1 = new Fiber(function() {
// Cálculo pesado - NÃO se beneficia de Fibers
for ($i = 0; $i < 10000000; $i++) {
$x = sqrt($i);
}
});
Use Fibers Para:
✅ Operações I/O-bound (rede, disco, banco de dados) ✅ Implementar event loops ✅ Múltiplas requisições HTTP simultâneas ✅ Geradores complexos com estado ✅ Concorrência cooperativa
NÃO Use Fibers Para:
❌ Tarefas CPU-bound (cálculos matemáticos pesados) ❌ Código síncrono simples que já funciona bem ❌ Quando paralelismo real é necessário (use processos/threads)
Comparação: Síncrono vs Fibers
<?php
// ❌ Síncrono - 3 segundos
function syncTasks() {
sleep(1); // Task 1
sleep(1); // Task 2
sleep(1); // Task 3
// Total: 3s
}
// ✅ Com Fibers - 1 segundo (concorrente)
function asyncTasks() {
$scheduler = new AsyncScheduler();
$scheduler->spawn(fn() => $scheduler->sleep(1)); // Task 1
$scheduler->spawn(fn() => $scheduler->sleep(1)); // Task 2
$scheduler->spawn(fn() => $scheduler->sleep(1)); // Task 3
$scheduler->run();
// Total: ~1s (executam "juntas")
}
🎓 Resumo
Conceitos-Chave
- Fiber = Unidade de execução que pode ser pausada e retomada
- suspend() = Pausa a Fiber atual
- resume() = Retoma uma Fiber suspensa
- Comunicação bidirecional = Enviar e receber valores
- Concorrência cooperativa = Múltiplas tarefas progredindo alternadamente
Fluxo Típico
<?php
// 1. Criar
$fiber = new Fiber(function() {
// 3. Executar até suspend()
Fiber::suspend();
// 5. Continuar até o fim
});
// 2. Iniciar
$fiber->start();
// 4. Retomar
$fiber->resume();
API Essencial
<?php
new Fiber(callable $callback)
$fiber->start(mixed ...$args): mixed
$fiber->resume(mixed $value = null): mixed
$fiber->throw(Throwable $exception): mixed
Fiber::getCurrent(): ?Fiber
Fiber::suspend(mixed $value = null): mixed
$fiber->isStarted(): bool
$fiber->isSuspended(): bool
$fiber->isRunning(): bool
$fiber->isTerminated(): bool
🔗 Recursos Relacionados
- Coroutines em PHP - Conceito mais amplo usando Generators e Fibers
- RFC: Fibers - Proposta oficial
- PHP Manual: Fiber - Documentação oficial
Fibers são a base para programação assíncrona moderna em PHP! 🚀