PHP 的基本赋值运算(传值赋值)是将变量的值拷贝到新变量中(=),事实上生成了一个独立的变量,改变其中一个不会影响另一个。也有另一种引用赋值(=&),事实上是给变量增加了一个引用,两个变量事实上存储了同一个内容,改变其中任何一个都是等效的。

在传递函数参数的时候,也有和赋值相似的情况:如果将一个变量传递给一个函数,那么这个函数只能修改这个变量的一个拷贝;如果将一个变量的引用传递给一个函数,那么这个函数则可以修改这个变量的内容。

这就带来了基本赋值没有的问题,无论是在函数内外,还是 unset 引用的时候,都会比基本赋值要复杂一点。与其解释引用在这些情况下的行为,不如先了解引用的工作方式。好在,php.net 上有个用户评论,很好地回答了这个问题,我把它翻译如下。读了它以后,你就能对引用有一个直观的理解。

首先,想象这样一个内存:

地址 引用  
1 NULL  
2 NULL  
3 NULL  
4 NULL  
5 NULL  

创建几个变量:

$a = 10;
$b = 20;
$c = array(1, 2, 3);

内存变成了这样:

地址 引用  
1 10 $a  
2 20 $b  
3 1 $c[0]  
4 2 $c1  
5 3 $c[2]  

来个引用赋值:

$a =& $c[2];
地址 引用  
1 NULL 这块唯一的引用 $a 指向别处了,所以块内容被垃圾回收销毁
2 20 $b  
3 1 $c[0]  
4 2 $c1  
5 3 $c[2], $a 现在 $a 成了这块的引用

再来个引用赋值:

$b =& $a; // 或者 $b =& $c[2]; 也是同样的效果
地址 引用  
1 NULL  
2 NULL 这块也没有引用了,被销毁
3 1 $c[0]  
4 2 $c1  
5 3 $c[2], $a, $b 现在 $b 也成了这块的引用

现在 unset 掉一个:

unset( $c[2] );
地址 引用  
1 NULL  
2 NULL  
3 1 $c[0]  
4 2 $c1  
5 3 $a, $b 虽然 $c[2] 没了,但这块还有引用,所以不会被销毁

再来个基本赋值:

$c[2] = 500;
地址 引用  
1 500 $c[2] 新插入了一个数据,放进内存中的空位
2 NULL  
3 1 $c[0]  
4 2 $c1  
5 3 $a, $b 这块被占用了,所以新数据不会放在这儿

如果想让 $c[2] 引用它最早先引用的那块数据(地址 5):

$c[2] =& $a; // 或者 $c[2] =& $b; 也是同样的效果
unset($a);
unset($b);
地址 引用  
1 NULL  
2 NULL  
3 1 $c[0]  
4 2 $c1  
5 3 $c[2] $c[2] 回来了,$a $b 被销毁

读完这个用户评论,引用的工作方式就清楚了。所以,最好的理解引用的方式,就是“引用是一个变量的别名”。事实上,新建变量时的变量名和后来为这个变量增加的引用,都可以看作是“引用”,事实上它们都是对同一块内存数据的引用,它们之间没有什么本质的区别。

不过值得注意的是,这个“别名”也遵循一般变量的生存周期。也就是说,在函数内给一个函数外的变量(在函数内声明为 global)生成一个引用,这个引用的生命周期同样仅限于函数内。就像是你上中学的时候,同学们给你取了个外号,但是当你升入大学以后,没有人会知道你有那个外号。

$str1 = 'I am serious.';
$str2 = 'Why so serious?';

function some_func() {
    global $str1, $str2;
    $str2 =& $str1;
}

some_func();
echo $str2; // Why so serious?

看起来,虽然大家都是引用,也得看出身啊:)。