【问题标题】:PHP An iterator cannot be used with foreach by referencePHP 迭代器不能通过引用与 foreach 一起使用
【发布时间】:2015-06-30 04:06:36
【问题描述】:

我有一个对象,它实现了Iterator 并拥有 2 个数组:“条目”和“页面”。每当我遍历这个对象时,我都想修改条目数组,但我收到错误 An iterator cannot be used with foreach by reference,我看到该错误始于 PHP 5.2

我的问题是,如何在使用foreach 的同时使用Iterator 类来更改循环对象的值?

我的代码:

//$flavors = instance of this class:
class PaginatedResultSet implements \Iterator {
    private $position = 0;

    public $entries = array();
    public $pages = array();

    //...Iterator methods...
}

//looping
//throws error here
foreach ($flavors as &$flavor) {
    $flavor = $flavor->stdClassForApi();
}

这样做的原因是,有时$flavors不是我的类的一个实例,而只是一个简单的数组。无论它是什么类型,我都希望能够轻松地修改这个数组。

【问题讨论】:

  • 为什么不先检查它是一个类还是一个数组,然后适当地对待它呢?
  • @MuppetGrinder 因为我在很多地方都使用了这些方法,我不想每次都进行检查,而是只在一个地方进行。

标签: php arrays foreach


【解决方案1】:

我刚刚尝试创建一个迭代器,它使用:

public function &current() {
    $element = &$this->array[$this->position];
    return $element;
}

但这仍然不起作用。

我能推荐的最好的方法是实现\ArrayAccess,这将允许你这样做:

foreach ($flavors as $key => $flavor) {
    $flavors[$key] = $flavor->stdClassForApi();
}

使用生成器:

根据生成器上的 Marks 评论进行更新,以下将允许您迭代结果而无需实现 \Iterator\ArrayAccess

class PaginatedResultSet {
    public $entries = array();

    public function &iterate()
    {
        foreach ($this->entries as &$v) {
            yield $v;
        }
    }
}

$flavors = new PaginatedResultSet(/* args */);

foreach ($flavors->iterate() as &$flavor) {
    $flavor = $flavor->stdClassForApi();
}

这是 PHP 5.5 中提供的功能。

【讨论】:

  • 也可以“通过引用”访问生成器的元素,因此这可能是一种替代方法
  • 我最终实现了 \ArrayAccess 和 \Iterator,然后使用 $key => $val 样式而不是通过引用。这非常有效。
  • @MarkBaker 我仍然需要研究它们是如何工作的,当他们说函数的状态在每次调用时都不会改变时,我对它们的意思有点困惑。
  • @Flosculus - 这意味着生成器内的变量在您迭代生成器时不会超出范围,只有在它终止时(不再有效)......我'我目前正在写一本关于 PHP 生成器的书
  • @MarkBaker 对生成器进行了更新。谢谢,很有教育意义。
【解决方案2】:

扩展 Flosculus 的解决方案,如果您不想在每次使用迭代变量时都引用键,您可以在 foreach 的第一行中将对它的引用分配给一个新变量。

foreach ($flavors as $key => $f) {
  $flavor = &$flavors[$key];
  $flavor = $flavor->stdClassForApi();
}

这在功能上与在基础对象上使用键相同,但有助于保持代码整洁,变量名称简短......如果你喜欢这种事情。

【讨论】:

    【解决方案3】:

    如果你在你的 calss 中实现了迭代器函数,我建议在类“setCurrent()”中添加另一个方法:

    //$flavors = instance of this class:
    class PaginatedResultSet implements \Iterator {
        private $position = 0;
    
        public $entries = array();
        public $pages = array();
    
        /* --- Iterator methods block --- */
        private $current;
    
        public function setCurrent($value){
            $this->current = $value;
        }
    
        public function current(){
            return $this->current;
        }
        //...Other Iterator methods...
    }
    

    然后你可以在 foreach 循环中使用这个函数:

    foreach ($flavors as $flavor) {
        $newFlavor = makeNewFlavorFromOldOne($flavor)
        $flavors -> setCurrent($newFlavor);
    }
    

    如果你在其他类中需要这个函数,你也可以定义一个新的迭代器,扩展Iterator接口包含setCurrent()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-05
      • 2012-10-07
      • 1970-01-01
      • 2017-04-15
      • 1970-01-01
      • 1970-01-01
      • 2021-05-09
      相关资源
      最近更新 更多