Frage

Ich bin eine Bibliothek in PHP 5.3 zu schreiben, der Großteil davon ist eine Klasse mit mehreren statischen Eigenschaften, die aus von Unterklassen erweitern Null-conf für Kind-Klassen zu ermöglichen.

Wie auch immer, hier ist eine Probe, die die Besonderheit zu verdeutlichen habe ich gefunden:

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

?>

Jetzt wird dies so ziemlich das gewünschte Verhalten für statische Vererbung soweit ich bin besorgt, aber static::$a =& $v; zu static::$a = $v; Wechsel (keine Referenz) das Verhalten bekomme ich erwartet, das ist:

'A'
'A'
'A'

'B'
'B'
'B'

'C'
'C'
'C'

erklären kann jemand, warum dies? Ich kann nicht verstehen, wie Referenzen Wirkung statische Vererbung in irgendeiner Weise: /

Update:

Basierend auf Artefacto Antwort , (in diesem Fall A) in der Basisklasse die folgende Methode aufweist und sie nach dem Klassendeklaration Aufruf erzeugt das Verhalten als ‚gewünscht‘ oben, ohne die Notwendigkeit in Setzer durch Bezugnahme auf zuweisen markiert, während die Ergebnisse verlassen, wenn mit selbst :: als ‚erwarteten‘ Verhalten, das oben.

/*...*/
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();
/*...*/
War es hilfreich?

Lösung

TL; DR Version

Die statische Eigenschaft $a ist ein anderes Symbol in jedem der Klassen, aber es ist eigentlich die gleiche Variable in dem Sinne, dass in $a = 1; $b = &$a;, $a und $b sind die gleichen Variablen (dh sie sind auf dem gleichen Referenzsatz) . Wenn eine einfache Zuordnung ($b = $v;) macht, wird der Wert der beiden Symbole ändern; wenn eine Zuweisung durch Bezugnahme ($b = &$v;) zur Verfügung gestellt wird nur $b betroffen sein.

Original-Version

Das erste, was, lassen Sie uns wissen, wie statische Eigenschaften ‚geerbt‘. zend_do_inheritance iteriert die übergeordnete Klasse statischen Eigenschaften Aufruf 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);

Die Definition, welche lautet:

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

Lassen Sie uns diese übersetzen. PHP Anwendungen kopieren auf Schreib, was bedeutet, es wird versuchen, die gleiche tatsächliche Speicherdarstellung (zval) der Werte zu teilen, wenn sie den gleichen Inhalt haben. inherit_static_prop wird für jede der übergeordneten Klasse statischen Eigenschaften genannt, so dass kann auf die Unterklasse kopiert werden. Die Umsetzung der inherit_static_prop sichergestellt, dass die statischen Eigenschaften der Unterklasse wird PHP Referenzen sein, ob die zval des Mutterunternehmens (insbesondere gemeinsam genutzt wird, wenn die übergeordnete Klasse eine Referenz hat, wird das Kind den zval teilen, wenn es nicht der Fall ist wird die zval kopiert und neue zval einen Bezug genommen werden;. der zweite Fall uns hier nicht wirklich interessieren)

So im Allgemeinen, wenn A, B und C gebildet werden, $a wird ein anderes Symbol für jede dieser Klassen wird (dh jede Klasse hat ihre Eigenschaften Hash-Tabelle und jede Hash-Tabelle hat einen eigenen Eintrag für $a), aber die darunter liegende zval wird das gleiche sein, und es wird eine Referenz sein.

Sie haben so etwas wie:

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

Wenn Sie also tun, um eine normale Zuordnung

static::$a = $v;

da alle drei Variablen die gleiche zval und es ist ein Referenz teilen, werden alle drei Variablen den Wert $v nehmen. Es wäre das gleiche, wenn Sie getan haben:

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

Auf der anderen Seite, wenn Sie das tun

static::$a =& $v;

Sie werden den Referenzsatz brechen. Angenommen, Sie haben es in der Klasse A. tun Sie sich jetzt:

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

Die analoge wäre

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

Work-around

Wie in Gordons nun gelöscht Antwort gekennzeichnet, die Referenzmenge zwischen den Eigenschaften der drei Klassen kann auch durch die Eigenschaft erneut deklariert wird in jedem der Klassen unterteilt werden:

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

Das ist, weil die Eigenschaft nicht auf die Unterklasse von der übergeordneten Klasse kopiert werden, wenn es neu deklariert ist (die Bedingung if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) in inherit_static_prop sehen).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top