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

发布于 2024-10-14 17:48:46 字数 554 浏览 7 评论 0原文

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

如果无法确定给定变量是否是引用/被引用,是否有通用方法来确定两个变量是否相互引用?同样,php.net 上的 注释 提供了一个函数这样的比较 - 尽管它涉及编辑一个变量并查看另一个变量是否受到类似的影响。如果可能的话,我宁愿避免这样做,因为我正在考虑的一些变量大量使用了魔术 getters/setters。

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

Is there a way in PHP to determine whether a given variable is a reference to another variable and / or referenced by another variable? I appreciate that it might not be possible to separate detecting "reference to" and "reference from" given the comment on php.net that setting $a=& $b means "$a and $b are completely equal here. $a is not pointing to $b or vice versa. $a and $b are pointing to the same place."

If it's not possible to determine whether a given variable is a reference / referenced, is there a generalised way of determining if two variables are references of each other? Again, a comment on php.net supplies a function for doing such a comparison - although it is one that involves editing one of the variables and seeing if the other variable is similarly effected. I'd rather avoid doing this if possible since some of the variables I'm considering make heavy use of magic getters / setters.

The background to the request in this instance is to write a debugging function to help view structures in detail.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

梦醒灬来后我 2024-10-21 17:48:46

您可以使用 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 内部,似乎它又创建了另一个途中某处参考;)

You can use 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

This though will not work anymore as of PHP 5.4 as they removed call time pass by reference support and may throw an E_STRICT level error on lower versions.

If you wonder, where the -4 in the above function come from: You tell me... I got it by trying. In my eyes it should be only 3 (the variable, the variable in my function, the variable passed to zend_debug_zval), but I'm not too good at PHP internals and it seems that it creates yet another reference somewhere on the way ;)

轻许诺言 2024-10-21 17:48:46

完整的工作示例:

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

Full working example:

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
治碍 2024-10-21 17:48:46

也许 xdebug_debug_zval() 对您有帮助。 http://www.xdebug.org/docs/all_functions

Maybe xdebug_debug_zval() helps you. http://www.xdebug.org/docs/all_functions

说不完的你爱 2024-10-21 17:48:46

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;
}

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

Take a peak at xdebug_debug_zval(). Right now, that's the only way to really know if you can determine everything about the variable's zval.

So here are a couple of helper functions to determine some helpful information:

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]);
}

So with some sample variables:

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

We can test if a variable is a reference:

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

We can get the number of variables linked to the zval (not necessarily a reference, can be for copy-on-write):

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

We can test if we can copy-on-write (copy without performing a memory copy):

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

And we can test if we can make a reference without copying the zval:

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

And now, we can check if a variable references itself through some black magic:

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;
}

It's a bit hacky since we can't determine what other symbols reference the same zval (only that other symbols reference). So this basically checks to see if $a is a reference, and if $a and $b both have the same refcount and reference flag set. Then, it changes one to check if the other changes (indicating they are the same reference).

苏大泽ㄣ 2024-10-21 17:48:46

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


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

就物体而言——也许吧。

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

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

object(a)#12 (0) {
}

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

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

Edit:
It seems I've answered the question 'is it possible to check if two variables are referencing same value in memory' not the actual question asked. :P


As far as 'plain' variables go the answer is 'no'.

As far as objects go - maybe.

All objects are by default handled by references. Also each object has it's serial number which you can see when you var_dump() it.

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

object(a)#12 (0) {
}

If you could get somehow to this #, you could effectively compare it for two variables, and see if they point to the same object. The question is how to get this number. var_export() does not return it. I don't see snything in Reflection classes that would get it either.

One thing that comes to my mind is using output buffering + regex

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文