检测PHP变量是否为引用/引用

时间:2011-01-27 14:28:31

标签: php php-internals

在PHP中是否有办法确定给定变量是否是对另一个变量的引用和/或是否由另一个变量引用?我很感激,鉴于php.net上的comment设置$a=& $b意味着“ $ a和$ b完全相同”,可能无法将检测“引用”和“引用”分开在这里等于。$ a没有指向$ b,反之亦然。$ a和$ b指向同一个地方。

如果无法确定给定变量是否为引用/引用,是否有一种通用的方法来确定两个变量是否是彼此的引用?同样,php.net上的comment提供了一个用于进行这种比较的函数 - 尽管它涉及编辑其中一个变量并查看其他变量是否同样受影响。如果可能的话,我宁愿避免这样做,因为我正在考虑的一些变量大量使用魔法吸气剂/设定者。

此实例中请求的背景是编写调试函数以帮助详细查看结构。

5 个答案:

答案 0 :(得分:6)

您可以使用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内部并且它似乎在某处创建了另一个引用方式;)

答案 1 :(得分:5)

完整的工作示例:

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

答案 2 :(得分:3)

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

答案 3 :(得分:1)

修改 似乎我已经回答了这个问题'是否可以检查两个变量是否在内存中引用相同的值'而不是实际问题。 :P


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

就物体而言 - 也许。

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

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

object(a)#12 (0) {
}

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

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

答案 4 :(得分: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的变量数量(不一定是引用,可以是写入时复制):

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(只有其他符号引用),因此有点hacky。因此,这基本上会检查$a是否为引用,以及$a$b是否都设置了相同的引用和引用标志。然后,它改变一个以检查另一个是否改变(表明它们是相同的参考)。