PHP 手册里的引用部分看得人一头雾水,所以按自己的理解写了一篇,并且写了一些有意思的示例代码,分享出来。
PHP 引用的解释
我们都知道 php 有引用这个功能,引用不同于指针,他相当于变量名的一个别名,可以想象成一个 linux 的文件名做一个软连接。
看下面代码
$damage = 100;
$harm = & $damage;
这时内存中的数据 100
有了两个名称 damage
和 harm
, 当我们调用 $damage
和 $harm
这两个变量名其实访问的是同一个数据。
所以,以下的代码可以理解
$damage = 100;
$harm = & $damage;
echo $damage.PHP_EOL; // 输出 100
$harm = 200;
echo $damage.PHP_EOL; // 输出 200
将 200
赋值给 $harm
时,由于 $damage
和 $harm
两个变量名对应同一个内存位置的数据,所以 $harm
的值变化 $damage
的值也同样会变化。
PHP 引用可以做什么
函数传递
通常我们将变量传递到函数中时,变量的值和函数内部对变量数据的处理就脱离了关系(这通常叫值传递)。
可以将变量以引用的形式传递到函数中,这样可以实现在函数内部改变外部变量的值。
看下面代码
$name = "狄仁杰";
function changeName(& $name){
$name = "项羽";
}
changeName( $name );
echo $name.PHP_EOL; // 输出“项羽”
可以看到函数并没有返回值,但是变量 $name
的值变了。
返回引用
看下面代码
<?php
class Magic {
private $map = [];
public function & bind($key){
return $this->map[$key];
}
public function set($key, $value){
$this->map[$key] = $value;
}
}
$name = "";
$magic = new Magic();
$name = & $magic->bind('name');
$magic->set('name', '花木兰');
echo $name."\n"; // 输出“花木兰”
$magic->set('name', '狄仁杰');
echo $name."\n"; // 输出“狄仁杰”
$name = "阿珂";
echo $magic->get('name')."\n"; // 输出“阿珂”
运行以上代码发现,我们并没有对变量$name
进行直接赋值,我们只是对对象内部的 $map
属性进行了设置,变量 $name
就做出了相应的改变, 有木有很神奇?
这是因为我们将数组$map
的一个元素name
作为引用返回给了外部变量$name
, 这样就建立了变量 $name
到 $this->map['name']
之间的连接,当两者任何一方的值被改变时,另一者也会被改变 。
所以,最后的代码,当外部变量 $name
发生变化,对象内部的对应的数据就也发生了变化。
注意以上示例代码,返回引用时有两处都用了 &
符号!。
unset 一个变量名
运行以下代码:
<?php
$damage = 100;
$harm = & $damage;
unset($damage);
echo $harm;
我们发现,将原变量名$damage
unset 掉了,引用 $harm
依然可以用,这说明当一个变量有引用时,相当于它有了多个别名,只要还有一个别名没有被 unset 掉,变量所占的内存就不会被注销。
理解 global 关键字
当我们需要在函数内部调用外部变量,我们一般需要用到global
关键字,这里可以稍微考虑以下。
我们上面好像多次讨论到了函数内部对外部变量的处理, 这跟 global
是不是有什么关系?
没错,global
的功能就是基于引用的机制来实现的。
<?php
$name = "狄仁杰";
function say(){
global $name;
echo $name."\n";
}
以上代码与下面代码等价
<?php
$name = "狄仁杰";
function say(){
$name = & $GLOBALS['name'];
echo $name."\n";
}
解释:所有的全局变量都在数组 $GLOBALS
中, global $name
相当于建立一个 变量$name
对$GLOBALS['name']
的引用,当任何一方发生变化时,另一方也会变化,但是 unset 掉一方,对另一方也不会有影响。