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)":
refcount
yis_ref
no se ven afectados, por lo que (en nuestro caso) se modifican los valores de todas las variables relacionadas. ($c[''ID'']
=>$a[''ID'']
=>$b)