2011-01-27 19 views
23

Czy istnieje sposób w PHP, aby określić, czy dana zmienna jest odniesieniem do innej zmiennej i/lub odwołuje się do innej zmiennej? Rozumiem, że może nie być możliwe oddzielenie wykrycia "odniesienia do" i "odniesienia od", biorąc pod uwagę comment na php.net, że ustawienie $a=& $b oznacza "$ a i $ b są całkowicie równe tutaj. $ A nie wskazuje na $ b lub odwrotnie. $ a i $ b wskazują na to samo miejsce: "Wykrywanie, czy zmienną PHP jest odwołanie/odniesienie

Jeśli nie można ustalić, czy dana zmienna jest referencją/odniesieniem, czy istnieje uogólniony sposób określania, czy dwie zmienne są odniesienia do siebie nawzajem? Ponownie, comment na php.net dostarcza funkcję do takiego porównywania - choć jest to takie, które wymaga edycji jednej ze zmiennych i sprawdzenia, czy inna zmienna jest podobnie wykonana. Wolałbym tego unikać, jeśli to możliwe, ponieważ niektóre zmienne, które rozważam, intensywnie używają magicznych programów pobierających/ustawiających.

Tło dla żądania w tym przypadku polega na napisaniu funkcji debugowania, aby ułatwić szczegółowe wyświetlanie struktur.

+0

Można sprawdzić, czy dwie zmienne są odniesienia do siebie: http://stackoverflow.com/a/18110347/632951 – Pacerier

Odpowiedz

6

Można 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 już pracować w PHP 5.4, ponieważ usunięto rozmowę czas płynął wsparcie odniesienia i może rzucać błąd E_STRICT poziom na niższych wersjach.

Jeśli zastanawiasz się, skąd pochodzi -4 w powyższej funkcji: Powiedz mi ... Dostałem to próbując. W moich oczach powinno to być tylko 3 (zmienna, zmienna w mojej funkcji, zmienna przekazywana do zend_debug_zval), ale nie jestem zbyt dobra w internach PHP i wydaje się, że tworzy ona kolejne odniesienie gdzieś po drodze;)

+1

' $ var = 'A'' to 1 '$ ref = &$var;' to 2, następnie ' countRefs ($ var) 'is 3 i' debug_zval_dump (&$var); 'jest 4 – Mchl

+1

@Mchl: Kod z' $ reg = & $ var' da '5', a nie' 4'. '4' jest kodem bez polecam ence. Właśnie dlatego nie dostaję tego numeru. – NikiC

1

Edit: Wydaje mam odpowiedzi na pytanie: „Czy to możliwe, aby sprawdzić, czy dwie zmienne są przedstawieniu samą wartość w pamięci” nie poprosił rzeczywista pytanie. : P


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

Jeśli chodzi o obiekty - może.

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

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

object(a)#12 (0) { 
} 

Jeśli udało ci się jakoś to zrobić, możesz efektywnie porównać go dla dwóch zmiennych i sprawdzić, czy wskazują na ten sam obiekt. Pytanie brzmi, jak zdobyć ten numer. var_export() nie zwraca go. Nie widzę nic w klasach Reflection, które mogłyby je uzyskać.

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

+0

przechwycić wyjście z 'var_export ($ zmienna, PRAWDA) 'następnie niewielkie wyrażenie powinno przechwycić numer –

+0

@ KristofferSall-Storgaard: to działa tylko dla' print_r() ' – calcinai

1

Skup się na xdebug_debug_zval(). W tej chwili jest to jedyny sposób, aby naprawdę wiedzieć, czy możesz ustalić wszystko na temat zmiennej zval.

Więc oto kilka funkcji pomocniczych w celu określenia kilka 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 niektórych zmiennych próbki:

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

Możemy sprawdzić, czy zmienna jest odniesienie:

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

Możemy uzyskać liczbę zmiennych powiązanych z obiektem zval (niekoniecznie odniesienie, może być na kopiowanie przy zapisie):

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

Możemy przetestować czy możemy kopiowanie przy zapisie (copy 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 

i teraz, możemy sprawdzić czy zmienna odniesienia się 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; 
} 

To trochę hacky, ponieważ nie możemy określić, co inne symbole odnoszą się do tego samego zera (tylko inne odniesienia do symboli). Tak więc to w zasadzie sprawdza, czy $a jest odniesieniem, a jeśli oba są $a i $b mają ten sam zestaw flag referencji i odniesienia. Następnie zmienia jeden, aby sprawdzić, czy inne zmiany (wskazując, że są to te same odniesienia).

5

Pełny przykład roboczych:

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 
Powiązane problemy