Обнаружение того, является ли переменная PHP ссылкой / ссылкой

Есть ли способ в PHP определить, является ли данная переменная ссылкой на другую переменную и / или ссылается на другую переменную? Я понимаю, что было бы невозможно разделить обнаружение «ссылки на» и «ссылку из», учитывая комментарий на php.net, что установка $a=& $b означает, что $ a и $ b здесь полностью равны. не указывая на $ b или наоборот. $ a и $ b указывают на то же место ".

Если невозможно определить, является ли данная переменная ссылкой / ссылкой, существует ли обобщенный способ определения, являются ли две переменные ссылками друг на друга? Опять же, комментарий к php.net предоставляет функцию для такого сравнения – хотя это один из них, который включает в себя редактирование одной из переменных и наблюдение, аналогично ли выполняется другая переменная. Я бы предпочел не делать этого, если это возможно, поскольку некоторые из переменных, которые я рассматриваю, сильно используют магические геттеры / сеттеры.

Фон для запроса в этом экземпляре – это написать функцию отладки, чтобы подробно просматривать структуры.

Вы можете использовать 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 ошибку уровня E_STRICT в более низких версиях.

Если вы задаетесь вопросом, откуда -4 функция -4 в приведенной выше функции: скажите мне … Я попробовал. На мой взгляд, это должно быть только 3 (переменная, переменная в моей функции, переменная, переданная в zend_debug_zval ), но я не слишком хорош для внутренних внутренних zend_debug_zval PHP и кажется, что она создает еще одну ссылку где-то в пути;)

Полный рабочий пример:

 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 

Может быть, xdebug_debug_zval () поможет вам. http://www.xdebug.org/docs/all_functions

Edit: Кажется, я ответил на вопрос: «Можно ли проверить, ссылаются ли две переменные на одно значение в памяти», а не на заданный вопрос. :П


Что касается «простых» переменных, то ответ «нет».

Что касается объектов – может быть.

Все объекты по умолчанию обрабатываются ссылками. Также каждый объект имеет его серийный номер, который вы можете видеть, когда вы его var_dump() .

 >> class a {}; >> $a = new a(); >> var_dump($a); object(a)#12 (0) { } 

Если бы вы могли каким-то образом перейти к этому #, вы могли бы эффективно сравнить его для двух переменных и посмотреть, указывают ли они на один и тот же объект. Вопрос в том, как получить это число. var_export() не возвращает его. Я не вижу snything в классах Reflection которые тоже получат его.

Одна вещь, которая приходит мне на ум, заключается в использовании буферизации вывода + регулярное выражение

Возьмите пик на 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 (только на ссылку других символов). Таким образом, это в основном проверяет, является ли $a ссылкой, и если $a и $b имеют одинаковый набор ссылок и набор ссылок. Затем он меняет один, чтобы проверить, изменилась ли другая (указывая, что они являются одной и той же ссылкой).