W3docs

yield

La parola chiave yield in PHP crea una funzione generatore che produce valori uno alla volta, mettendo in pausa e riprendendo l'esecuzione.

Introduzione

La parola chiave yield trasforma una normale funzione PHP in un generatore — una funzione che produce una sequenza di valori uno alla volta, mettendo in pausa dopo ogni valore e riprendendo da dove si era fermata quando viene richiesto il valore successivo. Questa pagina spiega come funziona yield, come restituire chiavi, come inviare valori a un generatore, la sintassi di delega yield from, e quando i generatori consentono di risparmiare memoria rispetto alla costruzione di un array completo.

Una funzione diventa un generatore nel momento in cui contiene almeno un yield. Chiamarla non esegue il corpo — restituisce un oggetto Generator. Il corpo viene eseguito solo durante l'iterazione, avanzando al yield successivo ad ogni passo. Poiché un generatore implementa l'interfaccia Iterator, di solito lo si consuma con un ciclo foreach.

Un primo generatore

Ogni yield consegna un valore al chiamante e congela la funzione fino a quando non viene richiesto il valore successivo.

php— editable, runs on the server

myGenerator() restituisce un oggetto generatore invece di eseguire immediatamente. Il ciclo foreach estrae i valori da esso: ad ogni passaggio la funzione viene eseguita fino al prossimo yield, emette quel valore e si mette in pausa. L'output è:

Hello World !

A differenza di una funzione che costruisce e restituisce un array con return, le tre stringhe non vengono mai mantenute in memoria contemporaneamente — ognuna viene prodotta su richiesta. Confronta questo con il modo in cui una funzione normale usa return per restituire un singolo valore e terminare.

Restituire chiavi con yield

Un generatore può restituire coppie chiave/valore con la sintassi yield $key => $value, esattamente come un array associativo. Le chiavi sono disponibili all'interno del foreach:

<?php

function settings()
{
  yield "host" => "localhost";
  yield "port" => 5432;
  yield "user" => "admin";
}

foreach (settings() as $key => $value) {
  echo "$key = $value\n";
}

Output:

host = localhost
port = 5432
user = admin

Se non si forniscono chiavi, PHP numera automaticamente i valori restituiti a partire da 0, proprio come un array indicizzato.

I generatori sono lazy: un range efficiente in termini di memoria

Il vero vantaggio di yield è che i valori vengono calcolati solo quando necessario. Un generatore che produce un milione di numeri utilizza una quantità costante di memoria, mentre un array di un milione di numeri li alloca tutti in anticipo.

<?php

function gen_range($start, $end)
{
  for ($i = $start; $i <= $end; $i++) {
    yield $i;
  }
}

$sum = 0;
foreach (gen_range(1, 1000000) as $n) {
  $sum += $n;
}

echo $sum;

Output:

500000500000

Il ciclo non costruisce mai un array [1, 2, …, 1000000]; esiste un solo intero alla volta. Questo è il pattern da utilizzare quando si leggono file di grandi dimensioni riga per riga o si trasmettono righe da un database in streaming.

Restituire un valore da un generatore

Un generatore può anche usare return per esporre un valore finale (PHP 7+). Il valore di ritorno non fa parte dell'iterazione — lo si legge in seguito con getReturn():

<?php

function counter()
{
  yield 1;
  yield 2;
  return "done";
}

$gen = counter();
foreach ($gen as $value) {
  echo $value . " ";
}
echo $gen->getReturn();

Output:

1 2 done

Chiamare getReturn() prima che il generatore abbia terminato l'iterazione genera un'eccezione, quindi è necessario esaurirlo prima.

Inviare valori con send()

I generatori sono bidirezionali: un'espressione yield può anche ricevere un valore inviato dal chiamante tramite send(). Questa è la base delle coroutine.

<?php

function echoTimes()
{
  while (true) {
    $received = yield;
    echo "Got: $received\n";
  }
}

$gen = echoTimes();
$gen->current();      // prime the generator (run up to the first yield)
$gen->send("a");
$gen->send("b");

Output:

Got: a
Got: b

send($value) riprende il generatore, facendo sì che l'espressione yield in pausa si valuti a $value, poi viene eseguita fino al prossimo yield.

Delega con yield from

yield from (PHP 7+) restituisce ogni valore da un altro generatore, array o Traversable, appiattendolo nel generatore corrente senza un ciclo manuale:

<?php

function inner()
{
  yield 2;
  yield 3;
}

function outer()
{
  yield 1;
  yield from inner();
  yield from [4, 5];
}

foreach (outer() as $value) {
  echo $value . " ";
}

Output:

1 2 3 4 5 

Quando usare un generatore

  • Sequenze grandi o illimitate — lettura di un file multi-gigabyte o di uno stream infinito in cui è impossibile mantenere tutto in un array.
  • Valori costosi che potrebbero non essere necessari — se il consumatore potrebbe fermarsi prima (break), un generatore evita di calcolare il resto.
  • Codice di iterazione più pulito — sostituire una classe Iterator personalizzata con una singola funzione.

Evita i generatori quando hai bisogno di accesso casuale ($arr[42]), di contare i risultati in modo economico con count(), o di iterare gli stessi dati più volte — un generatore può essere attraversato solo una volta. In questi casi, costruisci un array normale, eventualmente con funzioni PHP come array_map.

Esercitazione

Pratica
Qual è la funzione della parola chiave 'yield' in PHP?
Qual è la funzione della parola chiave 'yield' in PHP?
Was this page helpful?