yield
A palavra-chave yield em PHP cria uma função geradora que produz valores um a um, pausando e retomando a execução conforme necessário.
Introdução
A palavra-chave yield transforma uma função PHP comum em um gerador — uma função que produz uma sequência de valores um de cada vez, pausando após cada valor e retomando de onde parou quando o próximo valor é solicitado. Esta página aborda como o yield funciona, como produzir chaves, como enviar valores de volta a um gerador, a sintaxe de delegação yield from e quando os geradores economizam memória em comparação com a construção de um array completo.
Uma função se torna um gerador no momento em que contém pelo menos um yield. Chamá-la não executa o corpo — ela retorna um objeto Generator. O corpo só é executado à medida que você itera, avançando para o próximo yield a cada passo. Como um gerador implementa a interface Iterator, você normalmente o consome com um loop foreach.
Um primeiro gerador
Cada yield entrega um valor ao chamador e congela a função até que o próximo valor seja solicitado.
myGenerator() retorna um objeto gerador em vez de executar imediatamente. O loop foreach extrai os valores dele: a cada passagem, a função é executada até o próximo yield, emite esse valor e pausa. A saída é:
Hello World !Ao contrário de uma função que constrói e retorna um array com return, as três strings nunca são mantidas na memória ao mesmo tempo — cada uma é produzida sob demanda. Compare isso com como uma função normal usa return para enviar de volta um único valor e sair.
Produzindo chaves
Um gerador pode produzir pares chave/valor com a sintaxe yield $key => $value, exatamente como um array associativo. As chaves estão disponíveis dentro do foreach:
<?php
function settings()
{
yield "host" => "localhost";
yield "port" => 5432;
yield "user" => "admin";
}
foreach (settings() as $key => $value) {
echo "$key = $value\n";
}Saída:
host = localhost
port = 5432
user = adminSe você não fornecer chaves, o PHP numera automaticamente os valores produzidos a partir de 0, assim como um array indexado.
Geradores são preguiçosos: um intervalo econômico em memória
O verdadeiro benefício do yield é que os valores são calculados apenas quando necessário. Um gerador que produz um milhão de números usa memória constante, enquanto um array de um milhão de números aloca todos eles antecipadamente.
<?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;Saída:
500000500000O loop nunca constrói um array [1, 2, …, 1000000]; apenas um inteiro existe por vez. Este é o padrão a ser usado ao ler arquivos grandes linha por linha ou transmitir linhas de banco de dados.
Retornando um valor de um gerador
Um gerador também pode usar return para expor um valor final (PHP 7+). O valor de retorno não faz parte da iteração — você o lê depois com getReturn():
<?php
function counter()
{
yield 1;
yield 2;
return "done";
}
$gen = counter();
foreach ($gen as $value) {
echo $value . " ";
}
echo $gen->getReturn();Saída:
1 2 doneChamar getReturn() antes de o gerador ter terminado de iterar lança uma exceção, portanto, sempre esgote-o primeiro.
Enviando valores com send()
Os geradores são bidirecionais: uma expressão yield também pode receber um valor enviado pelo chamador via send(). Esta é a base das corrotinas.
<?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");Saída:
Got: a
Got: bsend($value) retoma o gerador, fazendo a expressão yield pausada avaliar para $value, e então executa até o próximo yield.
Delegando com yield from
yield from (PHP 7+) produz todos os valores de outro gerador, array ou Traversable, achatando-o no atual sem um loop manual:
<?php
function inner()
{
yield 2;
yield 3;
}
function outer()
{
yield 1;
yield from inner();
yield from [4, 5];
}
foreach (outer() as $value) {
echo $value . " ";
}Saída:
1 2 3 4 5 Quando usar um gerador
- Sequências grandes ou ilimitadas — lendo um arquivo de vários gigabytes ou um fluxo infinito onde manter tudo em um array é impossível.
- Valores caros que você pode não precisar — se o consumidor parar cedo (
break), um gerador evita calcular o restante. - Código de iteração mais limpo — substituindo uma classe
Iteratorpersonalizada por uma única função.
Evite geradores quando precisar de acesso aleatório ($arr[42]), para count() resultados de forma econômica, ou para iterar os mesmos dados várias vezes — um gerador só pode ser percorrido uma vez. Para esses casos, construa um array normal, possivelmente com funções PHP como array_map.