Наследование статических элементов в PHP
В 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 answers
Классический пример того, почему использование статики в качестве глобальных (в данном случае функций) - плохая идея, независимо от языка.
Наиболее надежным методом является создание нескольких подклассов реализации абстрактного базового класса "Действие".
Затем, чтобы попытаться устранить некоторые неудобства, связанные с созданием экземпляра класса только для вызова его методов, вы можете обернуть его в какую-нибудь фабрику.
Например:
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();
На самом деле я думаю, что это неправда: вы можете изменить статические свойства ( для этого вам нужно >=5.3 PHP). Но вы должны быть осторожны при повторном использовании этого статического свойства (и это ошибка в исходном коде)
Вам нужно использовать статический::$mystaticproperty вместо использования self::$mystaticproperty
self:: будет ссылаться на текущий класс, поэтому, если вы находитесь внутри унаследованного статического метода, это будет ссылаться на статическое свойство этого класса определил этот метод! При использовании ссылочного ключевого слова static:: будет действовать как $this - когда вы используете методы/свойства экземпляра.
doSomething() - это унаследованный статический метод в панели классов в вашем примере. Поскольку вы использовали self::там, он будет ссылаться на статическое свойство класса Foo. Вот почему вы не увидели никакой разницы... Попробуйте изменить себя:: на статический::!
Вот пример кода - я сам использовал его для тестирования этих вещей. У нас есть статическое наследование свойств/методов, переопределение и изменение значений в нем - запустите его, и вы увидите результат!
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 var - переопределение
класс A var2 - перезапись значения
значение из класса A
-- теперь тестируем класс B:
класс B var - переопределение
класс A var2 - перезапись значения
теперь вызываем B::modstaticproperty()...
класс В - измененное значение! - перезапись значения
-- теперь повторное тестирование класса А:
класс А var - переопределение
класс В - измененное значение! - перезапись значения
значение из класса А
И вот мы здесь, вы можете увидеть разницу между переопределенными и только перезаписанными статическими свойствами... посмотрите на выходную строку, которую я выделил жирным шрифтом! Когда мы вызвали функцию modStaticProperty() класса B, она также изменила значение этой статической переменной в классе A. Поскольку это статическое свойство было унаследовано и не было переопределено! Подумайте об этом...
Предстоящий выпуск 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;
}
}