PHP object 在 foreach 循环中加载为参考

PHP object loaded as reference in foreach loop

我正在遍历一个 object 并且我试图在更改其中一个变量的同时复制其中一个项目。

但是当我复制原来的然后在新的中更改标题时,旧的也随之改变。它不应该,因为我没有将它初始化为参考。

$calendar = array(
    (object)[
        'id' => 1,
        'title' => 'original 1',
    ],
    (object)[
        'id' => 2,
        'title' => 'original 2',
    ],
    (object)[
        'id' => 3,
        'title' => 'original 3',
    ],
);

foreach ($calendar AS $key => $item){
    if($item->id == 2){
        $item->title = 'new 2';
        array_splice($calendar, $key, 0, [1]);
        $calendar[$key] = $item;
    }
}

echo "<pre>";
print_r($calendar);
die();

我希望它的输出保持 original 2 不变。但它随之改变。

(
    [0] => stdClass Object
        (
            [id] => 1
            [title] => original 1
        )

    [1] => stdClass Object
        (
            [id] => 2
            [title] => new 2
        )

    [2] => stdClass Object
        (
            [id] => 2
            [title] => new 2
        )

    [3] => stdClass Object
        (
            [id] => 3
            [title] => original 3
        )

)

即使我制作了一个新的 object 并使用那个进行更改,它仍然会更改原始文件。

foreach ($calendar AS $key => $item){
    if($item->id == 2){
        $new_item = $item;
        $new_item->title = 'new 2';
        array_splice($calendar, $key, 0, [1]);
        $calendar[$key] = $new_item;
    }
}

现在我可能可以通过从头开始制作一个新的 object 并将值一个一个地复制到其中来解决这个问题。但这有什么乐趣呢?

所以我的问题是...为什么会这样?尽管我没有将 $item 转换为 &$item

很简单 PHP 对象赋值不会创建新对象。它只是创建一个指向同一对象的新指针。

我想你想使用 clone 关键字:

foreach ($calendar AS $key => $item){
    if($item->id == 2){
        $new_item = clone $item;
        $new_item->title = 'new 2';
        array_splice($calendar, $key, 0, [1]);
        $calendar[$key] = $new_item;
    }
}

分配还是克隆?

因为您正在使用对象,问题是 $new_item = $item; 没有创建新对象,它创建了 [=18= 的 新引用 ], 命名为 $new_item.

在下面的例子中,$a$b是同一个对象:

$a = new stdclass;
$b = $a;
var_dump($a, $b);

输出为:

object(stdClass)#1 (0) {...} // same object #1
object(stdClass)#1 (0) {...} // same object #1

克隆

您可以使用关键字 clone 创建一个新实例:

$a = new stdclass;
$b = clone $a; // Clone the object
var_dump($a, $b);

输出:

object(stdClass)#1 (0) {...} // object #1
object(stdClass)#2 (0) {...} // new object #2

因此,在您的情况下,您可以使用:

if ($item->id == 2) {
    $clone          = clone $item; // << Create a COPY of $item
    $clone->title   = 'new 2'; // Update the copy, not the reference
    $calendar[$key] = $clone; // Add this copy to final array
    // ...
}

关于参数的说明

当您使用对象作为某些函数的参数时,对象是 引用,因此,可以在该函数中更新给定对象。这是一个简单的例子(demo):

function updateObject(object $object): void {
    $object->newProperty = true;
}

$obj = new stdClass;
var_dump($obj);
updateObject($obj);
var_dump($obj);

上面的代码给出了以下(精简)输出:

object(stdClass)#1 (0) { }
object(stdClass)#1 (1) { ["newProperty"]=> bool(true) }

进一步阅读:Objects and references