Это долгая печальная история.
Когда 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
vs static
на New self vs. new static .
С добавлением ключевого слова static явным аргументом в пользу наличия abstract static
исчез выдачи предупреждения. Основная цель поздних статических привязок состояла в том, чтобы позволить методам, определенным в родительском классе, вызывать статические методы, которые будут определены в дочерних классах; разрешение абстрактных статических методов кажется разумным и последовательным, учитывая наличие поздних статических привязок.
Думаю, вы все еще можете убедить вас в сохранении предупреждения. Например, можно утверждать , что с PHP позволяет вызывать статические методы абстрактных классов, в моем примере выше (даже после фиксации его замены self
с static
) вы обнажая публичный метод , ParentClass::foo()
который сломан и что вы действительно не хотите разоблачить. Использование нестатического класса - то есть создание всех методов экземпляра методов и создание дочерних элементовParentClass
синглтонами или что-то в этом роде, решило бы эту проблему, поскольку ParentClass
, будучи абстрактным, нельзя создать экземпляр, и поэтому его методы экземпляра не могут называться. Я считаю этот аргумент слабым (потому что я думаю, что разоблачениеParentClass::foo()
не имеет большого значения, и использование синглтонов вместо статических классов часто излишне многословно и некрасиво), но вы можете разумно не согласиться - это несколько субъективный вызов.
Итак, основываясь на этом аргументе, разработчики PHP сохранили предупреждение на языке, верно?
Ну, не совсем .
В отчете об ошибке PHP 53081, ссылка на который приведена выше, содержится призыв к удалению предупреждения, поскольку добавление static::foo()
конструкции сделало абстрактные статические методы разумными и полезными. Расмус Лердорф (создатель PHP) начинает с того, что помечает запрос как поддельный и проходит длинную цепочку неверных аргументов, чтобы попытаться оправдать предупреждение. Затем, наконец, происходит этот обмен:
Джорджио
я знаю, но:
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();
Расмус
Да, именно так это и должно работать.
Джорджио
но нельзя :(
Расмус
Что нельзя?
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: Reclassify E_STRICT notices . В конце концов, здравомыслие восторжествовало, и как только выйдет PHP 7, мы все сможем с радостью использовать, abstract static
не получая этого глупого предупреждения.