在 PHP 中有两种传参方式,「传值」和「引用」,我们来看看它们的区别。
1. 传值
$foo = 123;
$bar = $foo;
$foo = 456;
echo $bar; // 输出 123
传值很好理解,就是简单的把一个变量的值拷贝一份,然后赋值给另一个变量。变量与变量之间互相独立,改变其中一个变量的值,对其它变量不会产生直接影响。
2. 引用
2.1 介绍
引用在官方手册中的介绍如下:
在 PHP 中引用意味着可以用不同的名字访问同一个变量内容。不像 C 的指针:例如你不能对它们做指针运算,它们也不是真正的内存地址等。PHP 里面,引用是符号表的别名。注意在 PHP 中,变量名和变量内容是不一样的,因此同样的变量内容可以有不同的变量名。最接近的比喻是 Unix 的文件名和文件——变量名是文件路径,而变量内容是文件本身。引用就好比是 Unix 文件系统中的硬链接。
引用有三种基本操作或使用:
- 引用赋值
- 引用传递
- 引用返回
2.2 引用赋值
$foo = 123;
$bar = &$foo;
$foo = 456;
echo $bar; // 输出 456
PHP 的引用允许两个变量指向同一内容。$foo
和 $bar
在这里是完全相同的,并不是 $a
指向了 $b
或者反过来,而是 $a
和 $b
指向了同一个地方。
2.3 引用传递
可以把一个变量通过引用传递给一个函数,这样该函数就可以修改这个变量了。
function foo(&$var)
{
$var++;
}
$a = 5;
foo($a);
echo $a; // 输出 6
以下内容可以通过引用传递:
- 变量,如
foo($a)
- 函数返回的引用,如:
function foo(&$var)
{
$var++;
}
function &bar()
{
$a = 5;
return $a;
}
foo(bar());
除此之外,任何其它表达式都不能通过引用传递,结果将会是未定义。
2.3 引用返回
当你想使用函数来查找引用应该绑定到哪个变量时,引用返回是很有用的。不要用引用返回来提升性能,引擎自己会自动进行优化。仅仅只在有合理的技术原因时才使用引用返回。
class foo
{
public $value = 42;
public function &getValue()
{
return $this->value;
}
}
$obj = new foo;
$myValue = &$obj->getValue(); // $myValue 是 $obj->value 的引用,即 42
$obj->value = 2;
echo $myValue; // 输出 2
和参数传递不同,这里必须在两个地方都使用
&
——来表明你希望得到的是一个引用返回,而不是值拷贝。$myValue
是引用绑定,不是普通的赋值。如果你打算在函数中用
return $this->value
来返回一个引用,是行不通的;因为返回的是一个表达式的结果,而不是一个变量。只能通过函数的引用来返回一个变量,别无他法。
要使用引用返回,必须用引用赋值:
function &collector()
{
static $collection = array();
return $collection;
}
$collection = &collector();
$collection[] = 'foo';
要将引用返回传递给另一个要使用的函数:
function &collector()
{
static $collection = array();
return $collection;
}
array_push(collector(), 'foo');
注意
array_push(&collector, 'foo');
会导致一个语法错误。
2.4 对象的引用
对象赋值时,默认是引用传递。
class foo
{
public $bar = 'baz';
}
$a = new foo;
$b = $a;
echo $a->bar; // 输出 baz
echo $b->bar; // 输出 baz
$b->bar = 'BAZ';
echo $a->bar; // 输出 BAZ
echo $b->bar; // 输出 BAZ
如果想在内存中生成两个一样的对象或者创建一个对象的副本,可以使用 clone
:
class foo
{
public $bar = 'baz';
}
$a = new foo;
$b = clone $a;
echo $a->bar; // 输出 baz
echo $b->bar; // 输出 baz
$b->bar = 'BAZ';
echo $a->bar; // 输出 baz
echo $b->bar; // 输出 BAZ