对象分配正在 PHP 中创建引用

Object assignment is creating a reference in PHP

我在将现有对象分配给数组时出现奇怪的行为。 在下面的示例中,我有一个包含一个 属性 的 class。我创建了一个包含 3 个实例的第一个数组(我们称它们为 2-4-6),然后使用第一个数组的一个对象(实例 4)创建了一个包含 2 个其他实例的第二个数组。在修改第二个数组中对象的值以创建 2 个新实例(即实例 3-5)时,实例 4 也被修改。因此,在第一个数组查询中,我得到了正确的值 (2-4-6),但在创建第二个数组后,我得到了修改后的值 (2-5-6)。我希望赋值运算符复制了对象,但它创建了对实例的引用。在示例中,我可以通过显式调用克隆来解决这个问题,但在更大范围内,这是行不通的(错误的代码优化?)。关于如何避免此问题的任何线索(或良好做法)? 谢谢!

<?php
    class TestBase
    {
        private int $m_test = 0;
        public function SetTest(int $v)
        {
            $this->m_test = $v;
        }
        public function GetTest() : int
        {
            return $this->m_test;
        }
    }

    function getNewList(TestBase $ref) : array
    {
        $newlist = [3 => new TestBase(), 5 => $ref];
        $newlist[3]->SetTest(3);
        $newlist[5]->SetTest(5);
        return $newlist;
    }

    $listOfTest = [2 => new TestBase(), 4 => new TestBase(), 6 => new TestBase()];
    $listOfTest[2]->SetTest(2);
    $listOfTest[4]->SetTest(4);
    $listOfTest[6]->SetTest(6);

    foreach ($listOfTest as $test)
    {
        echo $test->GetTest().'<br>';
    }
    // 2
    // 4
    // 6

    //$ref = clone $listOfTest[4];
    $ref = $listOfTest[4];
    $newList = getNewList($ref);

    foreach ($listOfTest as $test)
    {
        echo $test->GetTest().'<br>';
    }
    // 2
    // 5
    // 6
?>

问题在于 PHP 使用数组的方式。引用数组赋值 仅当参数和左值都是对数组的引用时才有效。如果传入一个对象,它将被复制并在两个地方设置对它的引用。后续对左值的重新赋值不会对原数组产生任何影响。

引用现有数组然后使用 clone 复制它可以解决这个问题。下面的示例显示了如何完成此操作,但您也可以使用工厂或其他方式创建一个新数组,该数组引用所有现有元素(通过对每个元素调用 get_object_vars())。

<?php
    function getNewList(TestBase $ref) : array
    {
        $newlist = [3 => clone $ref, 5 => clone $ref];
        $newlist[3]->SetTest(3);
        $newlist[5]->SetTest(5);
        return $newlist;
    }

    //$ref = clone $listOfTest[4];
    $ref = &$listOfTest[4];

    var_dump($ref); // TestBase Object (1) (2) { ["m_test"]=> int(5) }

    //$newList = getNewList($ref);

    foreach ($listOfTest as &$test)
    {
        echo "before: ".$test->GetTest().'<br>'; // 2, 4, 6 in output here!

        var_dump($test); // TestBase Object (1) (2) { ["m_test"]=> int(4) }

        echo "after: ".$test->GetTest().'<br>';  // 2, 4, 6 in output here!

        var_dump($test); // TestBase Object (1) (2) { ["m_test"]=> int(4) }
    }
?>