这就像一个人有两个不同的名字一样。
PHP 中的引用是访问相同变量内容的不同名称的一种方式。它们不像 C 指针;例如,你不能使用它们执行指针运算,它们不是实际的内存地址等等。有关更多信息,请参阅 引用不是什么。相反,它们是符号表别名。请注意,在 PHP 中,变量名和变量内容是不同的,因此相同的内容可以有不同的名称。最接近的类比是 Unix 文件名和文件 - 变量名是目录条目,而变量内容是文件本身。引用可以比作 Unix 文件系统中的硬链接。
与 C 不同,PHP 引用不作为预取消引用的指针处理,而是作为完整的别名。
它们所引用的数据在所有对它的引用都被删除之前,不会被垃圾回收。
“常规”变量本身被认为是引用,并且在垃圾回收方面,它们与使用 & 运算符赋值的变量的处理方式不同。
以下示例旨在澄清。
1) 当作为包含值的变量处理时,引用按预期工作。但是,它们实际上是引用原始数据的对象。
<?php
var = "foo";
$ref1 =& $var; // 新对象,引用 $var
$ref2 =& $ref1; // 直接引用 $var,而不是 $ref1!!!!!
echo $ref; // >foo
unset($ref);
echo $ref1; // >Notice: Undefined variable: ref1
echo $ref2; // >foo
echo $var; // >foo
?>
2) 当通过引用访问时,原始数据将不会被删除,直到 *所有* 对它的引用都被删除。这包括引用和没有 & 运算符分配的“常规”变量,并且在垃圾回收方面,它们之间没有区别。
<?php
$var = "foo";
$ref =& $var;
unset($var);
echo $var; // >Notice: Undefined variable: var
echo $ref; // >foo
?>
3) 要删除原始数据而不删除所有对它的引用,只需将其设置为 null。
<?php
$var = "foo";
$ref =& $var;
$ref = NULL;
echo $var; // 值为 NULL,因此没有打印任何内容
echo $ref; // 值为 NULL,因此没有打印任何内容
?>
4) 将数据放入数组中也被视为对它添加了另一个引用,用于垃圾回收。
有关更多信息,请参阅 https://php.net/manual/en/features.gc.refcounting-basics.php
以下三个代码片段显示了在不同情况下使用引用在标量变量、数组和对象中的效果。
在任何情况下,如果您坚持引用是变量的别名这一概念,结果都是预期的。通过引用赋值后(无论 $a =& $b 还是 $b =& $a),两个变量名都引用同一个变量。
使用标量的引用
<?php
/*
引用是同一个变量的别名
*/
$a = 1;
$b =& $a;
$b = 2;
echo "$a,$b\n"; // 2,2
$a = 3;
echo "$a,$b\n"; // 3,3
// 变量可以在赋值之前绑定
$c =& $d;
$c = 4;
echo "$c,$d\n"; // 4,4
?>
使用数组的引用
<?php
/*
数组元素引用标量变量
*/
$a = 1;
$b = 2;
$c = array(&$a, &$b);
$a = 3;
$b = 4;
echo "c: $c[0],$c[1]\n"; // 3,4
$c[0] = 5;
$c[1] = 6;
echo "a,b: $a,$b\n"; // 5,6
/*
数组之间的引用
*/
$d = array(1,2);
$e =& $d;
$d[0] = 3;
$d[1] = 4;
echo "e: $e[0],$e[1]\n"; // 3,4
$e[0] = 5;
$e[1] = 6;
echo "d: $d[0],$d[1]\n"; // 5,6
$e = 7;
echo "d: $d\n"; // 7 ( $d 不再是数组,而是一个整数 )
/*
使用 foreach 结构遍历引用数组
*/
$a = 1;
$b = 2;
$f = array(&$a,&$b);
foreach($f as $x) // 如果 $x 按值赋值,它不会改变引用的变量。
$x = 3;
echo "a,b: $a,$b\n"; // 1,2
foreach($f as &$x) // 如果 $x 按引用赋值,它会改变引用的变量。
$x = 3;
echo "a,b: $a,$b\n"; // 3,3
// 请注意,循环结束后,$x 仍然引用 $f[1],因此也引用 $b
$x = 4;
echo "a,b: $a,$b\n"; // 3,4 ( $b 受影响 )
// 为了避免之前的副作用,建议 unset $x,将其与 $f[1] 和 $b 断开连接
unset($x);
$x = 5;
echo "a,b: $a,$b\n"; // 3,4 ( $b 不受影响 )
?>
对象引用
<?php
/*
对象属性引用标量变量
*/
$a = 1;
$b = new stdClass();
$b->x =& $a;
$a = 2;
echo "b->x: $b->x\n"; // 2
$b->x = 3;
echo "a: $a\n"; // 3
/* 对象之间的引用 */
$c = new stdClass();
$c->x = 1;
$d =& $c;
$d->x = 2;
echo "c->x: $c->x\n"; // 2
$d = new stdClass();
$d->y = 3;
echo "c->y: $c->y\n"; // 3
echo "c->x: $c->x\n"; // Undefined property: stdClass::$x
?>
总之,“&$reference” 表示“永远不要在这里复制值,而是进行写入”。按引用赋值不是赋值,而是“将 &$variable 设为引用,并且它的值永远不会被复制,同时让我正在赋值的变量也使用该值”。
要“取消引用/取消别名”,您必须取消设置或将显式副本复制到新变量中。
作为引用的对象属性将在克隆后继续存在并保持引用。通常情况下,数组中的引用和 PHP 的数组函数(combine、intersect、call_user_func、func_get_args 等)也是如此。
调用使用引用参数的函数将使提供的变量成为引用。当对参数使用可变数组展开时,也是如此;提供者的数组元素将成为引用。
一般情况下,不要使用它们,除非您正在处理底层调用,或者需要累加器等。对于设计不佳的函数,如果它们使用引用,请为它们提供一个副本进行修改。