Question

Je vous écris une bibliothèque en PHP 5.3, dont la majeure partie est une classe avec plusieurs propriétés statiques qui est prolongée de sous-classes pour permettre zéro pour les classes de conf enfant.

Quoi qu'il en soit, voici un échantillon pour illustrer la particularité que j'ai trouvé:

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

?>

Maintenant, cela est assez comportement tant désiré pour l'héritage statique en ce qui me concerne, cependant, changer static::$a =& $v; à static::$a = $v; (aucune référence), vous obtenez le comportement que je m'y attendais, qui est:

'A'
'A'
'A'

'B'
'B'
'B'

'C'
'C'
'C'

Quelqu'un peut-il expliquer pourquoi il en est? Je ne peux pas comprendre comment l'héritage statique références effet de quelque façon que: /

Mise à jour:

Basé sur réponse de Artefacto , avec le procédé suivant dans la classe de base (dans ce cas, a) et l'appelant après que les déclarations de classe produit le comportement marqué comme « désiré » ci-dessus sans qu'il soit nécessaire d'attribuer à titre de référence dans poseurs, tout en laissant les résultats lorsque en utilisant l'auto :: que le comportement « attendu » ci-dessus.

/*...*/
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();
/*...*/
Était-ce utile?

La solution

TL; DR Version

La $a propriété statique est un symbole différent dans chacune des classes, mais il est en fait la même variable dans le sens que, dans $a = 1; $b = &$a;, $a et $b sont la même variable (ils sont sur le même ensemble de référence) . Lorsque vous effectuez une affectation simple ($b = $v;), la valeur des deux symboles changera; lors de la cession par référence ($b = &$v;), ne $b sera affectée.

Version originale

La première chose, nous allons comprendre comment les propriétés statiques sont « hérité ». zend_do_inheritance itère les propriétés statiques superclasse appel 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 définition est:

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

Traduisons cela. utilise PHP copie à l'écriture, ce qui signifie qu'il va essayer de partager la même représentation de la mémoire réelle (zval) des valeurs si elles ont le même contenu. inherit_static_prop est appelée pour chacun d'eux des propriétés statiques de la superclasse de sorte que peuvent être copiés à la sous-classe. La mise en œuvre de inherit_static_prop assure que les propriétés statiques de la sous-classe des références PHP, que ce soit ou non zval du parent est partagé (en particulier, si la superclasse a une référence, l'enfant partagera zval, si elle ne , zval sera copié et nouvelle zval sera fait référence;. le second cas, ne nous intéresse pas vraiment ici)

Donc, fondamentalement, quand A, B et C sont formés, $a sera un symbole différent pour chacune de ces classes (chaque classe a ses propriétés table de hachage et chaque table de hachage a sa propre entrée pour $a), mais les zval sous-jacent sera le même et ce sera une référence.

Vous avez quelque chose comme:

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

Par conséquent, lorsque vous effectuez une mission normale

static::$a = $v;

puisque les trois variables partagent la même zval, et la référence a, les trois variables prendra la valeur $v. Ce serait la même chose si vous avez fait:

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

Par contre, quand vous faites

static::$a =& $v;

vous violeront l'ensemble de référence. Disons que vous le faites en classe A. Vous avez maintenant:

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

analogue serait

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

Travail autour

Comme présenté dans la réponse maintenant supprimé Gordon, l'ensemble de référence entre les propriétés des trois classes peut également être rompu par redéclarant la propriété dans chacune des classes:

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

En effet, la propriété ne sera pas copié dans la sous-classe de la superclasse si elle est redéclaré (voir le if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) état dans inherit_static_prop).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top