这篇随笔主要是对《PHP5中文手册》内容中"引用的解释"一文的摘要。阅读之后让我大致明白了引用在PHP语言中的用途和特性。

1. PHP中引用的特性

首先,PHP中的引用不是C的指针,可以把它想象成UNIX文件系统中对文件的链接,是变量的另外一个别名或者映射。例如:$b = &$a中,$b和$a都只是一个变量名称,此操作使$b和$a都映射到同一个变量内容;$b = &$c则是让$b重新映射到另外一个变量内容,与$a再无关系;unset($a)只是断开了$a到变量内容的映射,但变量内容并没有被释放,依然可以使用$b来访问,一旦$b也unset了,那么该变量内容在没有任何映射的情况下会被释放。

2. 函数中global $a的作用

在函数中经常会见到global $a,$b这样的语句,其实质是对$_GLOBAL变量的一种引用,具体可以写成如下语句:
$a = &$_GLOBAL['a'];
$b = &$_GLOBAL['b'];
而$_GLOBAL['a']和$_GLOBAL['b']则分别对应全局作用域中$a和$b变量,在函数中对$a和$b的修改会触发函数外部$a和$b的变量内容的修改。
如果在函数内部做如下操作:
$c = array(1,2,3);
$a = &$c;
那么实际上就是改变了$a的映射关系,之前与$_GLOBAL['a']的映射就断开了,再也无法修改函数外部$a的内容。此特性也同样作用在用引用传递函数参数的例子中。
另外,需要注意在函数中unset($a)不会真正释放变量内容,可以看做只是断开了函数内部$a的映射而已。

3.函数的引用传递

引用传递的定义必须在函数定义中体现,在函数使用中,不要写成foo(&$a)的样子,否则会报"Call-time pass-by-reference过时"的警报。
如下情况可以使用引用传递:
A. 变量,如foo($a)
B. New语句,如foo(new Action())
C. 函数的引用返回,如foo(bar()),其中bar()是函数,定义为:function &bar() {$a = 5; return $a;}
其余情况不可作为引用传递,较常见的比如表达式、常量、无引用的函数返回。

4.函数的引用返回

引用返回需要注意使用形式,在定义时如下:
function &bar() {
   $a = 5;
   return $a;
}
在使用时如下:
$ret = &bar();
也就是说,定义和使用时都需要加&符号。

5.对象的赋值传递

尤其需要注意的是,在PHP4和PHP5中对象资源的赋值传递是有区别的。
PHP4中:
$a = new Object() 实际上$a和new Object()的映射到不同对象实例,所以需要显式地使用$a = & new Object()来进行引用赋值传递。
$b = $a 同上。
foo(new Object()) / foo($a) 同上。
foo() {$a = new Object(); return $a} 同上。
PHP5中:
$a = new Object() 两者映射到同一对象,不需要使用引用符。
$b = $a 同上。
foo(new Object()) / foo($a) 同上。
foo() {$a = new Object(); return $a} 同上。
PS:在进行测试的时候,发现一个有趣的现象,$a = new Object(); $b = $a后,如果$a->attr = 12; $b->attr = 13,那么最后的结果竟然是$a->attr和$b->attr都等于13,而不是想象中的各自独立,笔者估计$a->attr和$b->attr并没有做深拷贝,仍是同一块内存的不同映射。该论点待考证。当然,如果是$a = new Object(); $b = &$a; 则不存在该疑惑,因为从头到尾只有一个真实对象,$a和$b都只是映射而已。

6.unset与=null

使用unset($a)与$a=null的结果是不一样的。如果该块内存只有$a一个映射,那么unset($a)与$a=null等价,该内存的引用计数变为0,被自动回收;如果该块内存有$a和$b两个映射,那么unset($a)将导致$a=null且$b不变的情况,而$a=null会导致$a=$b=null的情况。
原因:某变量赋值为null,将导致该变量对应的内存块的引用计数直接置为0,被自动回收。