Это длинная, грустная история.
Когда PHP 5.2 впервые представил это предупреждение, поздние статические привязки еще не были в языке. Если вы не знакомы с поздними статическими привязками, обратите внимание, что подобный код не работает так, как вы могли бы ожидать:
<?php
abstract class ParentClass {
static function foo() {
echo "I'm gonna do bar()";
self::bar();
}
abstract static function bar();
}
class ChildClass extends ParentClass {
static function bar() {
echo "Hello, World!";
}
}
ChildClass::foo();
Если оставить в стороне предупреждение о строгом режиме, приведенный выше код не работает. Вызов self::bar()
в foo()
явно ссылается на метод bar()
для ParentClass
, даже когда foo()
вызывается как метод ChildClass
. Если вы попытаетесь запустить этот код с отключенным строгим режимом, вы увидите « Неустранимая ошибка PHP: невозможно вызвать абстрактный метод ParentClass :: bar () ».
Учитывая это, абстрактные статические методы в PHP 5.2 были бесполезны. Весь смысл в использовании абстрактного метода заключается в том, что вы можете написать код, который вызывает метод, не зная, какую реализацию он будет вызывать, а затем предоставить различные реализации для различных дочерних классов. Но поскольку PHP 5.2 не предлагает чистого способа написания метода родительского класса, который вызывает статический метод дочернего класса, для которого он вызывается, такое использование абстрактных статических методов невозможно. Следовательно, любое использование abstract static
в PHP 5.2 является плохим кодом, вероятно, из-за неправильного понимания того, как работает ключевое слово self
. Было вполне разумно предупредить об этом.
Но затем в PHP 5.3 добавилась возможность ссылаться на класс, для которого вызывался метод через ключевое слово static
(в отличие от ключевого слова self
, которое всегда ссылается на класс, в котором метод был определяется ). Если вы измените self::bar()
на static::bar()
в моем примере выше, он отлично работает в PHP 5.3 и выше. Вы можете прочитать больше о self
против static
на Новая личность против новой статики .
С добавленным ключевым словом static аргумент clear для abstract static
throw to a warning пропал. Основная цель поздних статических привязок состояла в том, чтобы позволить методам, определенным в родительском классе, вызывать статические методы, которые будут определены в дочерних классах; разрешение абстрактных статических методов представляется разумным и непротиворечивым, учитывая наличие поздних статических привязок.
Полагаю, вы все еще могли бы обосновать необходимость предупреждения. Например, вы можете утверждать, что, поскольку PHP позволяет вам вызывать статические методы абстрактных классов, в моем примере выше (даже после исправления его путем замены self
на static
) вы предоставляете открытый метод ParentClass::foo()
, который сломано и что вы действительно не хотите разоблачать. Использование нестатического класса - то есть создание всех методов экземпляра методов и создание дочерних элементов ParentClass
- синглетами или чем-то вроде этого - решило бы эту проблему, так как ParentClass
, будучи абстрактным, не может быть создан и т. Д. его методы экземпляра не могут быть вызваны. Я думаю, что этот аргумент слаб (потому что я думаю, что разоблачение ParentClass::foo()
не имеет большого значения и использование синглетонов вместо статических классов часто бесполезно многословно и безобразно), но вы можете не согласиться - это несколько субъективный вызов.
Итак, основываясь на этом аргументе, разработчики PHP сохранили предупреждение на языке, верно?
Э-э, не совсем .
В сообщении об ошибке PHP 53081, ссылка на которое приведена выше, содержится требование об удалении предупреждения, поскольку добавление конструкции static::foo()
сделало абстрактные статические методы разумными и полезными. Расмус Лердорф (создатель PHP) начинает с того, что помечает запрос как поддельный и проходит длинную цепочку неверных рассуждений, пытаясь оправдать предупреждение. Затем, наконец, происходит этот обмен:
Giorgio
я знаю, но:
abstract class cA
{
//static function A(){self::B();} error, undefined method
static function A(){static::B();} // good
abstract static function B();
}
class cB extends cA
{
static function B(){echo "ok";}
}
cB::A();
Rasmus
Правильно, именно так оно и должно работать.
Giorgio
но это не разрешено: (
Rasmus
Что не разрешено?
abstract class cA {
static function A(){static::B();}
abstract static function B();
}
class cB extends cA {
static function B(){echo "ok";}
}
cB::A();
Это отлично работает. Вы, очевидно, не можете вызвать self :: B (), но static :: B ()
в порядке.
Утверждение Расмуса о том, что код в его примере "работает нормально", является ложным; как известно, он выдает строгий режим предупреждения. Я предполагаю, что он тестировал без включенного строгого режима. Несмотря на это, растерянный Расмус оставил запрос ошибочно закрытым как «фальшивый».
И вот почему предупреждение все еще на языке. Возможно, это не совсем удовлетворительное объяснение - вы, вероятно, пришли сюда в надежде на разумное обоснование предупреждения. К сожалению, в реальном мире иногда выбор рождается из мирских ошибок и неправильных рассуждений, а не из рационального принятия решений. Это просто один из тех времен.
К счастью, уважаемый Никита Попов удалил предупреждение из языка в PHP 7 как часть PHP RFC: Реклассифицировать уведомления E_STRICT . В конечном счете здравомыслие восторжествовало, и после выхода PHP 7 мы все можем с радостью использовать abstract static
, не получив этого глупого предупреждения.