Introdotti nella versione 5.5 di PHP, i generators sono uno strumento eccezionale per quanto riguarda la velocità di esecuzione del nostro progetto. In sostanza, essi sono funzioni che permettono di creare un elemento che è possibile poi iterare tramite foreach , proprio come se fosse un array. Perché farlo? Creare un array di grandi dimensioni potrebbe impegnare molte risorse in esecuzione, sia appunto per la sua creazione, sia per il suo peso in memoria una volta creato. Con un generator non ce n’è bisogno: creeremo una funzione che è possibile iterare direttamente senza creare alcun array.
Da qui il nostro discorso sulla velocità di esecuzione: non dovendo più creare e conservare in memoria l’intero array, i nostri script saranno più performanti.

Partiamo subito con un esempio:

<?php

$array = [];

for ($i = 0; $i < 9999999999; $i++) {
    $array[] = $i;
}

Questo script genera un array da 9 miliardi di elementi. Se proviamo ad eseguirlo, riceveremo l’errore Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 134217736 bytes). Abbiamo naturalmente creato un caso estremo di proposito per dimostrare che la memoria è qualcosa di finito e da buoni sviluppatori dobbiamo usare meno risorse possibili.

Creiamo quindi una funzione generator:

<?php

// questa è un generator!
function getData() {
    for ($i = 0; $i < 999999; $i++) {
        yield $i;
    }
}

foreach(getData() as $data) {
    echo $data;
}

 

Una funzione generator altro non è che una normale funzione che utilizza la parola yield , anziché return . Vediamo cosa succede:

1) definiamo la funzione, con il nome che vogliamo
2) il ciclo foreach  itera la funzione stessa, ogni volta che viene invocata prende uno dei valori che “trattiene” (che è il significato proprio di yield ) e lo stampa a schermo

La parola yield

La keyword yield  è il cuore delle funzioni generator. Nella sua forma più semplice, assomiglia molto a return , anche se non si comporta allo stesso modo. Yield, infatti, non ferma l’esecuzione come return , ma conserva il valore e lo passa alla ciclo. Una volta che la funzione generator non genera più valori, il ciclo esce, proprio come se gli elementi di un array fossero finiti.

Sapete quanta memoria utilizza questa funzione? Zero. Questo perché non conserviamo mai il valore contenuto nella variabile $i  di getData() .

Una generator che restituisce chiave/valore

Nell’esempio precedente abbiamo costruito una funzione generator che restituisce dei semplici valori interi (nella variabile $i , per capirci). Ma le funzioni generator gestiscono tranquillamente anche le coppie chiavi/valore, proprio come gli array associativi.

La sintassi è molto simile a quella vista in precedenza:

<?php

function getDataPair() {
    for ($i = 0; $i < 10; $i++) {
        $value = rand(1,99);
        yield $i => $value;
    }
}

foreach(getData() as $data) {
    echo $data;
}

In questo caso getDataPair()  restituirà 10 valori (notare $i ) come una coppia di valori, con l’indice che sarà appunto il contenuti di $i  ed il valore associato un valore casuale generator da rand(1,99) . Avremo quindi un’output simile a questo:

[0, 72]
[1, 55]
[2, 4]
...
[9, 11]

Parametri

Come già accennato più volte, le funzioni generator sono delle normali funzioni che svolgono però un compito speciale. Al pari delle funzioni “normali”, possiamo passargli dei parametri:

<?php

function getDataConParametri(int $offset) {
    for ($i = 0; $i < 10; $i++) {
        yield ($offset + $i);
    }
}

foreach(getDataConParametri(9) as $data) {
    echo $data;
}

Ad esempio getDataConParametri()  accetta un parametro di tipo integer che funzionerà da offset, per cui tutti i valori restituiti partiranno da quell’offset. Abbiamo passato 9  come parametro, per cui l’output sarà questo:

[9, 14]
[10, 91]
[11, 23]
..
[18, 43]