在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
两者都具有相同的引用计数和引用标志集。然后,它更改一个以检查其他是否更改(表明它们是相同的参考)。