5

在今天之前,我从未在 PHP 中看到过 SegFault,但显然这是可能的。起初我以为是 mysql 驱动程序,但事实证明这是我的代码;)。

我花了大约 2 天的时间调试我的代码并最终找到了它的原因(所以对于所有遇到此问题的未来 PHP 程序员,欢迎您!)

长话短说,你不能unset()在你正在走路的同一个阵列上做一个array_walk()

目的是消除 $this->votes 中不存在于 $out 数组中的所有元素(其中 $this->votes 的键与 $out 中元素之一的 id 属性匹配)。

我遇到的问题大约是代码运行正常的一半时间,而另一半代码会因 apache 日志中的分段错误而崩溃(这使得调试非常困难,因为直到我注意到这个错误之前已经有一段时间了)。

是的,这是一段经过深思熟虑的代码......

    array_walk($this->votes, function(&$o, $key) use($that, $out) {
        $found = array_filter($out, function($p) use($key) {
            return $p['id'] == $key;
        });

        if(count($found) == 0) {
            unset($this->votes[$key]); //very very bad!!!!
        }
    });
4

1 回答 1

3

据我了解,最终发生的事情是unset()弄乱了$this->votes 数组的长度。array_walk使用一个迭代器,它期望$this->votes数组在整个遍历过程中保持相同的长度。如果我编写自己的array_walk函数 ( for ($i = 0; $i < count($this->votes); $i++),它只会抛出未定义的索引通知。但是由于我们正在使用array_walk它,它实际上会尝试查看内存中可能有也可能没有一些数据的位置。这可能会导致函数的不可预测性(有时代码可以正常运行,有时会导致段错误)。

所以正确的方法是

    $tmpVotes = array();
    array_walk($this->votes, function(&$o, $key) use($that, $out, $tmpVotes) {
        $found = array_filter($out, function($p) use($key, $that, $tmpVotes) {
            return $p['id'] == $key;
        });

        if(count($found) > 0) {
            $tpmVotes[$key] = $o;
        }
    });

    $this->votes = $tmpVotes;

来自 PHP 手册:

只有数组的值可能会改变;它的结构不能改变,即程序员不能添加、取消设置或重新排序元素。如果回调不遵守此要求,则此函数的行为未定义且不可预测。

如果有人有更好的方法来解释这里发生的事情,请发帖!

于 2013-02-07T21:15:41.393 回答