PHP 引用


引用是什么

在 PHP 中引用意味着用不同的名字访问同一个变量内容(内存块)。

和 C 指针不同的是:你不能对他们做指针运算,他们并不是实际的内存地址。 

所以在PHP 中引用是符号表别名。


引用做什么

使用引用进行的基本操作有三种:引用赋值、引用传递和引用返回。

引用赋值

PHP 的引用允许用两个变量指向同一个内容:

<?php
$a =& $b;

这意味着 $a 和 $b 指向了同一个变量。

注意:$a 和 $b 在这里是完全相同的,$a 和 $b 指向了同一个地方。

注意:如果对一个未定义的变量进行引用赋值、引用参数传递或引用返回,则会自动创建该变量。

示例 对未定义的变量使用引用

<?php
function foo(&$var) { }
foo($a); // $a 被创建,并被赋值为 null
$b = array();
foo($b['b']);
var_dump(array_key_exists('b', $b)); // bool(true)
$c = new StdClass;
foo($c->d);
var_dump(property_exists($c, 'd')); // bool(true)

同样的语法可以用在返回引用的函数中:

<?php
$foo =& find_var($bar);

new 会自动返回引用,因此在语法上是无效的。 

示例 在函数内引用全局变量

<?php
$var1 = "Example variable";
$var2 = "";
function global_references($use_globals)
{
    global $var1, $var2;
    if (!$use_globals) {
        $var2 =& $var1; // visible only inside the function
    } else {
        $GLOBALS["var2"] =& $var1; // visible also in global context
    }
}
global_references(false);
echo "var2 is set to '$var2'\n"; // var2 is set to ''
global_references(true);
echo "var2 is set to '$var2'\n"; // var2 is set to 'Example variable'

把 global $var; 当成是 $var =& $GLOBALS['var']; 的简写。从而将其它引用赋给 $var 只改变了本地变量的引用。

注意:如果在 foreach 语句中给一个具有引用的变量赋值,被引用的对象也被改变。

示例 引用与 foreach 语句

<?php
$ref = 0;
$row =& $ref;
foreach (array(1, 2, 3) as $row) {
    // do something
}
echo $ref; // 3 - last element of the iterated array

虽然不是严格意义上的引用赋值,但使用语言结构array()创建的表达式也可以通过在要添加的数组元素前加上&来实现。例如:

<?php
$a = 1;
$b = array(2, 3);
$arr = array(&$a, &$b[0], &$b[1]);
$arr[0]++; $arr[1]++; $arr[2]++;
/* $a == 2, $b == array(3, 4); */

但是,请注意,数组内部的引用是有潜在危险的。对右侧的引用进行正常的(不是通过引用)赋值,并不会将左侧的引用变成引用,但是在这些正常的赋值中,数组内部的引用会被保留下来。这也适用于数组按值传递的函数调用。例如:

<?php
/* Assignment of scalar variables */
$a = 1;
$b =& $a;
$c = $b;
$c = 7; //$c is not a reference; no change to $a or $b
/* Assignment of array variables */
$arr = array(1);
$a =& $arr[0]; //$a and $arr[0] are in the same reference set
$arr2 = $arr; //not an assignment-by-reference!
$arr2[0]++;
/* $a == 2, $arr == array(2) */
/* The contents of $arr are changed even though it's not a reference! */

换句话说,数组的引用行为是在逐个元素的基础上定义的,单个元素的引用行为与数组容器的引用状态是分离的。

传引用

引用做的第二件事是用引用传递变量。这是通过在函数内建立一个本地变量并且该变量在呼叫范围内引用了同一个内容来实现的。例如:

<?php
function foo(&$var)
{
    $var++;
}
$a=5;
foo($a);

将使 $a 变成 6。这是因为在 foo 函数中变量 $var 指向了和 $a 指向的同一个内容。更多详细解释见引用传递。

引用返回

引用做的第三件事是引用返回。


引用不是什么

如前所述,引用不是指针。这意味着下面的结构不会产生预期的效果:

<?php
function foo(&$var)
{
    $var =& $GLOBALS["baz"];
}
foo($bar);

这将使 foo 函数中的 $var 变量在函数调用时和 $bar 绑定在一起,但接着又被重新绑定到了 $GLOBALS["baz"] 上面。不可能通过引用机制将 $bar 在函数调用范围内绑定到别的变量上面,因为在函数 foo 中并没有变量 $bar(它被表示为 $var,但是 $var 只有变量内容而没有调用符号表中的名字到值的绑定)。可以使用引用返回来引用被函数选择的变量。


引用传递

可以将一个变量通过引用传递给函数,这样该函数就可以修改其参数的值。语法如下:

<?php
function foo(&$var)
{
    $var++;
}
$a=5;
foo($a);
// $a is 6 here

注意在函数调用时没有引用符号——只有函数定义中有。光是函数定义就足够使参数通过引用来正确传递了。

以下内容可以通过引用传递:

  • 变量,例如 foo($a);

  • 从函数中返回的引用,例如:

<?php
function foo(&$var)
{
    $var++;
}
function &bar()
{
    $a = 5;
    return $a;
}
foo(bar());

任何其它表达式都不能通过引用传递,结果未定义。

例如下面引用传递的例子是无效的:

<?php
function foo(&$var)
{
    $var++;
}
function bar() // Note the missing &
{
    $a = 5;
    return $a;
}
foo(bar()); // 导致 notice 信息
foo($a = 5) // 表达式,不是变量
foo(5) // 导致致命错误
class Foobar
{
}
foo(new Foobar()) // Produces a notice as of PHP 7.0.7
                  // Notice: Only variables should be passed by reference

引用返回

引用返回用在当想用函数找到引用应该被绑定在哪一个变量上面时。 不要用返回引用来增加性能,引擎足够聪明来自己进行优化。 仅在有合理的技术原因时才返回引用! 使用此语法返回引用:

<?php
class foo {
    public $value = 42;
    public function &getValue() {
        return $this->value;
    }
}
$obj = new foo;
$myValue = &$obj->getValue(); // $myValue is a reference to $obj->value, which is 42.
$obj->value = 2;
echo $myValue;                // prints the new value of $obj->value, i.e. 2.

本例中 getValue 函数所返回的对象的属性将被赋值, 而不是拷贝,就和没有用引用语法一样。

注意: 和参数传递不同,这里必须在两个地方都用 & 符号——指出返回的是一个引用,而不是通常的一个拷贝,同样也指出 $myValue 是作为引用的绑定,而不是通常的赋值。

注意: 如果试图这样从函数返回引用:return ($this->value);,这将不会起作用, 因为在试图返回一个表达式的结果而不是一个引用的变量。 只能从函数返回引用变量——没别的方法。

要使用返回的引用,必须使用引用赋值:

<?php
function &collector() {
  static $collection = array();
  return $collection;
}
$collection = &collector();
$collection[] = 'foo';

要将返回的引用传递给另一个期望引用的函数,你可以使用这个语法:

<?php
function &collector() {
  static $collection = array();
  return $collection;
}
array_push(collector(), 'foo');

注意:array_push(&collector(), 'foo');将不起作用,它会导致一个致命的错误。


取消引用

当 unset 一个引用,只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了。例如:

<?php
$a = 1;
$b =& $a;
unset($a);

不会 unset $b,只是 $a。


引用定位

许多 PHP 的语法结构是通过引用机制实现的,所以上述有关引用绑定的一切也都适用于这些结构。 一些结构,例如引用传递和返回,已经在上面提到了。 其它使用引用的结构有:

global 引用

当用 global $var 声明一个变量时实际上建立了一个到全局变量的引用。 也就是说和这样做是相同的:

<?php
$var =& $GLOBALS["var"];

这意味着,例如,unset $var 不会 unset 全局变量。

$this

在一个对象的方法中,$this 永远是调用它的对象的引用。