Domanda

Sto scrivendo una libreria in PHP 5.3, la maggior parte dei quali è una classe con diverse proprietà statiche che si estende da dalle sottoclassi per consentire a zero-conf per classi figlie.

In ogni caso, ecco un esempio per illustrare la particolarità che ho trovato:

<?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'

?>

comportamento Ora, questo è più o meno desiderato per l'ereditarietà statica per quanto mi riguarda, però, cambiando static::$a =& $v; a static::$a = $v; (nessun riferimento) si ottiene il comportamento che mi aspettavo, che è il seguente:

'A'
'A'
'A'

'B'
'B'
'B'

'C'
'C'
'C'

Qualcuno può spiegare perché questo è? Non riesco a capire come l'ereditarietà effetto riferimenti statici in alcun modo: /

Aggiornamento:

In base a di Artefacto , avente la seguente metodo nella classe base (in questo caso, a) e chiamando dopo le dichiarazioni di classe produce il comportamento etichettato come 'desiderata' sopra senza la necessità di assegnare per riferimento nella incastonatori, lasciando i risultati quando utilizzando self :: come il comportamento 'previsto' sopra.

/*...*/
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();
/*...*/
È stato utile?

Soluzione

TL; DR versione

La proprietà $a statico è un simbolo diverso in ciascuna delle classi, ma in realtà la stessa variabile nel senso che in $a = 1; $b = &$a;, $a e $b sono la stessa variabile (cioè, sono sullo stesso set di riferimento) . Quando si effettua un'assegnazione semplice ($b = $v;), il valore di entrambi i simboli cambierà; quando si effettua un'assegnazione per riferimento ($b = &$v;), viene influenzato solo $b.

Versione originale

Per prima cosa, cerchiamo di capire come le proprietà statiche sono 'ereditato'. zend_do_inheritance itera le proprietà statiche superclasse chiamando 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 definizione dei quali è:

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

Diamo tradurre questo testo. usi PHP copy on write, il che significa che cercherà di condividere la stessa rappresentazione memoria effettiva (zval) dei valori se hanno lo stesso contenuto. inherit_static_prop viene chiamata per ogni una delle proprietà superclasse statiche in modo che possono essere copiati alla sottoclasse. L'attuazione di assicura inherit_static_prop che le proprietà statiche della sottoclasse sarà riferimenti PHP, se lo zval del genitore è condivisa (in particolare, se la superclasse ha un riferimento, il bambino condividerà la zval, se non lo fa , lo zval verrà copiato e nuova zval si farà riferimento;. il secondo caso in realtà non ci interessa qui)

Quindi, in pratica, quando si formano A, B e C, $a sarà un simbolo diverso per ciascuna di queste classi (Per esempio, ogni classe ha la sua tabella di proprietà di hash e ogni tabella di hash ha la propria voce per $a), ma la zval sottostante sarà lo stesso e sarà un riferimento.

Hai qualcosa di simile:

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

Di conseguenza, quando si esegue un compito normale

static::$a = $v;

dal momento che tutte e tre le variabili condividono lo stesso zval e il suo riferimento di una, tutte e tre le variabili assumerà il valore di $v. Sarebbe lo stesso se avete fatto:

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

D'altra parte, quando si fa

static::$a =& $v;

verrà rompendo il set di riferimento. Diciamo che lo si fa in classe A. Ora avete:

//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);

L'analogo sarebbe

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

work-around

Come descritto in risposta ora cancellato di Gordon, il set di riferimento tra le proprietà delle tre classi può essere rotto da redeclaring la struttura in ognuna delle classi:

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

Questo perché la proprietà non verrà copiato nella sottoclasse dalla superclasse se è ridichiarato (vedi if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) condizione in inherit_static_prop).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top