在PHP中是否有办法确定给定变量是否是对另一个变量的引用和/或是否由另一个变量引用?我很感激,鉴于php.net上的comment设置$a=& $b
意味着“ $ a和$ b完全相同”,可能无法将检测“引用”和“引用”分开在这里等于。$ a没有指向$ b,反之亦然。$ a和$ b指向同一个地方。“
如果无法确定给定变量是否为引用/引用,是否有一种通用的方法来确定两个变量是否是彼此的引用?同样,php.net上的comment提供了一个用于进行这种比较的函数 - 尽管它涉及编辑其中一个变量并查看其他变量是否同样受影响。如果可能的话,我宁愿避免这样做,因为我正在考虑的一些变量大量使用魔法吸气剂/设定者。
此实例中请求的背景是编写调试函数以帮助详细查看结构。
答案 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
是否都设置了相同的引用和引用标志。然后,它改变一个以检查另一个是否改变(表明它们是相同的参考)。