在foreach()迭代期间修改数组

Gor*_*man 10 php arrays foreach

我有一些关于在foreach()循环期间修改数组的问题.在下面的代码中,我遍历三个包含闭包/回调的数组并调用每个数组.我在迭代期间向每个数组的末尾附加一个闭包,但是有时foreach()似乎没有认识到数组已经改变了大小,因此附加的闭包不会被调用.

class Foo
{
    private $a1 = array();
    private $a2 = array();

    public function f()
    {
        echo '<pre style="font-size: 20px;">';
        echo 'PHP: ' . phpversion() . '<br><br>';

        $this->a1[] = function() { echo 'a1 '; };
        $this->a1[] = array($this, 'g');
        foreach ($this->a1 as &$v)
        {
            // The callback added in g() never gets called.
            call_user_func($v);
            //echo 'count(v) = ' . count($v) . ' ';
        }

        echo '<br>';

        // The same thing works fine with a for() loop.
        $this->a2[] = function() { echo 'a2 '; };
        $this->a2[] = array($this, 'h');
        for ($i = 0; $i < count($this->a2); ++$i)
            call_user_func($this->a2[$i]);

        echo '<br>';

        // It also works fine using a local array as long as it
        // starts off with more than one element.
        $a3[] = function() { echo 'a3 '; };
        //$a3[] = function() { echo 'a3 '; };
        $i = 0;
        foreach ($a3 as &$x)
        {
            call_user_func($x);
            if ($i++ > 1) // prevent infinite loop
                break;

            // Why does this get called only if $a3 originally starts
            // with more than one element?
            $a3[] = function() { echo 'callback '; };
        }

        echo '</pre>';
    }

    private function g()
    {
        echo 'g() ';
        $this->a1[] = function() { echo 'callback '; };
    }

    private function h()
    {
        echo 'h() ';
        $this->a2[] = function() { echo 'callback '; };
    }
}

$foo = new Foo;
$foo->f();
Run Code Online (Sandbox Code Playgroud)

输出:

PHP: 5.3.14-1~dotdeb.0

a1 g() 
a2 h() callback 
a3
Run Code Online (Sandbox Code Playgroud)

预期产量:

a1 g() callback
a2 h() callback 
a3 callback
Run Code Online (Sandbox Code Playgroud)

$a3如果我在循环之前取消注释第二个元素的输出:

a3 a3 callback
Run Code Online (Sandbox Code Playgroud)
  1. 为什么第一个循环foreach ($this->a1 as &$v)实现$v没有另一个迭代元素?
  2. 为什么$a3在第三个循环期间修改工作foreach ($a3 as &$x),但仅当数组以多个元素开始时?

我意识到在迭代期间修改数组可能不是一个好主意,但是因为PHP似乎允许它,我很好奇为什么上面的工作方式如此.

小智 3

1.为什么第一个循环 foreach ($this->a1 as &$v) 没有意识到 $v 还有另一个要迭代的元素?

该行为看起来是由于每次 foreach 迭代时内部指针在数组上前进所致。在数组的最后一次迭代时,即内部指针已经为空时,将数组元素添加到数组末尾,意味着该元素将不会被迭代。通过对代码进行一些修改,可以看到这一点。

class Foo
{
    private $a1 = array();
    private $a2 = array();

    public function f()
    {
        echo '<pre style="font-size: 20px;">';
        echo 'PHP: ' . phpversion() . '<br><br>';

        $this->a1[] = function() { echo 'a1 <br/>'; };
        $this->a1[] = array($this, 'g');
        foreach ($this->a1 as $key => &$v)
        {
           //lets get the key that the internal pointer is pointing to 
           // before the call.
                  $intPtr = (key($this->a1) === null) ? 'null' : key($this->a1);
                echo 'array ptr before key ', $key, ' func call is ',    
                       $intPtr, '<br/>' ;
            call_user_func($v);
            //echo 'count(v) = ' . count($v) . ' ';
        }

        echo '<br><br>';

        // The same thing works fine with a for() loop.
        $this->a2[] = function() { echo 'a2 '; };
        $this->a2[] = array($this, 'h');
        for ($i = 0; $i < count($this->a2); ++$i)
            call_user_func($this->a2[$i]);

        echo '<br><br>';

        // It also works fine using a local array as long as it
        // starts off with more than one element.
        $a3[] = function() { echo 'a3 '; };
        //$a3[] = function() { echo 'a3 '; };
        $i = 0;
        foreach ($a3 as &$x)
        {
            call_user_func($x);
            if ($i++ > 1) // prevent infinite loop
                break;

            // Why does this get called only if $a3 originally starts
            // with more than one element?
            $a3[] = function() { echo 'callback '; };
        }

        echo '</pre>';
    }

    private function g()
    {
        echo 'g() <br>';
        $this->a1[] = function() { echo 'callback '; };
    }

    private function h()
    {
        echo 'h() <br>';
        $this->a2[] = function() { echo 'callback '; };
    }
}

$foo = new Foo;
$foo->f(); 
Run Code Online (Sandbox Code Playgroud)

输出:

array ptr before key 0 func call is 1
a1 
array ptr before key 1 func call is null <-will not iterate over any added elements!
g() 

a2 h() 
callback 

a3
Run Code Online (Sandbox Code Playgroud)

2.为什么修改 $a3 在第三次 foreach 循环期间有效($a3 as &$x),但仅当数组以多个元素开始时才有效?

当然,如果在内部指针返回 null 之前向数组添加一个元素,那么该元素将被迭代。在您的情况下,如果数组有一个元素,那么在第一次迭代时内部指针已经返回 null。但是,如果最初有多个元素,则可以在第一次迭代时添加附加元素,因为此时内部指针将指向第二个初始元素。