Обнаружение того, является ли переменная PHP ссылкой/ссылкой
Есть ли способ в PHP определить, является ли данная переменная ссылкой на другую переменную и/или ссылается на другую переменную? Я ценю, что было бы невозможно разделить обнаружение "ссылки на" и "ссылку из" с учетом комментарий на php.net, что установка $a=& $b
означает, что $a и $b здесь полностью равны. $a не указывает на $b или наоборот. $a и $b указывают на одно и то же место. "
Если невозможно определить, является ли данная переменная ссылкой/ссылкой, существует ли обобщенный способ определения, являются ли две переменные ссылками друг на друга? Опять же, комментарий на php.net предоставляет функцию для такого сравнения - хотя это один из них, который включает в себя редактирование одной из переменных и просмотр if аналогична реализация другой переменной. Я бы предпочел не делать этого, если это возможно, поскольку некоторые из переменных, которые я рассматриваю, сильно используют магические геттеры/сеттеры.
Фон для запроса в этом экземпляре - это написать функцию отладки, чтобы подробно просмотреть структуры.
Ответы
Ответ 1
Вы можете использовать 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, и кажется, что она создает еще одну ссылку где-то в пути;)
Ответ 2
Полный рабочий пример:
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
Ответ 3
Может быть, xdebug_debug_zval() поможет вам. http://www.xdebug.org/docs/all_functions
Ответ 4
Edit:
Кажется, я ответил на вопрос: "Можно ли проверить, ссылаются ли две переменные на одно значение в памяти", а не на заданный вопрос.: P
Насколько "простые" переменные идут, ответ "нет".
Что касается объектов - возможно.
Все объекты по умолчанию обрабатываются ссылками. Также каждый объект имеет его серийный номер, который вы можете видеть, когда вы var_dump()
его.
>> class a {};
>> $a = new a();
>> var_dump($a);
object(a)#12 (0) {
}
Если бы вы могли каким-то образом перейти на этот #, вы могли бы эффективно сравнить его для двух переменных и посмотреть, указывают ли они на один и тот же объект. Вопрос в том, как получить это число. var_export()
не возвращает его. Я не вижу snything в классах Reflection
, которые тоже получат его.
Одна вещь, которая приходит мне на ум, заключается в использовании буферизации вывода + регулярное выражение
Ответ 5
Возьмите пик 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
имеют одинаковый набор значений refcount и reference. Затем он меняет один, чтобы проверить, изменилась ли другая (указывая, что они являются одной и той же ссылкой).