ZetCode

PHP yield 关键字

最后修改于 2025 年 4 月 16 日

PHP 的 yield 关键字创建生成器函数,这些函数可以暂停执行并一次返回一个值。生成器提供内存高效的迭代大型数据集,而无需构建数组。它们在处理数据序列和流时非常强大。

基本定义

生成器函数是一种特殊的函数,它使用 yield 而不是 return。调用时,它会返回一个可以迭代的 Generator 对象。每次迭代都会从上一个 yield 恢复执行。

yield 关键字既为迭代器提供了值,又暂停了函数执行。当请求下一个值时,执行会恢复。这使得能够内存高效地处理大型数据集。

生成器实现了 Iterator 接口,因此它们可以与 foreach 循环一起使用。它们在 yield 之间维护其状态,包括局部变量的值。生成器可以在迭代期间同时 yield 和接收值。

基本生成器函数

此示例演示了一个简单的生成器函数,该函数 yield 三个值。

basic_generator.php
<?php

declare(strict_types=1);

function numberGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

foreach (numberGenerator() as $number) {
    echo $number . "\n";
}

numberGenerator 函数按顺序 yield 三个数字。调用时,它会返回一个可以迭代的 Generator 对象。每次 yield 都会暂停执行,直到下一个迭代。foreach 循环一次消耗一个值。

生成范围

此示例显示了如何创建内存高效的范围生成器。

range_generator.php
<?php

declare(strict_types=1);

function xrange($start, $limit, $step = 1) {
    for ($i = $start; $i <= $limit; $i += $step) {
        yield $i;
    }
}

foreach (xrange(1, 1000000) as $number) {
    echo $number . "\n";
}

xrange 生成器在不创建数组的情况下生成从 start 到 limit 的数字。无论范围大小如何,它都使用恒定的内存。这与 range() 不同,后者会在内存中构建整个数组。生成器在大型序列方面表现出色。

Yield 键值对

此示例演示了从生成器 yield 关联键值对。

key_value_generator.php
<?php

declare(strict_types=1);

function userGenerator() {
    yield 'id' => 1;
    yield 'name' => 'John Doe';
    yield 'email' => 'john@example.com';
}

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

生成器 yield 键值对,这些键值对可以在 foreach 循环中消耗。此语法类似于数组迭代。每次 yield 都同时提供键和值。生成器可以在不存储所有元素的情况下模拟关联数组。

接收值

此示例显示了生成器如何在迭代期间接收值。

receiving_generator.php
<?php

declare(strict_types=1);

function echoingGenerator() {
    echo "Generator started\n";
    $input = yield;
    echo "Received: $input\n";
    $input = yield;
    echo "Received: $input\n";
}

$gen = echoingGenerator();
$gen->send('First');
$gen->send('Second');

生成器在首次调用时启动。send 方法向生成器提供值。每次 send 都会恢复执行,直到下一个 yield。值成为 yield 表达式的结果。这使得能够进行双向通信。

内存效率

此示例比较了数组方法和生成器方法之间的内存使用情况。

memory_comparison.php
<?php

declare(strict_types=1);

function generateLines($file) {
    $handle = fopen($file, 'r');
    while (!feof($handle)) {
        yield fgets($handle);
    }
    fclose($handle);
}

// Array approach would load entire file into memory
foreach (generateLines('large_file.txt') as $line) {
    // Process each line
}

生成器一次读取一行,使用最少的内存。数组方法会一次性加载整个文件。生成器非常适合大型数据集。内存使用量与输入大小无关,保持恒定。这使得能够处理大于可用内存的文件。

无限序列

此示例演示了如何使用生成器创建无限序列。

infinite_generator.php
<?php

declare(strict_types=1);

function fibonacci() {
    $a = 0;
    $b = 1;
    while (true) {
        yield $a;
        [$a, $b] = [$b, $a + $b];
    }
}

$count = 0;
foreach (fibonacci() as $number) {
    echo $number . "\n";
    if (++$count > 10) break;
}

斐波那契生成器创建了一个没有内存问题的无限序列。while(true) 循环对于数组来说很危险,但对于 yield 来说是安全的。消费者控制何时停止迭代。这种模式对于数学序列和流很有用。

生成器委托

此示例展示了如何使用 yield from 委托给另一个生成器。

delegation_generator.php
<?php

declare(strict_types=1);

function countToThree() {
    yield 1;
    yield 2;
    yield 3;
}

function countToFive() {
    yield from countToThree();
    yield 4;
    yield 5;
}

foreach (countToFive() as $number) {
    echo $number . "\n";
}

yield from 语法委托给另一个生成器。来自内部生成器的值直接 yield 给调用者。这使得生成器组合和代码重用成为可能。委托完成后,执行会返回到外部生成器。

最佳实践

来源

PHP 生成器文档

本教程涵盖了 PHP 生成器,并通过实际示例展示了 yield 的用法,用于内存高效迭代、无限序列和数据处理。

作者

我叫 Jan Bodnar,我是一名热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

列出 所有 PHP 教程