You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在嵌套yield语句中迭代SplObjectStorage并保留外层迭代状态?

解决SplObjectStorage嵌套迭代时指针干扰的问题

问题本质

SplObjectStorage的迭代状态与存储实例绑定,不像数组的foreach会自动创建独立迭代上下文。嵌套迭代同一个SplObjectStorage实例时,内层迭代会将共享指针移至末尾,直接导致外层迭代终止,无法遍历后续元素。

可行解决方案

1. 获取独立迭代器(推荐,简洁高效)

SplObjectStorage实现了IteratorAggregate接口,调用getIterator()会返回独立的SplObjectStorageIterator实例,每个迭代器拥有专属指针状态,内层迭代不会干扰外层。

修改迭代函数示例:

function iterate2(\SplObjectStorage $objects) {
    // 使用独立迭代器而非直接遍历原存储
    $iterator = $objects->getIterator();
    foreach ($iterator as $object) {
        yield ((array)$object)[0];
    }
}

或直接在遍历逻辑中使用:

foreach ($objects->getIterator() as $a) {
    foreach ($objects->getIterator() as $b) {
        $valA = ((array)$a)[0];
        $valB = ((array)$b)[0];
        echo "$valA -> $valB\n";
    }
}

该方法无需克隆存储,性能开销极低,彻底避免指针干扰。

2. 克隆存储进行迭代(适合需隔离原存储的场景)

若迭代过程中可能修改原存储,或需要完全隔离迭代上下文,可在生成器内部克隆SplObjectStorage:

function findAll(\SplObjectStorage $objects) {
    $clone = clone $objects;
    foreach ($clone as $object) {
        yield $object;
    }
}

克隆开销在多数场景下可忽略,同时能保证原存储的迭代状态不受影响。

3. 手动记录并恢复指针(不推荐,仅特殊场景使用)

若受限制无法使用前两种方法,可在外层迭代时记录当前key,内层迭代结束后重置指针位置。但该方法依赖存储key的稳定性,且迭代中修改存储会导致失效,代码冗余:

foreach ($objects as $outerKey => $a) {
    $valA = ((array)$a)[0];
    // 内层迭代
    foreach ($objects as $b) {
        $valB = ((array)$b)[0];
        echo "$valA -> $valB\n";
    }
    // 重置外层指针到之前的位置
    $objects->rewind();
    while ($objects->key() !== $outerKey) {
        $objects->next();
    }
    // 移动指针到下一个元素,准备外层下一次迭代
    $objects->next();
}

为什么数组可以正常嵌套迭代?

PHP数组的foreach会自动创建迭代快照(独立指针上下文),每次遍历基于快照进行,内层迭代不会修改外层的迭代状态。而SplObjectStorage默认的foreach直接使用自身迭代器状态,没有自动创建独立上下文的机制。

内容的提问来源于stack exchange,提问作者Javad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 08:03:33