Наследование статических членов в PHP - PullRequest
33 голосов
/ 10 февраля 2009

В PHP, если статический атрибут определен в родительском классе, он не может быть переопределен в дочернем классе. Но мне интересно, есть ли способ обойти это.

Я пытаюсь написать оболочку для чужой (несколько неуклюжей) функции. Рассматриваемая функция может быть применена ко многим различным типам данных, но требует различных флагов и опций для каждого. Но в 99% случаев по умолчанию для каждого типа будет достаточно.

Было бы хорошо, если бы это можно было сделать с наследованием, без необходимости каждый раз писать новые функции. Например:

class Foo {
    public static $default = 'DEFAULT';

    public static function doSomething ($param = FALSE ) {
        $param = ($param === FALSE) ? self::$default : $param;
        return $param;
    }
}

class Bar extends Foo {
    public static $default = 'NEW DEFAULT FOR CHILD CLASS';
}

echo Foo::doSomething() . "\n"; 
// echoes 'DEFAULT'

echo Bar::doSomething() . "\n"; 
// echoes 'DEFAULT' not 'NEW DEFAULT FOR CHILD CLASS' 
// because it references $default in the parent class :(

Ответы [ 3 ]

31 голосов
/ 17 февраля 2013

На самом деле я думаю, что это не так: вы можете переопределять статические свойства ( вам нужно> = 5.3 PHP для этого). Но вы должны быть осторожны, ссылаясь на это статическое свойство (и это ошибка в исходном коде).

Вам нужно использовать static :: $ myStaticProperty вместо использования self :: $ myStaticProperty

self :: будет ссылаться на текущий класс , поэтому, если вы находитесь внутри унаследованного статического метода, это будет ссылаться на статическое свойство этого класса, определенного этим методом! При использовании ссылочного ключевого слова static :: будет действовать как $ this - когда вы используете экземпляры методов / свойств.

doSomething () - это унаследованный статический метод в классе Bar в вашем примере. Поскольку вы использовали self :: there, он будет ссылаться на статическое свойство класса Foo. По этой причине вы не видите никакой разницы ... Попробуйте изменить self :: на static :: !

Вот пример кода - я сам использовал его для проверки этих вещей. У нас есть статическое свойство / метод наследования, переопределения и изменения значения - запустите его, и вы увидите результат!

class A {

    // a static property - we will test override with it
    protected static $var = 'class A var - override';
    // a static property - we will test value overwrite with it
    protected static $var2 = 'class A var2 - value overwrite';


    public static function myStaticOverridePropertyTest() {
        return static::$var;
    }
    public static function myStaticValueOverwritePropertyTest() {
        return static::$var2;
    }

    /**
     * This method is defined only here - class B will inherit this one!
     * We use it to test the difference btw self:: and static::
     * 
     * @return string
     */
    public static function myStaticMethodTest() {
        //return self::getValue();
        return static::getValue();
    }

    /**
     * This method will be overwritten in class B
     * @return string
     */
    protected static function getValue() {
        return 'value from class A';
    }
}


class B extends A {

    // we override this inherited static property
    protected static $var = 'class B var - override';

    /**
     * This method is overwritten from class A
     * @return string
     */
    protected static function getValue() {
        return 'value from class B';
    }

    /**
     * We modify the value of the inherited $var2 static property
     */
    public static function modStaticProperty() {
        self::$var2 = 'class B - altered value! - value overwrite';
    }
}

echo ("-- testing class A:\n");
echo (A::myStaticOverridePropertyTest(). "\n");
echo (A::myStaticValueOverwritePropertyTest(). "\n");
echo (A::myStaticMethodTest(). "\n");

echo ("-- now testing class B:\n");
echo (B::myStaticOverridePropertyTest(). "\n");
echo (B::myStaticValueOverwritePropertyTest(). "\n");
echo ("  now invoking B::modStaticProperty()     .\n");
B::modStaticProperty();
echo (B::myStaticValueOverwritePropertyTest(). "\n");

echo ("-- now re-testing class A:\n");
echo (A::myStaticOverridePropertyTest(). "\n");
echo (A::myStaticValueOverwritePropertyTest(). "\n");
echo (A::myStaticMethodTest(). "\n");

Это выдаст:

- класс тестирования A:
класс A var - переопределить
класс A var2 - перезапись значения
значение из класса A
- сейчас тестирую класс B:
класс B var - переопределить
класс A var2 - перезапись значения
теперь вызываем B :: modStaticProperty () ...
класс B - измененное значение! - перезапись значения
- теперь повторное тестирование класса A:
класс A var - переопределить
класс B - измененное значение! - перезапись значения
значение из класса A

И здесь мы видим разницу между переопределенными и только перезаписанными значениями статических свойств ... посмотрите строку вывода, которую я выделил жирным шрифтом! Когда мы вызывали modStaticProperty () класса B, он также изменял значение этой статической переменной в классе A. Поскольку это статическое свойство было унаследовано и не было переопределено! Подумай об этом ...

27 голосов
/ 10 февраля 2009

Предстоящий релиз PHP 5.3.0 включает позднюю статическую привязку , что может помочь. Используя эту функцию, вы можете использовать статическую переменную внутри статического метода и позволить поздней статической привязке позаботиться о поиске «правильного» метода.

class Foo {
    public static function getDefault() {
        static $default = 'DEFAULT';
        return $default;
    }
    public static function doSomething ($param) {
        $default=static::getDefault(); // here is the late static binding
        $param = ($param === FALSE) ? $default : $param;
        return $param;

    }
}

class Bar extends Foo {
     public static function getDefault() {
        static $default = 'NEW DEFAULT FOR CHILD CLASS';
        return $default;
    }
}
16 голосов
/ 10 февраля 2009

Классический пример того, почему использование статики в качестве глобалов (в данном случае функций) - плохая идея, независимо от языка.

Самый надежный метод - создать несколько подклассов реализации абстрактного базового класса "Action".

Затем, чтобы попытаться устранить некоторые неудобства создания экземпляра класса просто для вызова его методов, вы можете обернуть его в какой-нибудь фабрике.

Например:

abstract class AbstractAction {
  public abstract function do();
}

class FooAction extends AbstractAction {
  public function do() {
    echo "Do Foo Action";
  }
}

class BarAction extends AbstractAction {
  public function do() {
    echo "Do Bar Action";
  }
}

Затем создайте фабрику, чтобы «помочь» в реализации функции

class ActionFactory {
  public static function get($action_name) {
    //... return AbstractAction instance here
  }  
}

Тогда используйте его как:

ActionFactory::get('foo')->do();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...