2011-01-27 7 views
23

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

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

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

+0

Вы можете проверить, если две переменные ссылки друг друга: http://stackoverflow.com/a/18110347/632951 – Pacerier

ответ

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

'$ var = 'A'' является 1' $ ref = &$var; 'равно 2, затем' countRefs ($ var) 'равно 3 и' debug_zval_dump (&$var); 'is 4 – Mchl

+1

@Mchl: Код с' $ reg = & $ var' будет давать '5', а не' 4'. '4' - это код без ссылка ENCE. Вот почему я не получаю номер. – NikiC

1

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


Насколько «простые» переменные идут, ответ «нет».

Что касается объектов - возможно.

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

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

object(a)#12 (0) { 
} 

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

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

+0

захватить выход из 'var_export ($ переменная, TRUE), то второстепенное регулярное выражение должно записывать номер –

+0

@ KristofferSall-Storgaard: это работает только для 'print_r()' – calcinai

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

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 
Смежные вопросы