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 掉一方,对另一方也不会有影响。