Pregunta

Estoy escribiendo una biblioteca en PHP 5.3, el mayor de los cuales es una clase con varias propiedades estáticas que se extiende desde las subclases para permitir cero-conf para las clases de los niños.

De todos modos, aquí está una muestra para ilustrar la particularidad que he encontrado:

<?php

class A {
    protected static $a;
    public static function out() { var_dump(static::$a); }
    public static function setup($v) { static::$a =& $v; }
}
class B extends A {}
class C extends A {}

A::setup('A');
A::out(); // 'A'
B::out(); // null
C::out(); // null

B::setup('B');
A::out(); // 'A'
B::out(); // 'B'
C::out(); // null

C::setup('C');
A::out(); // 'A'
B::out(); // 'B'
C::out(); // 'C'

?>

Ahora comportamiento, esto es más o menos deseada para la herencia estática en lo que a mí respecta, sin embargo, el cambio de static::$a =& $v; a static::$a = $v; (sin referencia) se obtiene el comportamiento que esperaba, que es:

'A'
'A'
'A'

'B'
'B'
'B'

'C'
'C'
'C'

Puede alguien explicar por qué esto es? No puedo entender cómo la herencia efecto referencias estática de ninguna manera: /

Actualización:

de Artefacto , que tiene el siguiente método en la clase base (en este ejemplo, a) y llamándolo después de las declaraciones de clase produce el comportamiento etiquetado como 'desea' anteriormente sin la necesidad de asignar por referencia en organismos, mientras que deja los resultados cuando mediante auto :: como el comportamiento 'espera' arriba.

/*...*/
public static function break_static_references() {
    $self = new ReflectionClass(get_called_class());
    foreach($self->getStaticProperties() as $var => $val)
        static::$$var =& $val;
}
/*...*/
A::break_static_references();
B::break_static_references();
C::break_static_references();
/*...*/
¿Fue útil?

Solución

TL; DR versión

El $a propiedad estática es un símbolo diferente en cada una de las clases, pero en realidad es la misma variable en el sentido de que en $a = 1; $b = &$a;, $a y $b son la misma variable (es decir, están en el mismo conjunto de referencia) . Al hacer una asignación simple ($b = $v;), el valor de ambos símbolos cambiará; cuando se hace una asignación por referencia ($b = &$v;), se verá afectada solamente $b.

Versión original

En primer lugar, vamos a entender cómo las propiedades estáticas son 'heredó'. zend_do_inheritance itera las propiedades estáticas de llamada superclase inherit_static_prop:

zend_hash_apply_with_arguments(&parent_ce->default_static_members TSRMLS_CC,
    (apply_func_args_t)inherit_static_prop, 1, &ce->default_static_members);

La definición de lo que es:

static int inherit_static_prop(zval **p TSRMLS_DC, int num_args,
    va_list args, const zend_hash_key *key)
{
    HashTable *target = va_arg(args, HashTable*);

    if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) {
        SEPARATE_ZVAL_TO_MAKE_IS_REF(p);
        if (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, p,
                sizeof(zval*), NULL) == SUCCESS) {
            Z_ADDREF_PP(p);
        }
    }
    return ZEND_HASH_APPLY_KEEP;
}

Vamos a traducir esto. usos de PHP copiar al escribir, lo que significa que va a tratar de compartir la misma representación real de la memoria (zval) de los valores si tienen el mismo contenido. inherit_static_prop se llama para cada una de las propiedades de la superclase estáticos por lo que se puede copiar a la subclase. La implementación de inherit_static_prop asegura que las propiedades estáticas de la subclase será referencias PHP, si el zval de los padres es compartida (en particular, si la superclase tiene una referencia, el niño va a compartir el zval, si no lo hace , se copiará el zval y nueva zval se hará una referencia;. el segundo caso, en realidad no nos interesa aquí)

Así que, básicamente, cuando se forman A, B y C, $a será un símbolo diferente para cada una de dichas clases (es decir, cada clase tiene su tabla de propiedades de hash y cada tabla hash tiene su propia entrada para $a), pero la zval subyacente será el mismo y será una referencia.

Usted tiene algo como:

A::$a -> zval_1 (ref, reference count 3);
B::$a -> zval_1 (ref, reference count 3);
C::$a -> zval_1 (ref, reference count 3);

Por lo tanto, cuando se hace una asignación normales

static::$a = $v;

ya que las tres variables comparten el mismo zval y su referencia, las tres variables asumirá el valor $v. Sería lo mismo si lo hizo:

$a = 1;
$b = &$a;
$a = 2; //both $a and $b are now 1

Por otro lado, cuando lo hace

static::$a =& $v;

se le rompiendo el conjunto de referencia. Digamos que lo hace en la clase A. Ahora tiene:

//reference count is 2 and ref flag is set, but as soon as
//$v goes out of scope, reference count will be 1 and
//the reference flag will be cleared
A::$a -> zval_2 (ref, reference count 2);

B::$a -> zval_1 (ref, reference count 2);
C::$a -> zval_1 (ref, reference count 2);

El análogo sería

$a = 1;
$b = &$a;
$v = 3;
$b = &$v; //$a is 1, $b is 3

trabajo en torno

Como aparece en la respuesta ahora borrado de Gordon, el conjunto de referencia entre las propiedades de las tres clases también puede ser roto por redeclarandolos la propiedad en cada una de las clases:

class B extends A { protected static $a; }
class C extends A { protected static $a; }

Esto se debe a que la propiedad no se copiará a la subclase de la superclase si se vuelve a declarar (ver la condición en la if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) inherit_static_prop).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top