Wykrywanie, czy zmienna PHP jest referencją / odniesieniem

Czy istnieje sposób w PHP, aby określić, czy dana zmienna jest odwołaniem do innej zmiennej i / lub odwołaniem do innej zmiennej? Doceniam, że nie da się oddzielić "referencji od" I "referencji od" biorąc pod uwagę komentarz na php.net to ustawienie $a=& $b oznacza " $A i $b są tu całkowicie równe. $a nie wskazuje na $b lub odwrotnie. $a i $ b wskazują na to samo miejsce."

Jeśli nie jest możliwe ustalenie, czy podana zmienna jest odniesieniem / odniesieniem, czy istnieje ogólny sposób określania, czy dwie zmienne są odniesieniami do siebie? Znowu komentarz na php.net dostarcza funkcję do wykonania takiego porównania-chociaż jest to taka, która polega na edycji jednej ze zmiennych i sprawdzaniu, czy druga zmienna jest podobnie wykonywana. Wolałbym tego uniknąć, jeśli to możliwe, ponieważ niektóre zmienne, które rozważam, wykorzystują w dużym stopniu magiczne gettery / settery.

Tło do żądanie w tym przypadku polega na napisaniu funkcji debugowania, aby pomóc w szczegółowym wyświetlaniu struktur.

Author: Pacerier, 2011-01-27

5 answers

Możesz użyć 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

To jednak nie będzie działać już od PHP 5.4, ponieważ usunięto Przejście czasu wywołania przez obsługę referencji i może spowodować błąd poziomu E_STRICT w niższych wersjach.

Jeśli zastanawiasz się, skąd pochodzi -4 w powyższej funkcji: powiedz mi... Próbowałem. W moich oczach powinno być tylko 3 (zmienna, zmienna w mojej funkcji, zmienna przekazana do zend_debug_zval), ale nie jestem zbyt dobry w PHP i wydaje się, że tworzy jeszcze kolejna Referencja gdzieś po drodze;)

 6
Author: NikiC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-20 14:39:08

Pełny przykład roboczy:

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
 5
Author: Pacerier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-07 17:54:53

Może xdebug_debug_zval () ci pomoże. http://www.xdebug.org/docs/all_functions

 3
Author: osm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-27 14:33:59

Edit: Wygląda na to, że odpowiedziałem na pytanie "czy można sprawdzić, czy dwie zmienne odwołują się do tej samej wartości w pamięci", a nie na zadawane pytanie. : P


Jeśli chodzi o zmienne "zwykłe", odpowiedź brzmi "nie".

Jeśli chodzi o przedmioty-może.

Wszystkie obiekty są domyślnie obsługiwane przez referencje. Również każdy obiekt ma swój numer seryjny, który możesz zobaczyć, gdy var_dump() to.

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

object(a)#12 (0) {
}

Jeśli można dostać się jakoś do tego#, można skutecznie porównaj go dla dwóch zmiennych i sprawdź, czy wskazują na ten sam obiekt. Pytanie brzmi, jak zdobyć ten numer. var_export() nie zwraca go. Nie widzę nic w Reflection klasach, które też by to dostały.

Jedna rzecz, która przychodzi mi do głowy, to użycie buforowania wyjścia + regex

 1
Author: Mchl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-27 14:54:47

Weź szczyt w xdebug_debug_zval () . W tej chwili, to jedyny sposób, aby naprawdę wiedzieć, czy można określić wszystko o zmiennej zval.

Oto kilka funkcji pomocniczych do określenia przydatnych informacji:

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]);
}

Więc z przykładowymi zmiennymi:

$a = 'test';
$b = $a;
$c = $b;
$d =& $c;
$e = 'foo';

Możemy sprawdzić, czy zmienna jest referencją:

isRef('a'); // false
isRef('c'); // true
isRef('e'); // false

Możemy uzyskać liczbę zmiennych powiązanych z zval (niekoniecznie referencją, może być dla copy-on-write):

getRefCount('a'); // 2
getRefCount('c'); // 2
getRefCount('e'); // 1

Możemy sprawdzić, czy możemy kopiować przy zapisie (kopiować bez wykonywania kopii pamięci):

canCopyOnWrite('a'); // true
canCopyOnWrite('c'); // false
canCopyOnWrite('e'); // true

I możemy sprawdzić, czy możemy zrobić odniesienie bez kopiowania zval:

canReferenceWithoutCopy('a'); // false
canReferenceWithoutCopy('c'); // true
canReferenceWithoutCopy('e'); // true

A teraz możemy sprawdzić, czy zmienna odwołuje się do siebie poprzez jakąś czarną magię:

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;
}

Jest to trochę trudne, ponieważ nie możemy określić, jakie inne symbole odwołują się do tego samego zval (tylko, że inne symbole odwołują się). Więc to w zasadzie sprawdza, czy $a jest reference, oraz if $a i $b mają ustawiony taki sam znacznik refcount i reference. Następnie zmienia jeden, aby sprawdzić, czy inne się zmieniają(wskazując, że są tym samym odniesieniem).

 1
Author: ircmaxell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-20 15:33:57