ZetCode

PHP 值复制 vs 引用

最后修改于 2025 年 5 月 16 日

本教程解释了 PHP 如何处理值复制和引用,这会影响变量赋值和参数传递。 理解这些概念对于编写高效的 PHP 代码至关重要,尤其是在处理大型数据结构时。

PHP 中的值复制

默认情况下,PHP 对变量赋值使用写时复制语义。 这意味着变量按值复制,但实际的复制只发生在修改变量时。

特征 按值复制 按引用复制
默认行为 是 (写时复制) 否 (必须使用 & 运算符)
内存使用 直到修改时有效 共享,直到取消设置
示例 $b = $a $b = &$a

按值复制示例

PHP 的默认复制行为会在修改变量时创建独立的副本,但使用写时复制来优化内存使用。

main.php
<?php

declare(strict_types=1);

// Simple types are always copied by value
$a = 10;
$b = $a; // Copy by value

echo "Original: a = $a, b = $b\n";
$b = 20; // Doesn't affect $a
echo "After change: a = $a, b = $b\n";

// Arrays use copy-on-write
$arr1 = [1, 2, 3];
$arr2 = $arr1; // Copy-on-write (no actual copy yet)

$arr2[0] = 99; // Now makes a copy
echo "arr1[0] = {$arr1[0]}, arr2[0] = {$arr2[0]}\n";

在示例中,修改 $b 不会影响 $a,因为它们是独立的副本。 但是,修改 $arr2 会创建一个新的数组副本,而 $arr1 保持不变。 这是由于 PHP 的写时复制优化,它避免了在未进行修改之前不必要的内存使用。

$ php main.php
Original: a = 10, b = 10
After change: a = 10, b = 20
arr1[0] = 1, arr2[0] = 99

PHP 中的字符串:按值复制

PHP 中的字符串是标量类型,始终按值复制。 将一个字符串变量分配给另一个变量会创建一个独立的副本。 修改一个字符串不会影响另一个字符串,因为每个变量都保存其自己的值。

main.php
<?php

declare(strict_types=1);

$s1 = "hello";
$s2 = $s1; // Copy by value

echo "Original: s1 = $s1, s2 = $s2\n";
$s2 = "there"; // s1 is not affected

echo "After change: s1 = $s1, s2 = $s2\n";

在此示例中,$s1$s2 是独立的。 更改 $s2 不会影响 $s1,因为字符串在 PHP 中始终按值复制。

$ php main.php
Original: s1 = hello, s2 = hello
After change: s1 = hello, s2 = there

对象和引用

自 PHP 5 以来,默认情况下,对象按引用处理。 将对象分配给一个新变量不会创建副本,而是创建对同一对象的另一个引用。

main.php
<?php

declare(strict_types=1);

class Person {
    public $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
}

$person1 = new Person('Alice');
$person2 = $person1; // Both reference the same object

echo "Original: person1->name = {$person1->name}, ";
echo "person2->name = {$person2->name}\n";

$person2->name = 'Bob'; // Affects both references
echo "After change: person1->name = {$person1->name}, ";
echo "person2->name = {$person2->name}\n";

// To create a true copy, use clone
$person3 = clone $person1;
$person3->name = 'Charlie';
echo "After clone: person1->name = {$person1->name}, ";
echo "person3->name = {$person3->name}\n";

在示例中,修改 $person2 也会影响 $person1,因为它们引用同一个对象。 但是,使用 clone 会创建一个新对象,允许独立修改。

$ php main.php
Original: person1->name = Alice, person2->name = Alice
After change: person1->name = Bob, person2->name = Bob
After clone: person1->name = Bob, person3->name = Charlie

显式引用

PHP 允许使用 & 运算符创建显式引用。 这会创建一个别名,其中两个变量都指向相同的数据。

main.php
<?php

declare(strict_types=1);

$a = 10;
$b = &$a; // $b is now a reference to $a

echo "Original: a = $a, b = $b\n";
$b = 20; // Changes both $a and $b
echo "After change: a = $a, b = $b\n";

// Works with arrays too
$arr1 = [1, 2, 3];
$arr2 = &$arr1;
$arr2[0] = 99;
echo "arr1[0] = {$arr1[0]}, arr2[0] = {$arr2[0]}\n";

// To break a reference
unset($b); // Removes the reference, $a keeps its value

在此示例中,修改 $b 也会影响 $a,因为它们是对相同值的引用。 这也适用于数组。

$ php main.php
Original: a = 10, b = 10
After change: a = 20, b = 20
arr1[0] = 99, arr2[0] = 99

参数传递

PHP 支持通过值传递和通过引用传递函数参数。 默认情况下,参数通过值传递,但可以指定引用。

main.php
<?php

declare(strict_types=1);

// Pass by value (default)
function modifyValue($x) {
    $x = 100;
    echo "Inside function: x = $x\n";
}

$a = 10;
modifyValue($a);
echo "After function: a = $a\n";

// Pass by reference
function modifyReference(&$x) {
    $x = 100;
    echo "Inside function: x = $x\n";
}

$b = 10;
modifyReference($b);
echo "After function: b = $b\n";

// Objects are always passed by reference
function modifyObject($obj) {
    $obj->value = 100;
}

$object = new stdClass();
$object->value = 10;
modifyObject($object);
echo "Object value: {$object->value}\n";

在示例中,第一个函数修改了变量的副本,而第二个函数修改了原始变量。 对象按引用传递,因此其属性被直接修改。

$ php main.php
Inside function: x = 100
After function: a = 10
Inside function: x = 100
After function: b = 100
Object value: 100

返回引用

PHP 函数可以返回引用,但应谨慎使用,因为它可能导致代码混淆和维护问题。

main.php
<?php

declare(strict_types=1);

class Container {
    private $data = [];
    
    public function &getDataReference() {
        return $this->data;
    }
    
    public function getDataCopy() {
        return $this->data;
    }
}

$container = new Container();
$ref = &$container->getDataReference();
$ref['key'] = 'value'; // Modifies the container's private data

$copy = $container->getDataCopy();
$copy['key'] = 'new value'; // Doesn't affect container

var_dump($container->getDataCopy());

在此示例中,getDataReference 方法返回对容器私有数据的引用,允许修改影响原始数据。 getDataCopy 方法返回一个副本,因此修改不会影响原始数据。

$ php main.php
array(1) {
  ["key"]=>
  string(5) "value"
}

总结与最佳实践

本教程涵盖了 PHP 对值和引用复制的方法,包括赋值行为、参数传递和对象处理。

来源

PHP 引用

PHP 对象和引用

作者

我叫 Jan Bodnar,是一名充满激情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出 所有 PHP 教程