funciones - variables globales php
Detectando si una variable PHP es una referencia/referencia (5)
¿Hay 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 al establecer $a=& $b
significa que " $ a y $ b son completamente iguales aquí. $ A es no apuntando a $ b o viceversa. $ a y $ b apuntan al mismo lugar " .
Si no es posible determinar si una variable determinada es una referencia / referencia, ¿existe una forma generalizada de determinar si dos variables son referencias entre sí? De nuevo, un comment en php.net proporciona una función para hacer tal 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.
Haga 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 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]);
}
Entonces con algunas variables de 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 vinculadas al zval (no necesariamente una referencia, puede ser para copiar-escribir):
getRefCount(''a''); // 2
getRefCount(''c''); // 2
getRefCount(''e''); // 1
Podemos probar si podemos copiar por escritura (copiar sin realizar una copia de 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 verificar si una variable se refiere a sí misma a través de alguna magia negra:
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 el mismo marcador de referencia. Luego, cambia uno para verificar si el otro cambia (lo que indica que son la misma referencia).
Puede usar 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, ya no funcionará a partir de PHP 5.4 ya que eliminaron el tiempo de paso de llamada por soporte de referencia y pueden arrojar un error de nivel E_STRICT
en 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 pasada 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;)
Tal vez xdebug_debug_zval () te ayude. http://www.xdebug.org/docs/all_functions
Editar: Parece que he respondido a la pregunta ''¿es posible verificar si dos variables hacen referencia al mismo valor en la memoria'', no la pregunta real? :PAG
En cuanto a las variables ''simples'', la respuesta es ''no''.
En cuanto a los objetos, tal vez.
Todos los objetos son manejados por referencia por defecto. Además, cada objeto tiene su número de serie que puede ver cuando lo var_dump()
.
>> class a {};
>> $a = new a();
>> var_dump($a);
object(a)#12 (0) {
}
Si pudieras obtener de alguna manera este #, podrías compararlo efectivamente para 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 las clases de Reflection
que lo haga tampoco.
Una cosa que me viene a la mente es usar buffering de salida + regex
Ejemplo completo 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