PHP 类 : referenced variable in one method affects non-referenced variable in other method

标签 php class properties scope reference

我对以下示例中的行为感到困惑。我在一个项目中偶然发现了这个问题,我花了几个小时将问题缩小到一个简单的例子。所以这是我的简单测试类:

<?php
class Foo {
    public $sess = [['name' => 'John']];

    public function info() {
        $this->runTest();
        echo $this->sess[0]['name'];
    }

    private function runTest() {
        $localSess = &$this->sess[0];
        $this->sessTest();
    }

    private function sessTest() {
        $sessCopy = $this->sess;
        $this->sess[0]['name'] = 'Bob';
        $this->sess = $sessCopy;
    }
}

$myFoo = new Foo;
$myFoo->info();

意外的结果输出是:

Bob

如果 $localSess 只是一个简单的赋值而不是引用,输出是(如预期的):John

我不明白这是怎么回事。由于类属性 $sess 是一个数组而不是对象,因此对 $sessCopy 的赋值应该是一个简单的副本。但是,如果您在修改 $this->sess 后立即打印 $sessCopy 的内容,它将包含“name”的更改值。所以看起来好像 $sessCopy 毕竟是一个引用?但前提是调用方法中的 $localSess 是引用。为什么这很重要?顺便说一句,如果 $localSess 是对类属性的整个数组的引用,而不仅仅是它的第一个索引,那么一切都会按预期工作。

有人可以解释一下吗?如果一个函数中仅存在一个变量引用就可以影响另一个函数中局部变量(不是引用!)的内容,那么这似乎是危险和可怕的。

最佳答案

所以我决定创建一个更简单的示例来说明相同的问题:

$a = [['name' => 'John']];
$b = $a; // makes a copy of `$a`
$b[0]['name'] = "Bob";
echo $a[0]['name]; // returns "John" as expected

但是如果你这样做了:

$a = [['name' => 'John']];
/* var_dump($a) returns:
array(1) {
  [0]=>
  array(1) {
    ["name"]=>
    string(4) "John"
  }
}
*/
$local = &$a[0]; // seems to modify $a[0], so its type &array
/* var_dump($a) returns:
array(1) {
  [0]=>
  &array(1) {
    ["name"]=>
    string(4) "John"
  }
}
*/
$b = $a; // makes a copy of `$a`
$b[0]['name'] = "Bob";
echo $a[0]['name]; // returns "Bob"

我承认这种行为很奇怪,但在多个 PHP 文档注释中都有记录:

事实证明,这实际上是 2000 年首次报告的 PHP 错误:

本应更新文档,但我找不到:

Due to peculiarities of the internal workings of PHP, if a reference is made to a single element of an array and then the array is copied, whether by assignment or when passed by value in a function call, the reference is copied as part of the array. This means that changes to any such elements in either array will be duplicated in the other array (and in the other references), even if the arrays have different scopes (e.g. one is an argument inside a function and the other is global)! Elements that did not have references at the time of the copy, as well as references assigned to those other elements after the copy of the array, will behave normally (i.e. independent of the other array).

但是,它已被标记为 Wont fixed,可能是出于性能原因。 值得注意的评论:

We have discussed this issue and it will put a considerable slowdown on php's performance, to fix this properly.

Therefore this behaviour will be documented.

即使它从未被记录。

我已决定就此问题提出另一个错误报告,以查看在这个问题上的立场是否发生了变化。

我现在重新报告了这个问题: https://bugs.php.net/bug.php?id=80955

解决方案

一种解决方法是:

$b = unserialize(serialize($a));

另一个是:

function array_clone($array) {
    return array_map(function($element) {
        if ((is_array($element))) {
            return array_clone($element);
        } else if (is_object($element)) {
            return clone $element;
        } else {
            return $element;
        }
    }, $array);
}

$b = array_clone($a);

关于PHP 类 : referenced variable in one method affects non-referenced variable in other method,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67106717/

相关文章:

php - 如何在用户注册时创建目录(PHP)

php - 单击动态创建的复选框时通过 AJAX/PHP 更新 MySQL 中的值?

javascript - 为什么一个外部更新的数组不会自动刷新?

c# - 如何定义一个像 'checked' 这样的关键字的属性?

python - 在 python 中使用 'autospec=True' 时模拟类属性

javascript - 如何使用 jquery 和 PHP 传递单选按钮值?

php - 在 MySql 中存储大量内容

Ruby 对象作为指针打印出来

java - 如何使用类参数分解 dao

ios - 观察属性或覆盖 setter 更好吗?