识别引用

PHP 中的许多语法结构都是通过引用机制实现的,因此本文中关于引用绑定的所有内容也适用于这些结构。一些结构(如按引用传递和返回)已在上面提到。其他使用引用的结构是

全局引用

当您将变量声明为 global $var 时,实际上是在创建对全局变量的引用。这意味着,这与以下代码相同:

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

这也意味着取消设置 $var 不会取消设置全局变量。

添加注释

用户贡献的注释 8 个注释

21
BenBE at omorphia dot de
17 年前
嗨,

如果您想检查两个变量是否相互引用(即指向相同的内存),您可以使用以下函数

<?php

function same_type(&$var1, &$var2){
return
gettype($var1) === gettype($var2);
}

function
is_ref(&$var1, &$var2) {
//如果存在引用,则类型相同
if(!same_type($var1, $var2)) {
return
false;
}

$same = false;

//我们现在只需要询问 var1 是否是数组 ;-)
if(is_array($var1)) {
//在 $var1 中查找未使用的索引
do {
$key = uniqid("is_ref_", true);
} while(
array_key_exists($key, $var1));

//两个变量的内容不同...它们不可能相同
if(array_key_exists($key, $var2)) {
return
false;
}

//如果更改反映在 $var2 中,则数组指向相同的数据
$data = uniqid("is_ref_data_", true);
$var1[$key] =& $data;
//似乎进行了修改...
if(array_key_exists($key, $var2)) {
if(
$var2[$key] === $data) {
$same = true;
}
}

//撤消我们的更改...
unset($var1[$key]);
} elseif(
is_object($var1)) {
//相同的对象必须具有相同的类名 ;-)
if(get_class($var1) !== get_class($var2)) {
return
false;
}

$obj1 = array_keys(get_object_vars($var1));
$obj2 = array_keys(get_object_vars($var2));

//在 $var1 中查找未使用的索引
do {
$key = uniqid("is_ref_", true);
} while(
in_array($key, $obj1));

//两个变量的内容不同...它们不可能相同
if(in_array($key, $obj2)) {
return
false;
}

//如果更改反映在 $var2 中,则数组指向相同的数据
$data = uniqid("is_ref_data_", true);
$var1->$key =& $data;
//似乎进行了修改...
if(isset($var2->$key)) {
if(
$var2[$key] === $data) {
$same = true;
}
}

//撤消我们的更改...
unset($var1->$key);
} elseif (
is_resource($var1)) {
if(
get_resource_type($var1) !== get_resource_type($var2)) {
return
false;
}

return ((string)
$var1) === ((string) $var2);
} else {
//简单变量...
if($var1!==$var2) {
//数据不匹配...它们不可能相同...
return false;
}

//要检查简单类型的变量的引用
//只需存储其旧值并检查第二个变量的修改 ;-)

do {
$key = uniqid("is_ref_", true);
} while(
$key === $var1);

$tmp = $var1; //我们在这里需要一个副本!!!
$var1 = $key; //将 var1 设置为 $key 的值(副本)
$same = $var1 === $var2; //检查 $var2 是否也被修改了...
$var1 = $tmp; //撤消我们的更改...
}

return
$same;
}

?>

尽管此实现相当完整,但它无法处理函数引用和其他一些小问题。
此函数在您想手动序列化递归数组时特别有用。

用法类似于
<?php
$a
= 5;
$b = 5;
var_dump(is_ref($a, $b)); //false

$a = 5;
$b = $a;
var_dump(is_ref($a, $b)); //false

$a = 5;
$b =& $a;
var_dump(is_ref($a, $b)); //true
echo "---\n";

$a = array();
var_dump(is_ref($a, $a)); //true

$a[] =& $a;
var_dump(is_ref($a, $a[0])); // true
echo "---\n";

$b = array(array());
var_dump(is_ref($b, $b)); //true
var_dump(is_ref($b, $b[0])); //false
echo "---\n";

$b = array();
$b[] = $b;
var_dump(is_ref($b, $b)); //true
var_dump(is_ref($b, $b[0])); //false
var_dump(is_ref($b[0], $b[0][0])); //true*
echo "---\n";

var_dump($a);
var_dump($b);

?>

* 请注意 PHP 的内部行为,它似乎在实际复制变量之前就进行了引用赋值!因此,对于最后一个测试用例,您会得到一个包含(不同的)递归数组的数组,而不是您可能期望的包含空数组的数组。

BenBE。
7
ludvig dot ericson at gmail dot com
18 年前
为了清晰起见

$this 是一个伪变量 - 因此不是一个真正的变量。ZE 对它的处理方式与普通变量不同,这意味着某些高级变量操作将无法对其进行操作(出于显而易见的原因)

<?php
class Test {
var
$monkeys = 0;

function
doFoobar() {
$var = "this";
$
$var->monkeys++; // 这行代码将失败。
}
}

$obj = new Test;
$obj->doFoobar(); // 此调用将失败。
var_dump($obj->monkeys); // 如果它能到达这里,将返回 int(0)。
?>
3
ksamvel at gmail dot com
18 年前
可以通过简单的运算符 ==(object) 检查对任何对象的引用。示例

<?php
class A {}

$oA1 = new A();

$roA = & $oA1;

echo
"roA and oA1 are " . ( $roA == $oA1 ? "same" : "not same") . "<br>";

$oA2 = new A();
$roA = & $roA2;

echo
"roA and oA1 are " . ( $roA == $oA1 ? "same" : "not same") . "<br>";
?>

输出

roA and oA1 are same
roA and oA1 are not same

当前技术可能对对象缓存有用,当使用继承时,只需要复制扩展类的基本部分(类似于 C++:oB = oA)

<?php
class A {
/* 任何更改 A 状态的函数都应该将 $bChanged 设置为 true */
public function isChanged() { return $this->bChanged; }
private
$bChanged;
//...
}

class
B extends A {
// ...
public function set( &$roObj) {
if(
$roObj instanceof A) {
if(
$this->roAObj == $roObj &&
$roObj->isChanged()) {
/* 对象没有被修改,不需要复制 B 的 A 部分 */
} else {
/* 复制 B 的 A 部分 */
$this->roAObj = &$roObj;
}
}
}

private
$roAObj;
}
?>
3
Sergio Santana: ssantana at tlaloc dot imta dot mx
19 年前
有时需要对象的某个方法返回对自身的一个引用。这里有一个编码方法

<?php
class MyClass {
public
$datum;
public
$other;

function &
MyRef($d) { // 该方法
$this->datum = $d;
return
$this; // 返回引用
}
}

$a = new MyClass;
$b = $a->MyRef(25); // 创建引用

echo "This is object \$a: \n";
print_r($a);
echo
"This is object \$b: \n";
print_r($b);

$b->other = 50;

echo
"This is object \$a, modified" .
" indirectly by modifying ref \$b: \n";
print_r($a);
?>

这段代码输出
This is object $a
MyClass Object
(
[datum] => 25
[other] =>
)
This is object $b
MyClass Object
(
[datum] => 25
[other] =>
)
This is object $a, modified indirectly by modifying ref $b
MyClass Object
(
[datum] => 25
[other] => 50
)
1
Sergio Santana: ssantana at tlaloc dot imta dot mx
18 年前
*** 关于对象复杂引用的警告 ***
-----------------------------------------------
在类和对象上下文中的引用使用
尽管在文档中定义得很好,
但它在某种程度上很棘手,因此在使用
对象时必须非常小心。让我们检查以下两个
示例

<?php
y {
public
$d;
}

$A = new y;
$A->d = 18;
echo
"对象 \$A 操作前:\n";
var_dump($A);

$B = $A; // 这不是一个显式的 (=&) 引用赋值,
// 但是,$A 和 $B 指向同一个实例
// 尽管它们不是等效的名称
$C =& $A; // 显式引用赋值,$A 和 $C 指向
// 同一个实例,并且它们已成为等效
// 同一个实例的名称

$B->d = 1234;

echo
"\n对象 \$B 操作后:\n";
var_dump($B);
echo
"\n对象 \$A 操作后隐式修改:\n";
var_dump($A);
echo
"\n对象 \$C 操作后隐式修改:\n";
var_dump($C);

// 让 $A 指向另一个实例
$A = new y;
$A->d = 25200;
echo
"\n\$A 修改后对象 \$B:\n";
var_dump($B); // $B 不改变
echo "\n\$A 修改后对象 \$A:\n";
var_dump($A);
echo
"\n\$A 修改后对象 \$C 隐式修改:\n";
var_dump($C); // $C 随着 $A 的变化而变化
?>

因此,请注意赋值 $X = $Y 和 $X =& $Y 之间的区别。
当 $Y 除了对象实例之外的任何东西时,第一个赋值意味着
$X 将保存 $Y 的一个独立副本,第二个赋值意味着
$X 和 $Y 将引用同一个东西,因此它们紧密地绑定在一起,直到
要么 $X 要么 $Y 被迫引用另一个东西。但是,当 $Y
恰好是一个对象实例时,$X = $Y 的语义会发生变化,并且
变得与 $X =& $Y 只有很小的区别,因为在两种情况下
$X 和 $Y 都成为对同一个对象的引用。请查看此
示例输出

对象 $A 操作前
object(y)#1 (1) {
["d"]=>
int(18)
}

对象 $B 操作后
object(y)#1 (1) {
["d"]=>
int(1234)
}

对象 $A 操作后隐式修改
object(y)#1 (1) {
["d"]=>
int(1234)
}

对象 $C 操作后隐式修改
object(y)#1 (1) {
["d"]=>
int(1234)
}

\$A 修改后对象 $B
object(y)#1 (1) {
["d"]=>
int(1234)
}

\$A 修改后对象 $A
object(y)#2 (1) {
["d"]=>
int(25200)
}

\$A 修改后对象 $C 隐式修改
object(y)#2 (1) {
["d"]=>
int(25200)
}

让我们回顾一下第二个示例
<?php
yy {
public
$d;
function
yy($x) {
$this->d = $x;
}
}

function
modify($v)
{
$v->d = 1225;
}

$A = new yy(3);
var_dump($A);
modify($A);
var_dump($A);
?>

虽然,一般来说,一个形式参数声明为
在上面的 'modify' 函数中为 $v,意味着
实际参数 $A,在调用时传递
函数,不会被修改,但这不是
当 $A 是一个对象实例时的情况。请查看
执行时示例代码的输出

object(yy)#3 (1) {
["d"]=>
int(3)
}
object(yy)#3 (1) {
["d"]=>
int(1225)
}
1
CodeWorX.ch
13 年前
这里有一种非传统(而且速度不快)的方法来检测数组中的引用

<?php

function is_array_reference ($arr, $key) {
$isRef = false;
ob_start();
var_dump($arr);
if (
strpos(preg_replace("/[ \n\r]*/i", "", preg_replace("/( ){4,}.*(\n\r)*/i", "", ob_get_contents())), "[" . $key . "]=>&") !== false)
$isRef = true;
ob_end_clean();
return
$isRef;
}

?>
1
biziclop
4 个月前
这里还有另一个函数来检查两个变量是否相互引用。
BenBE 的函数工作正常,但它很长,可能很慢。
这是一个更短、更快,希望没有错误的替代方案。
至少从 PHP 4.3.0 到 8.3.4 工作:https://3v4l.org/5JNSl

<?php

function is_same_ref( & $a, & $b ){
// 保存原始值
$ori_a = $a;
$ori_b = $b;
// 赋予它们不同的值
$a = 1;
$b = 2;
// 比较它们,如果它们相等,它们就是相互的引用
$eq = $a === $b;
// 恢复原始值
$a = $ori_a;
$b = $ori_b;
return
$eq;
}

$a = array(1,2);
$b =& $a;
$c =& $a; // 检查副作用
var_dump( is_same_ref( $a, $b ));
// bool(true)

$a[] = 3;
$b[] = 4;
// 在 is_same_ref() 中以各种方式处理数组之后,
// 所有三个变量仍然引用同一个数组:
var_dump( implode( $a ), implode( $b ), implode( $c ));
// string(4) "3456"
// string(4) "3456"
// string(4) "3456"
?>
-1
Abimael Rodrguez Coln
13 年前
这是检查是否为引用的方法之一
<?php
$a
= 1;
$b =& $a;
$c = 2;
$d = 3;
$e = array($a);
function
is_reference($var){
$val = $GLOBALS[$var];
$tmpArray = array();
/**
* 添加键值对,不使用引用
*/
foreach($GLOBALS as $k => $v){
if(!
is_array($v)){
$tmpArray[$k] = $v;
}
}

/**
* 修改其余变量的值
*/
foreach($GLOBALS as $k => $v){
if(
$k != 'GLOBALS'
&& $k != '_POST'
&& $k != '_GET'
&& $k != '_COOKIE'
&& $k != '_FILES'
&& $k != $var
&& !is_array($v)
){
usleep(1);
$GLOBALS[$k] = md5(microtime());
}
}

$bool = $val != $GLOBALS[$var];

/**
* 恢复默认值
*/
foreach($tmpArray as $k => $v){
$GLOBALS[$k] = $v;
}

return
$bool;
}
var_dump(is_reference('a'));
var_dump(is_reference('b'));
var_dump(is_reference('c'));
var_dump(is_reference('d'));
?>

此代码不会检查数组中是否包含引用。
To Top