2011-01-27 3 views
23

C'è un modo in PHP per determinare se una data variabile è un riferimento a un'altra variabile e/o di riferimento da un'altra variabile? Apprezzo che potrebbe non essere possibile separare rilevare "riferimento" e "di riferimento da" data la comment su php.net che modificando $a=& $b significa "$ a e $ b sono completamente uguali. $ A non punta a $ b o viceversa. $ a e $ b puntano allo stesso posto. "rilevare se una variabile PHP è un riferimento/riferimento

Se non è possibile determinare se una data variabile è un riferimento/riferimento, c'è un modo generalizzato di determinare se due variabili sono riferimenti l'uno dell'altro? Ancora una volta, un comment su php.net fornisce una funzione per fare un simile confronto - sebbene sia uno che comporta la modifica di una delle variabili e vedere se l'altra variabile viene similmente effettuata. Preferirei evitare di farlo, se possibile, dal momento che alcune delle variabili che sto prendendo in considerazione fanno un uso pesante di getter/setter magici.

Lo sfondo alla richiesta in questo caso è quello di scrivere una funzione di debug per aiutare le strutture vista in dettaglio.

+0

È possibile verificare se due variabili sono riferimenti di ogni altro: http://stackoverflow.com/a/18110347/632951 – Pacerier

risposta

6

È possibile utilizzare 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 

Questo però non funziona più come PHP 5.4 come hanno tolto chiamata passare il tempo con il supporto di riferimento e può gettare un errore E_STRICT livello sulle versioni inferiori.

Se vi chiedete, dove il -4 nella funzione di cui sopra provengono da: Lei mi dice ... l'ho presa da provare. Ai miei occhi dovrebbe essere solo 3 (la variabile, la variabile nella mia funzione, la variabile passata a zend_debug_zval), ma non sono troppo bravo a internals PHP e sembra che crei ancora un altro riferimento da qualche parte sulla strada;)

+1

' $ var = 'A'' è 1 '$ ref = &$var;' è 2, quindi ' countRefs ($ var) 'è 3 e' debug_zval_dump (&$var); 'è 4 – Mchl

+1

@Mchl: il codice con' $ reg = & $ var' darà '5', non' 4'. Il '4' è il codice senza il riferimento ENCE. Ecco perché non ottengo il numero. – NikiC

1

Edit: Sembra che ho risposto alla domanda 'è possibile controllare se due variabili fanno riferimento stesso valore in memoria' non la questione reale chiese. : P


Per quanto riguarda le variabili "semplici", la risposta è "no".

Per quanto riguarda gli oggetti, forse.

Tutti gli oggetti sono di default gestite da riferimenti. Inoltre ogni oggetto ha il suo numero di serie che puoi vedere quando lo hai var_dump().

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

object(a)#12 (0) { 
} 

Se si potesse ottenere in qualche modo a questo #, è possibile confrontare in modo efficace per due variabili, e vedere se puntano allo stesso oggetto. La domanda è come ottenere questo numero. var_export() non lo restituisce. Non riesco a vedere nulla nelle classi Reflection.

Una cosa che mi viene in mente sta usando il buffering + regex

+0

catturare l'output da 'var_export ($ variabile, TRUE) 'allora una regex minore dovrebbe acquisire il numero –

+0

@ KristofferSall-Storgaard: che funziona solo per' print_r() ' – calcinai

1

dare uno sguardo a xdebug_debug_zval(). In questo momento, questo è l'unico modo per sapere davvero se è possibile determinare tutto sullo zval della variabile.

Così qui sono un paio di funzioni di supporto per determinare alcune informazioni utili:

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

Quindi, con alcune variabili del campione:

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

possiamo testare se una variabile è un riferimento:

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

Possiamo ottenere il numero di variabili collegate allo zval (non necessariamente un riferimento, può essere per copy-on-write):

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

possiamo testare se siamo in grado di copy-on-write (copia senza eseguire una copia di memoria):

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

E possiamo verificare se siamo in grado di fare un riferimento senza copiare lo zval:

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

E ora, siamo in grado di controllare se una variabile si riferimenti attraverso qualche magia nera:

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

È un po 'hacky poiché non è possibile determinare quali altri simboli fanno riferimento allo stesso zval (solo gli altri simboli di riferimento). Quindi questo in pratica controlla se $a è un riferimento e se $a e $b hanno entrambi lo stesso numero di punti di riferimento e di riferimento. Quindi, ne cambia uno per verificare se le altre modifiche (indicando che sono lo stesso riferimento).

5

completa esempio funzionante:

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