2011-01-27 18 views
23

¿Existe alguna forma en PHP para determinar si una variable dada es una referencia a otra variable y/o referenciada por otra variable? Aprecio que no sea posible separar la detección de "referencia a" y "referencia de" dado el comment en php.net que la configuración $a=& $b significa que "$ a y $ b son completamente iguales aquí. $ A no apunta a $ b o viceversa. $ a y $ b apuntan al mismo lugar. "Detectando si una variable de PHP es una referencia/referencia

Si no es posible determinar si una variable determinada es una referencia/referencia, ¿existe una forma generalizada de determinar si dos variables son referencias el uno del otro? De nuevo, un comment en php.net proporciona una función para realizar dicha comparación, aunque es una que implica editar una de las variables y ver si la otra variable se efectúa de manera similar. Prefiero evitar hacer esto si es posible, ya que algunas de las variables que estoy considerando hacen un uso intensivo de captadores/setters mágicos.

El fondo de la solicitud en esta instancia es escribir una función de depuración para ayudar a ver las estructuras en detalle.

+0

se puede comprobar si dos variables son referencias de uno al otro: http://stackoverflow.com/a/18110347/632951 – Pacerier

Respuesta

6

Puede utilizar 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 

esto, sin embargo, no va a funcionar más a partir de PHP 5.4, ya que eliminan pase duración de la llamada por el apoyo de referencia y puede generar un error E_STRICT nivel en las versiones inferiores.

Si te preguntas, de dónde viene el -4 en la función anterior: dime ... lo tengo intentando. En mi opinión, debería ser solo 3 (la variable, la variable en mi función, la variable pasó a zend_debug_zval), pero no soy muy bueno en el funcionamiento interno de PHP y parece que crea otra referencia en algún punto del camino;)

+1

' $ var = 'A'' es 1 '$ ref = &$var;' es 2, luego ' countRefs ($ var) 'es 3 y' debug_zval_dump (&$var); 'es 4 – Mchl

+1

@Mchl: El código con' $ reg = & $ var' dará '5', no' 4'. El '4' es el código sin el referido ence. Es por eso que no entiendo el número. – NikiC

1

Editar: Parece que he respondido a la pregunta '¿es posible comprobar si dos variables hacen referencia mismo valor en la memoria' no hizo la pregunta real. : P


En cuanto a las variables 'simples', la respuesta es 'no'.

En cuanto a los objetos, tal vez.

Todos los objetos se manejan por defecto por referencia. Además, cada objeto tiene su número de serie que puede ver cuando var_dump().

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

object(a)#12 (0) { 
} 

Si usted podría conseguir de alguna manera a este #, se podría comparar con eficacia por dos variables, y ver si apuntan al mismo objeto. La pregunta es cómo obtener este número. var_export() no lo devuelve. No veo nada en Reflection clases que lo obtengan tampoco.

Una cosa que viene a la mente está utilizando buffers de salida + expresiones regulares

+0

capturar la salida de 'var_export ($ variable, TRUE) 'entonces una regex menor debería capturar el número –

+0

@ KristofferSall-Storgaard: que solo funciona para' print_r() ' – calcinai

1

Haz un pico en xdebug_debug_zval(). En este momento, esa es la única manera de saber realmente si puedes determinar todo sobre el zval de la variable.

Así que aquí hay un par de funciones de ayuda para determinar alguna información útil:

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

Así, con algunas variables de la muestra:

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

podemos probar si una variable es una referencia:

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

Podemos obtener el número de variables relacionadas con el zval (no necesariamente una referencia, puede ser para la copia en escritura):

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

podemos probar si podemos copiar al escribir (copiar sin realizar una copia de la memoria):

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

Y podemos probar si podemos hacer una referencia sin copiar el zval:

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

Y ahora, podemos comprobar si una variable referencia a sí mismo a través de un poco de magia negro:

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

Es un poco hacky ya que no podemos determinar qué otros símbolos hacen referencia al mismo zval (solo esa otra referencia de símbolos). Así que esto básicamente verifica si $a es una referencia, y si $a y $b tienen el mismo refcount y conjunto de banderas de referencia. Luego, cambia uno para verificar si el otro cambia (lo que indica que son la misma referencia).

5

completo ejemplo de trabajo:

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 
Cuestiones relacionadas