26

PHP中有没有办法确定给定变量是否是对另一个变量的引用和/或被另一个变量引用?我明白,鉴于php.net 上的评论$a=& $b,设置意味着“ $a 和 $b 在这里完全相等。$a 没有指向 $b 或反之亦然。$a 和 $b 指向同一个地方。

如果无法确定给定变量是否为引用/被引用,是否有一种通用方法可以确定两个变量是否相互引用?同样,在 php.net 上的评论提供了进行这种比较的功能 - 尽管它涉及编辑其中一个变量并查看另一个变量是否受到类似影响。如果可能的话,我宁愿避免这样做,因为我正在考虑的一些变量会大量使用魔法 getter/setter。

在这个实例中请求的背景是编写一个调试函数来帮助详细查看结构。

4

5 回答 5

8

完整的工作示例:

function EqualReferences(&$first, &$second){
    if($first !== $second){
        return false;
    } 
    $value_of_first = $first;
    $first = ($first === true) ? false : true; // modify $first
    $is_ref = ($first === $second); // after modifying $first, $second will not be equal to $first, unless $second and $first points to the same variable.
    $first = $value_of_first; // unmodify $first
    return $is_ref;
}

$a = array('foo');
$b = array('foo');
$c = &$a;
$d = $a;

var_dump(EqualReferences($a, $b)); // false
var_dump(EqualReferences($b, $c)); // false
var_dump(EqualReferences($a, $c)); // true
var_dump(EqualReferences($a, $d)); // false
var_dump($a); // unmodified
var_dump($b); // unmodified
于 2013-08-07T17:54:53.703 回答
7

您可以使用debug_zval_dump

function countRefs(&$var) {
    ob_start();
    debug_zval_dump(&$var);
    preg_match('~refcount\((\d+)\)~', ob_get_clean(), $matches);
    return $matches[1] - 4;
}

$var = 'A';
echo countRefs($var); // 0

$ref =& $var;
echo countRefs($var); // 1

但从 PHP 5.4 开始,这将不再起作用,因为它们删除了通过引用传递的调用时间支持,并且可能会E_STRICT在较低版本上引发级别错误。

如果你想知道,-4上面的函数来自哪里:你告诉我......我通过尝试得到它。在我看来,它应该只有 3(变量、我的函数中的变量、传递给的变量zend_debug_zval),但我不太擅长 PHP 内部结构,似乎它在途中的某个地方创建了另一个引用;)

于 2011-01-27T15:00:33.567 回答
4

也许 xdebug_debug_zval() 可以帮助你。http://www.xdebug.org/docs/all_functions

于 2011-01-27T14:33:59.933 回答
1

在xdebug_debug_zval()达到顶峰。现在,这是真正知道您是否可以确定有关变量 zval 的所有内容的唯一方法。

所以这里有几个帮助函数来确定一些有用的信息:

function isRef($var) {
    $info = getZvalRefCountInfo($var);
    return (boolean) $info['is_ref'];
}
function getRefCount($var) {
    $info = getZvalRefCountInfo($var);
    return $info['refcount'];
}
function canCopyOnWrite($var) {
    $info = getZvalRefCountInfo($var);
    return $info['is_ref'] == 0;
}
function canReferenceWithoutCopy($var) {
    $info = getZvalRefCountInfo($var);
    return $info['is_ref'] == 1 || $info['refcount'] == 1;
}

function getZvalRefCountInfo($var) {
    ob_start();
    xdebug_debug_zval($var);
    $info = ob_get_clean();
    preg_match('(: \(refcount=(\d+), is_ref=(\d+)\))', $info, $match);
    return array('refcount' => $match[1], 'is_ref' => $match[2]);
}

所以有一些样本变量:

$a = 'test';
$b = $a;
$c = $b;
$d =& $c;
$e = 'foo';

我们可以测试一个变量是否是一个引用:

isRef('a'); // false
isRef('c'); // true
isRef('e'); // false

我们可以得到链接到 zval 的变量的数量(不一定是引用,可以用于 copy-on-write):

getRefCount('a'); // 2
getRefCount('c'); // 2
getRefCount('e'); // 1

我们可以测试我们是否可以写时复制(复制而不执行内存复制):

canCopyOnWrite('a'); // true
canCopyOnWrite('c'); // false
canCopyOnWrite('e'); // true

我们可以测试是否可以在不复制 zval 的情况下进行引用:

canReferenceWithoutCopy('a'); // false
canReferenceWithoutCopy('c'); // true
canReferenceWithoutCopy('e'); // true

现在,我们可以通过一些黑魔法来检查一个变量是否引用了自己:

function isReferenceOf(&$a, &$b) {
    if (!isRef('a') || getZvalRefCountInfo('a') != getZvalRefCountInfo('b')) {
        return false;
    }
    $tmp = $a;
    if (is_object($a) || is_array($a)) {
        $a = 'test';
        $ret = $b === 'test';
        $a = $tmp;
    } else {
        $a = array();
        $ret = $b === array();
        $a = $tmp;
    }
    return $tmp;
}

这有点棘手,因为我们无法确定哪些其他符号引用了相同的 zval(只有其他符号引用)。所以这基本上检查是否$a是一个引用,如果$a$b两者都具有相同的引用计数和引用标志集。然后,它更改一个以检查其他是否更改(表明它们是相同的参考)。

于 2011-10-20T15:15:51.197 回答
1

编辑: 似乎我已经回答了“是否可以检查两个变量是否在内存中引用相同的值”这个问题,而不是实际提出的问题。:P


就“普通”变量而言,答案是“不”。

就物体而言 - 也许。

默认情况下,所有对象都由引用处理。此外,每个对象都有它的序列号,你可以看到var_dump()它。

>> class a {};
>> $a = new a();
>> var_dump($a);

object(a)#12 (0) {
}

如果你能以某种方式得到这个#,你就可以有效地比较两个变量,看看它们是否指向同一个对象。问题是如何获得这个数字。var_export()不返回它。我也没有在Reflection课堂上看到任何能得到它的东西。

我想到的一件事是使用输出缓冲+正则表达式

于 2011-01-27T14:31:50.900 回答