La gestione della concorrenza in PHP è sempre stata un terreno accidentato. Per decenni, ogni richiesta HTTP ha significato un processo singolo, e operazioni I/O lente (chiamate API, query di database, letture di file) bloccavano l'intero script. Le soluzioni tradizionali — processi figlio con pcntl_fork, code di job (Beanstalkd, RabbitMQ), o estensioni C come event — hanno introdotto complessità e overhead. Con PHP 8.1, i Fibers hanno cambiato il paradigma. Offrono un modello di concorrenza cooperativa che permette di sospendere e riprendere funzioni arbitrariamente, rendendo la programmazione asincrona finalmente accessibile a ogni sviluppatore PHP. Questa guida esplora cosa sono, come si usano e perché possono sostituire processi e code in molti scenari reali.
Cosa Sono i Fibers in PHP 8.1
Un Fiber è un'unità di esecuzione leggera che può essere sospesa dal suo interno e ripresa dall'esterno. A differenza dei thread, i Fiber non eseguono codice in parallelo: il contesto rimane singolo, ma la CPU viene volontariamente ceduta (yield) per permettere ad altri Fiber di avanzare. Questo è chiamato multitasking cooperativo.
Meccanismo di Sospensione e Ripresa
La classe Fiber espone due metodi fondamentali: suspend() (statico) e resume(). All'interno di un Fiber, si chiama Fiber::suspend($value) per mettere in pausa l'esecuzione e passare un valore al chiamante. Il chiamante (solitamente un event loop) riceve quel valore e può decidere quando riprendere il Fiber con $fiber->resume($anotherValue).
$fiber = new Fiber(function (): void {
$first = Fiber::suspend('Hello');
$second = Fiber::suspend('World');
echo $first . ' ' . $second;
});
$result1 = $fiber->start(); // 'Hello'
$result2 = $fiber->resume('Ciao'); // restituisce 'World' internamente, ma echo 'Ciao World'
Questo meccanismo permette di scrivere codice che sembra sincrono ma si comporta in modo asincrono, eliminando la necessità di callback espliciti o catene di promise.
Fibers vs Promises: Un Confronto Pratico
Le Promises (come quelle di ReactPHP, Amp o Guzzle) hanno dominato l'asincronia PHP per anni. Richiedono un event loop esplicito e trasformano il flusso logico in catene di then(). I Fiber, invece, mantengono la struttura lineare del codice.
Esempio: Due Richieste HTTP in Parallelo
Immaginiamo di dover recuperare due URL esterni. Con Promise ReactPHP:
use React\Http\Browser;
use React\EventLoop\Loop;
$loop = Loop::get();
$client = new Browser($loop);
$promise1 = $client->get('https://api.example.com/data1');
$promise2 = $client->get('https://api.example.com/data2');
$both = \React\Promise\all([$promise1, $promise2]);
$both->then(function (array $responses) {
// elabora
});
$loop->run();
Con i Fiber, possiamo usare una libreria che integra Fiber e un event loop (ad esempio Amp v3) per scrivere:
use function Amp\delay;
use function Amp\async;
use function Amp\Http\Client\request;
$response1 = request('https://api.example.com/data1');
$response2 = request('https://api.example.com/data2');
$body1 = $response1->getBody()->buffer();
$body2 = $response2->getBody()->buffer();
// Il codice sembra sincrono ma esegue le richieste in parallelo
La sintassi con Fiber è più vicina al PHP tradizionale, riducendo la curva di apprendimento.
Implementazione Pratica: Un Client HTTP Asincrono con Fiber
Per capire il potenziale, costruiamo un piccolo esempio che esegue tre richieste HTTP in parallelo usando solo i Fiber e una funzione di non-blocking I/O simulata. Nota: in produzione si usano librerie come amphp/http-client o react/http.
// Simuliamo una chiamata asincrona con sleep (non bloccante? No, ma per esempio)
function fetchUrl(string $url, float $delay): string {
// In un ambiente reale, qui si userebbe socket non bloccante e select()
usleep((int)($delay * 1_000_000));
return "Risposta da $url\n";
}
$fiber1 = new Fiber(function () {
return fetchUrl('https://api1.example.com', 0.2);
});
$fiber2 = new Fiber(function () {
return fetchUrl('https://api2.example.com', 0.3);
});
$fiber3 = new Fiber(function () {
return fetchUrl('https://api3.example.com', 0.1);
});
$fiber1->start();
$fiber2->start();
$fiber3->start();
// Simuliamo un event loop rudimentale
$running = true;
while ($running) {
$running = false;
foreach ([$fiber1, $fiber2, $fiber3] as $fiber) {
if (!$fiber->isTerminated()) {
$fiber->resume();
$running = true;
}
}
}
echo $fiber1->getReturn();
echo $fiber2->getReturn();
echo $fiber3->getReturn();
Nella realtà, un vero event loop gestisce lo scheduling e le risorse di rete. Ma il principio è chiaro: i Fiber permettono di scrivere codice parallelo lineare.
I Fiber Come Alternativa a Processi e Code
Spesso, per eseguire operazioni concorrenti in PHP, si ricorre a pcntl_fork (per parallelismo vero su CPU multi-core) o a sistemi di code (Redis, RabbitMQ). I Fiber non forniscono parallelismo su CPU (rimangono single-thread), ma eccellono per operazioni I/O-bound: chiamate HTTP, letture di file, query di database lente.
Vantaggi rispetto a Processi Separati
- Nessun overhead di creazione di processo: i Fiber condividono la stessa memoria del processo padre.
- Comunicazione immediata: variabili condivise senza pipe o messaggi serializzati.
- Gestione errori più semplice: le eccezioni si propagano naturalmente.
Vantaggi rispetto alle Code
- Latenza ridotta: nessun passaggio attraverso un broker esterno.
- Sincronizzazione diretta: il risultato di un Fiber può essere atteso nello stesso script.
- Codice più compatto: non serve configurare worker, code, e gestire fallimenti.
Ciò non significa che processi e code siano obsoleti. Per workload CPU-intensive (elaborazione immagini, calcoli lunghi) o per task che devono sopravvivere a un restart del server, processi e code restano la scelta migliore. I Fiber sono un'arma in più per la concorrenza I/O.
Best Practices e Trappole Comuni
Usare i Fiber richiede attenzione:
- Non bloccare mai un Fiber con
sleep()o operazioni I/O sincrone: il fiber si blocca e blocca l'intero event loop. Sostituire con versioni asincrone (es.usleepnon è asincrono, va evitato). - Gestire le eccezioni: un Fiber non gestito che lancia un'eccezione la propaga al chiamante di
resume(). Usaretry/catchattorno aresume(). - Limite di concorrenza: evitare di creare migliaia di Fiber contemporaneamente. Ogni Fiber occupa stack (default 8KB). Per molteplici connessioni simultanee, usare un pool o librerie come amphp che gestiscono il numero di Fiber attivi.
- Non abusare dei Fiber: se il lavoro è CPU-bound, i Fiber non danno beneficio. Usare processi o thread (con
pthreadso estensioni appropriate).
Conclusione e Prossimi Passi
I Fiber di PHP 8.1 rappresentano un salto qualitativo per la programmazione asincrona in PHP. Permettono di scrivere codice concorrente leggibile, senza il peso cognitivo delle promise o la complessità dei processi. Abbiamo visto come sospendere e riprendere l'esecuzione, confrontato i Fiber con le promesse tradizionali, e analizzato scenari in cui sostituiscono code e processi. Per approfondire, consulta la documentazione ufficiale PHP sui Fiber e dai un’occhiata al framework Amp v3, che integra nativamente i Fiber. Se vuoi esplorare altri aspetti del PHP moderno, leggi la guida PHP 8 Moderno o confronta con l'uso di Readonly Properties ed Enums.
Best Practice finale: inizia sostituendo un semplice script che esegue più richieste HTTP sincrone con un approccio a Fiber. Misura i tempi di risposta e osserva il miglioramento immediato. Poi, gradualmente, introduci i Fiber nei flussi di lavoro di import/export e nelle API che devono aggregare dati da più fonti.
Sponsored Protocol