Определение, является ли переменная PHP ссылочной или ссылочной - PullRequest
24 голосов
/ 27 января 2011

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

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

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

Ответы [ 5 ]

6 голосов
/ 27 января 2011

Вы можете использовать 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, и кажется, что он создает еще одну ссылку где-то на пути;)

5 голосов
/ 07 августа 2013

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

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 голосов
/ 27 января 2011

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

1 голос
/ 20 октября 2011

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

1 голос
/ 27 января 2011

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


Что касается "простых" переменных, то ответом будет "нет".

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

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

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

object(a)#12 (0) {
}

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

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

...