Pur potendo contare su progetti come ReactPHP e Guzzle, fino ad ora PHP si è evoluto nativamente come un linguaggio basato su codice sincrono, questo significa ad esempio che l'esecuzione di una funzione viene interrotta fino alla restituzione di un risultato.
Tale dinamica può essere spiegata a larghe linee tramite il problema chiamato "Di che colore è la tua funzione?". Se infatti assumessimo che ogni funzione debba essere associata ad un colore e che il modo in cui avviene la chiamata ad una funzione dipenda da questo dato, avremmo l'unica possibilità di chiamare una funzione di un determinato colore da un'altra funzione dello stesso colore.
Funzioni sincrone e asincrone
Il meccanismo descritto non è forse intuitivo ma fondamentalmente ciò avviene perché le funzioni sincrone restituiscono valori mentre quelle asincrone invocano callback, nello stesso modo le funzioni sincrone possono offrire il loro risultato sotto forma di valore di ritorno mentre il risultato delle asincrone dipende sempre dal callback. Rispetto ad una sincrona una funzione asincrona modifica inoltre il modo in cui una funzione deve essere chiamata.
Ora abbiamo che le funzioni sincrone non possono chiamare le asincrone mentre può avvenire il contrario, ma per la chiamata ad una funzione asincrona è necessario che l'intero stack di chiamata sia asincrono.
Per questa ragione, utilizzando un linguaggio che la supporta, se una funzione restituisce una promise in uno stack di chiamata il risultato non potrà essere noto fino alla risoluzione della promise stessa, per far questo però l'intero stack deve poter restituire una promise.
Fiber e funzioni asincrone
Per rimediare al limite dovuto alla mancanza di codice asincrono, PHP 8.1 introduce il concetto di Fiber a cui fanno riferimento una classe omonima, una di riflessione (ReflectionFiber
) e due classi per le eccezioni, FiberError
e FiberExit
.
Le Fiber sono state implementate per rimuovere la differenza tra codice sincrono e asincrono consentendo di interrompere le funzioni senza che ciò coinvolga il call stack nel suo insieme. In pratica le Fiber mettono in pausa lo stack di esecuzione, quindi per le chiamate dirette delle funzioni non si deve modificare il modo in cui esse sono invocate. Di seguito l'esempio riportato nella RFC di PHP 8.1.
final class Fiber
{
public function __construct(callable $callback) {}
public function start(mixed ...$args): mixed {}
public function resume(mixed $value = null): mixed {}
public function throw(Throwable $exception): mixed {}
public function isStarted(): bool {}
public function isSuspended(): bool {}
public function isRunning(): bool {}
public function isTerminated(): bool {}
public function getReturn(): mixed {}
public static function this(): ?self {}
public static function suspend(mixed $value = null): mixed {}
}
new Fiber
genera un oggetto Fiber
mentre Fiber::suspend()
sospende l'esecuzione della Fiber corrente e riprende l'esecuzione tramite la chiamata a Fiber->start()
, Fiber->resume()
o Fiber->throw()
.
Fiber->resume()
può riattivare una Fiber restituendo un valore da Fiber::suspend()
mentre Fiber->throw()
può fare lo stesso se viene intercettata un'eccezione.
Fiber->getReturn()
restituisce un valore di ritorno dalla conclusione della Fiber mentre Fiber::this()
fornirà l'istanza della in esecuzione o NULL
se chiamato da {main}
.