php - special - string strip_tags
Jugando con referencias (2)
Es un comportamiento más o menos definido (pero a veces no documentado); principalmente porque $a no es una array sino un ArrayObject .
Veamos primero tu tercer fragmento de código:
$a = new ArrayObject();
$a[''ID''] = 42;
$b = &$a[''ID''];
$c = $a;
$c[''ID''] &= 0;
La última asignación se traduce en:
$tmp = &$c->offsetGet(''ID'');
$tmp &= 0; // or: $tmp = $tmp & 0;
El punto de offsetGet() aquí solo se llama offsetGet() y devuelve una referencia a $c[''ID''] , como se indica en este comentario . Debido a que offsetSet() no se llama, el valor de $b cambia.
Por cierto, el incremento (++) y el operador de decremento (-) funcionan de manera similar, no se llama a offsetSet() .
Diferencias
Esto es diferente de tu primer ejemplo:
$a = new ArrayObject();
$a[''ID''] = 42;
$b = &$a[''ID''];
$c = $a;
$c[''ID''] = 37;
La última declaración tiene el siguiente equivalente:
$c->offsetSet(''ID'', 37);
Antes de asignar un nuevo valor a $c[''ID''] , el valor anterior es unset() ; esta es la razón por la que $b es la única variable que aún se mantiene en 42 .
Se puede ver una prueba de este comportamiento cuando usa objetos en lugar de números:
class MyLoggerObj
{
public function __destruct()
{
echo "Destruct of " . __CLASS__ . "/n";
}
}
$a = new ArrayObject();
$a[''ID''] = new MyLoggerObj();
$a[''ID''] = 37;
echo $a[''ID'']."/n";
Output :
Destruct of MyLoggerObj
37
Como puede ver, el destructor se llama en MyLoggerObj ; eso es porque en este caso ya no hay ninguna variable que se aferre al valor.
Prima
Si intenta averiguar cuándo se offsetGet() y offsetSet() extendiendo ArrayObject se sentirá decepcionado al descubrir que no puede imitar de forma adecuada mixed &offsetGet($key); . De hecho, hacerlo cambia el comportamiento de ArrayObject de tal manera que es imposible probar el comportamiento de esta clase.
puedo ver porque
$a = new ArrayObject();
$a[''ID''] = 42;
$b = &$a[''ID''];
$c = $a;
$c[''ID''] = 37;
echo $a[''ID'']."/n";
echo $b."/n";
echo $c[''ID'']."/n";
salidas 37, 42, 37
mientras
$a = new ArrayObject();
$a[''ID''] = 42;
$b = &$a[''ID''];
$c = $a;
$b = 37;
echo $a[''ID'']."/n";
echo $b."/n";
echo $c[''ID'']."/n";
salidas 37, 37, 37
En ambos casos, $b es una referencia a $a[''ID''] mientras que $c es un puntero al mismo objeto que $a .
Cuando $b cambia $a[''ID''] y $c[''ID''] cambia porque la asignación de $b cambia el valor referenciado por $a[''ID''] .
Cuando $c[''ID''] cambia, se asigna un nuevo int a $a[''ID''] , $b ya no hace referencia a $a[''ID''] .
Pero esto me pica
$a = new ArrayObject();
$a[''ID''] = 42;
$b = &$a[''ID''];
$c = $a;
$c[''ID''] &= 0;
$c[''ID''] |= 37;
echo $a[''ID'']."/n";
echo $b."/n";
echo $c[''ID'']."/n";
(salidas 37, 37, 37)
¿Es este comportamiento definido? No vi nada de eso en la documentación ...
Tomemos este código como base: ( refcounting documentación )
$a = new ArrayObject();
$a[''ID''] = 42;
$b = &$a[''ID''];
$c = $a;
xdebug_debug_zval(''a'');
xdebug_debug_zval(''b'');
xdebug_debug_zval(''c'');
Esto da:
a:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
public ''ID'' => (refcount=2, is_ref=1),int 42
b:
(refcount=2, is_ref=1),int 42
c:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
public ''ID'' => (refcount=2, is_ref=1),int 42
Como dice: $a es un objeto, $b es una referencia de $a[''ID''] ( $a[''ID''] y $b : refcount=2, is_ref=1 ) y $ c es una copia como referencia (desde PHP5) , entonces $ c es una referencia de $ a (ahora es el mismo objeto: refcount=2, is_ref=0 )
Si lo hacemos: $c[''ID''] = 37;
Obtenemos:
a:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
public ''ID'' => (refcount=1, is_ref=0),int 37
b:
(refcount=1, is_ref=0),int 42
c:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
public ''ID'' => (refcount=1, is_ref=0),int 37
$c[''ID''] se le asigna un nuevo int así que =>
$b vuelve independiente ( refcount=1 and is_ref=0 ), así como $a[''ID''] y $c[''ID'']
PERO como $c y $a son dependientes, $a[''ID''] y $c[''ID''] toman el mismo valor 37.
Ahora, tomemos el código base y lo hacemos: $c[''ID''] &= 0;
ACTUALIZACIÓN : Inesperadamente, obtenemos:
a:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
public ''ID'' => (refcount=2, is_ref=1),int 0
b:
(refcount=2, is_ref=1),int 0
c:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
public ''ID'' => (refcount=2, is_ref=1),int 0
en lugar de: (si: $c[''ID''] = $c[''ID''] & 0; )
a:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
public ''ID'' => (refcount=1, is_ref=0),int 0
b:
(refcount=1, is_ref=0),int 42
c:
(refcount=2, is_ref=0),
object(ArrayObject)[1]
public ''ID'' => (refcount=1, is_ref=0),int 0
ArrayObject implementa ArrayAccess para:
Como se dice en el comentario y documentado aquí :
Una modificación directa es una que reemplaza completamente el valor de la dimensión de la matriz, como en $ obj [6] = 7. Una modificación indirecta, por otro lado, solo cambia parte de la dimensión, o intenta asignar la dimensión por referencia a otra variable, como en $ obj [6] [7] = 7 o $ var = & $ obj [6]. Los incrementos con ++ y los decrementos con - también se implementan de una manera que requiere una modificación indirecta.
Una posible respuesta:
"Operador combinado (+ =, - =, & =, | =) se podría trabajar de la misma manera (modificación indirecta)":
refcountyis_refno se ven afectados, por lo que (en nuestro caso) se modifican los valores de todas las variables relacionadas. ($c[''ID'']=>$a[''ID'']=>$b)