登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  php教程

PHP Generator return 获取遍历结束值的实现方式

来源:17golang原创

时间:2026-11-13 16:31:12 406浏览 收藏

PHP 生成器的遍历结束值,要在同一个 Generator 正常执行完毕后调用 getReturn()取得。yield提供 foreach 消费的逐项数据,return提供生成器结束时的独立结果;它不会变成 foreach 的最后一项。若提前 break 或执行因未处理异常中断,就不能把循环外的位置当成“汇总已经准备好”。

PHP 官方手册:https://www.php.net/manual/en/generator.getreturn.php

先保留 Generator,再在完整遍历后取汇总

例如价格流需要一边输出条目,一边在结束后给出条数和总额,可以把明细放在 yield,把汇总放在 return。下面是原创 PHP 8 示例,展示预期语义,未作为本机运行记录。

 $count, 'total' => $total];
}

// 保存这一次调用返回的对象,避免结束后又创建一个新生成器。
$rows = pricedRows([12, 25, 8]);
foreach ($rows as $price) {
    echo $price, PHP_EOL;
}
// 完整消费后读取汇总;return 数组不会成为 foreach 的额外一项。
$summary = $rows->getReturn();
echo json_encode($summary, JSON_UNESCAPED_UNICODE), PHP_EOL;

按这段代码,明细依次为 12、25、8;结束汇总是 {"count":3,"total":45}。这两个通道用途不同:消费者可逐项处理,最后再读取一个小型结果,无需为了取得汇总而先把整个明细流转成数组。

调试时首先看变量是否指向同一对象。pricedRows(...)每调用一次都会得到一个新的生成器;遍历一个对象以后,再调用函数并在新对象上取返回值,读不到先前那次遍历的结果。函数签名中的 : Generator描述函数交给调用方的对象类型,不是在声明内部 return 数组的类型。

Generator对象与yield明细return汇总及消费者的静态关系
原创结构图:同一个生成器关联两种结果。foreach读取yield明细,结束后的getReturn读取return汇总,不是运行截图。

拿不到返回值时,检查是否仍停在 yield

getReturn() 是读取接口,不会替调用方把剩余任务自动执行完。刚创建生成器、停在中间某个 yield、甚至停在最后一个 yield,都可能尚未执行到最终 return。普通 foreach 完整结束会继续推进到结束;在循环体中 break 则不会完成这件事。

 1];
}

$items = stockItems();
foreach ($items as $item) {
    // break 只停止消费者循环,生成器此时仍暂停在 yield。
    break;
}
// 此例中 valid() 为 true,不调用 getReturn(),也不擅自继续消耗。
if ($items->valid()) {
    echo '当前只有部分数据,尚无结束汇总', PHP_EOL;
}

这个例子尤其容易误判:只有一个 yield,消费者也已经拿到那一项,但 return 在恢复执行之后才会到达。若业务确实要处理完整输入,应按原消费逻辑继续;若业务就是找到首项即停止,应把结果定义为部分结果,不应为了取汇总盲目调用 next() 排空生成器,因为剩余代码可能还包含读取、计算或写入等副作用。

手动遍历还应区分 current() 和 next():前者用于取当前明细,后者恢复生成器。next() 没有“返回最终汇总”的职责。使用何种遍历方式,都要把消费动作与完成结果的读取分别写清。

用 yield from 时显式接住内部返回值

外层使用 yield from 委托内部生成器时,内部 yield 的条目会向外暴露,内部 return 则是 yield from 表达式的值。若外层需要向调用方报告该汇总,必须显式接住并 return,或者将其包装进外层结果。

 2];
}

function exportValues(): Generator
{
    // yield from 转交明细,其表达式结果是内部生成器的 return 值。
    $innerSummary = yield from chunkValues();
    return ['source' => 'demo', 'summary' => $innerSummary];
}

$export = exportValues();
foreach ($export as $value) {
    echo $value, PHP_EOL;
}
// 读取的是外层明确返回的结构,不会自动跳到内部生成器。
var_export($export->getReturn());

此例最终结果含有外层的 source 和内部 summary。假如外层仅写 yield from chunkValues(); 然后自然结束,没有返回该表达式结果,调用外层 getReturn() 不会自动替你找到内部汇总。若只想原样转交,可以写 return yield from chunkValues();。

内部生成器yield from表达式内部汇总外层生成器与外层getReturn的关系
原创结构图:内部汇总属于yield from表达式结果,外层return决定调用方最终读取的结构;图中连线表达归属,不表示执行时序。

委托也可以面向数组等可遍历对象,但不要因此假定所有 iterable 都具有生成器式结束汇总。接口若依赖 getReturn(),就应明确要求 Generator,或在边界处做相应类型处理。语法依据:https://www.php.net/manual/en/language.generators.syntax.php。

把正常完成与中断分成不同处理分支

完整遍历且正常返回时,再读取汇总;业务提前结束时,记录部分处理状态;异常逃出生成器或消费者时,先按失败路径处理。不能在 catch 中不加区分地调用 getReturn() 来“补取成功结果”,也不宜用 finally 无条件读取汇总,以免读取错误掩盖原始异常。

还要区分“无法读取返回值”和“返回值本来是 null”。生成器正常走到末尾但没有显式返回结果时,结束值可为 null;显式 return null;也同样如此。不要用 if ($gen->getReturn())判断是否完成,因为 0、false、空数组和 null 都可能是合法结束值。

valid() 可以帮助识别是否仍有当前迭代项,但它不是业务成功标识;初始化尚未启动的生成器时也可能推进执行。即使生成器已不再有效,若其执行是因异常终止,也不能直接推断一定存在正常结束汇总。工程上应由消费流程保留正常完成与失败状态,而不是仅根据一个布尔值恢复业务结论。

按症状选择需要修改的位置

症状优先检查修改方向
foreach 没有出现汇总是否把 return 当作 yield循环结束后对原对象调用 getReturn()
提示尚未返回是否刚创建、提前 break 或停在最后一个 yield明确完整消费或部分结果的契约
明细正常,结果为 null是否正常结束但没有 return 汇总显式 return 所需结构
内部返回了,外层没取到yield from 表达式结果是否丢弃保存内部汇总并显式返回
统计与实际成功数不一致统计的是产出数还是消费成功数分别定义和记录两种计数

读代码时,可以按“原对象、完成时机、return 所在层、失败分支”四个位置检查。示例中的 count 是生成器产出的数量;如果消费者收到条目后写数据库失败,生成器不会自动知道写入是否成功。跨越两个组件的业务统计应单独建模。

常见问题

getReturn() 支持哪些 PHP 版本?

官方手册将该方法列为 PHP 7、PHP 8 功能。本文示例按 PHP 8 写法展示。

iterator_to_array() 能把结束汇总一并放进去吗?

它收集的是迭代项,return 不会成为额外明细。保留 Generator 对象,在完整消费后另取汇总;无须收集全部明细时,直接 foreach 更合适。

break 后还想随时看到统计怎么办?

应另行设计进度状态或消费者计数。结束汇总以正常完成为前提,不能承担实时进度接口的职责。

需要 new Generator() 吗?

不需要,也不能这样实例化。调用包含 yield 的生成器函数即可取得 Generator 对象。类手册:https://www.php.net/manual/en/class.generator.php。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>