PHP 5.3: Поздняя статическая привязка не работает для свойств, определенных в родительском классе, но отсутствует в дочернем классе

Взгляните на этот пример и обратите внимание на указанные выходы.

<?php

class Mommy
{
    protected static $_data = "Mommy Data";

    public static function init( $data )
    {
        static::$_data = $data;
    }

    public static function showData()
    {
        echo static::$_data . "<br>";
    }
}

class Brother extends Mommy
{
}

class Sister extends Mommy
{
}

Brother::init( "Brother Data" );
Sister::init( "Sister Data" );

Brother::showData(); // Outputs: Sister Data
Sister::showData(); // Outputs: Sister Data

?>

Насколько я понимаю, использование ключевого слова static будет относиться к дочернему классу, но, очевидно, оно волшебным образом применяется к родительскому классу, когда оно отсутствует в дочернем классе. (Это своего рода опасное поведение для PHP, подробнее об этом ниже.)

У меня есть две следующие причины, по которым я хочу это сделать:

  1. Мне не нужна избыточность определения всех свойств во всех дочерних классах.
  2. Я хочу, чтобы свойства были определены как значения по умолчанию в родительском классе, и я хочу, чтобы определение дочернего класса могло переопределять эти свойства везде, где это необходимо. Дочерний класс должен исключать свойства всякий раз, когда предполагаются значения по умолчанию, поэтому я не определяю свойства в дочерних классах в приведенном выше примере.

Однако, если мы хотим переопределить свойство во время выполнения (через метод init), оно переопределит его для родительского класса! С этого момента дочерние классы, инициализированные ранее (как в случае с Brother), неожиданно изменяются для вас.

По-видимому, это результат того, что дочерние классы не имеют собственной копии статического свойства, когда оно явно не определено внутри дочернего класса, но вместо того, чтобы выдавать ошибку, он переключает поведение static на доступ к родителю. Следовательно, существует ли способ, которым родительский класс мог бы динамически создавать свойство, принадлежащее дочернему классу, не появляясь внутри определения дочернего класса? Таким образом, дочерний класс мог бы иметь свою собственную копию static свойство и ключевое слово static могут ссылаться на него должным образом, и его можно записать с учетом значений по умолчанию родительского свойства.

Или есть другое решение, хорошее, плохое или уродливое?


person OCDev    schedule 02.01.2011    source источник


Ответы (1)


Это действительно относится к правильному классу, просто, если не объявлено повторно или иначе набор ссылок не сломан, статические свойства в подклассах находятся в том же наборе ссылок, что и в суперклассе.

Итак, вы должны сделать:

class Brother extends Mommy
{
    protected static $_data;
}

or:

class Brother extends Mommy
{
}

$tmp = null;
Brother::$_data =& $tmp;
unset($tmp);
person Artefacto    schedule 02.01.2011
comment
Я смог приспособить ваше второе решение так, чтобы оно работало из родительского класса, и таким образом точно ответил на мой вопрос. Мой родительский класс создает свойство для дочернего класса в методе init следующим образом: общедоступная статическая функция init ($ data) {$ calledClass = get_called_class (); $ tmp = ноль; $ calledClass :: $ _ data = & $ tmp; не задано ($ tmp); static :: $ _ data = $ data; } - person OCDev; 02.01.2011
comment
На самом деле, я могу просто сделать это: публичная статическая функция init ($ data) {$ calledClass = get_called_class (); $ calledClass :: $ _ data = & $ data; } - person OCDev; 02.01.2011
comment
В качестве особого примечания, родительский класс должен иметь свойство, определенное для того, чтобы этот метод работал, в противном случае, если класс Mommy не имеет $ _data, определенного как свойство, при попытке повторно указать Ссылка на дочернее свойство с $ calledClass :: $ _ data = & $ data ;. (Это потому, что дочерний класс никогда не наследует указатель от родителя, поэтому он не может переназначить указатель на что-либо.) Это контрастирует с явным определением его в дочернем классе без обходного пути - он не требует его в родительский класс при его определении непосредственно в дочернем классе. - person OCDev; 02.01.2011