Почему PHP 5.2+ запрещает использование абстрактных методов статического класса?

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

Строгие стандарты : статическая функция. Программа :: getSelectSQL () не должна быть абстрактной в Program.class.inc

Данная функция относится к абстрактной программе родительского класса и объявляется абстрактной статической, поскольку она должна быть реализована в ее дочерних классах, таких как TVProgram.

Я нашел ссылки на это изменение здесь :

Выброшены абстрактные статические функции класса. Из-за недосмотра PHP 5.0.x и 5.1.x допускали абстрактные статические функции в классах. Начиная с PHP 5.2.x, только интерфейсы могут иметь их.

Мой вопрос: может кто-нибудь объяснить в ясной форме, почему не должно быть абстрактной статической функции в PHP?

Related of "Почему PHP 5.2+ запрещает использование абстрактных методов статического класса?"

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

То же самое касается расширения любого класса статическими методами. Если вы расширяете этот класс и создаете статический метод одной и той же сигнатуры, вы фактически не переопределяете статический метод суперкласса

EDIT (16 сентября 2009 г.)
Обновите это. Запустив PHP 5.3, я вижу, что абстрактный статик вернулся, для хорошего или плохого. (см. http://php.net/lsb для получения дополнительной информации)

КОРРЕКЦИЯ (by philfreo)
abstract static все еще не разрешен в PHP 5.3, LSB связан, но отличается.

Для этой проблемы очень простая работа, что на самом деле имеет смысл с точки зрения дизайна. Как писал Джонатан:

То же самое касается расширения любого класса статическими методами. Если вы расширяете этот класс и создаете статический метод одной и той же сигнатуры, вы фактически не переопределяете статический метод суперкласса

Итак, как работа вокруг, вы можете это сделать:

 <?php abstract class MyFoo implements iMyFoo { public static final function factory($type, $someData) { // don't forget checking and do whatever else you would // like to do inside a factory method $class = get_called_class()."_".$type; $inst = $class::getInstance($someData); return $inst; } } interface iMyFoo { static function factory($type, $someData); static function getInstance(); function getSomeData(); } ?> 

И теперь вы утверждаете, что любой класс подкласса MyFoo реализует статический метод getInstance и общедоступный метод getSomeData. И если вы не подклассифицируете MyFoo, вы все равно можете реализовать iMyFoo для создания класса с аналогичной функциональностью.

Это долгая, печальная история.

Когда 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 Fatal error: нельзя вызвать абстрактный метод 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 в New self vs. new static .

С добавлением статического ключевого слова ясный аргумент для того, чтобы abstract static бросить предупреждение, исчез. Основной целью статических привязок было дать возможность методам, определенным в родительском классе, вызвать статические методы, которые будут определены в дочерних классах; позволяющие абстрактные статические методы кажутся разумными и последовательными, учитывая существование поздних статических привязок.

Возможно, вы все еще можете подать предупреждение. Например, вы можете утверждать, что, поскольку PHP позволяет вызывать статические методы абстрактных классов, в моем примере выше (даже после его исправления путем замены self static ) вы подвергаете публичному методу ParentClass::foo() который разбит и что вы действительно не хотите раскрывать. Использование нестатического класса, т. ParentClass всех методов экземпляров методов и создание дочерних элементов ParentClass все это синглтоны или что-то еще, позволит решить эту проблему, поскольку 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: реклассифицировать уведомления E_STRICT . В конечном счете, здравомыслие превалировало, и как только PHP 7 выпущен, мы все можем счастливо использовать abstract static не получив этого глупого предупреждения.

Я знаю, что это старый, но …

Почему бы просто не просто исключить статический метод этого родительского класса, тем более, если вы не переопределите его, вызвано это исключение.

Я бы сказал, что абстрактный класс / интерфейс можно рассматривать как контракт между программистами. Это больше касается того, как вещи должны выглядеть / вести себя как и не выполнять фактическую функциональность. Как видно из php5.0 и 5.1.x, это не естественный закон, который мешает разработчикам php делать это, но желание идти вместе с другими шаблонами проектирования OO на других языках. В основном эти идеи пытаются предотвратить неожиданное поведение, если оно уже знакомо с другими языками.

Я не вижу причин запрещать статические абстрактные функции. Лучший аргумент, что нет оснований запрещать их, заключается в том, что они разрешены на Java. Вопросы: – технически осуществимы? – Да, поскольку они существуют в PHP 5.2 и существуют на Java. Так что МОЖЕТ это сделать. СЛЕДУЕТ это сделать? – У них есть смысл? Да. Имеет смысл реализовать часть класса и оставить для пользователя другую часть класса. Это имеет смысл в нестатических функциях, почему это не имеет смысла для статических функций? Одно использование статических функций – это классы, где не должно быть более одного экземпляра (одиночные числа). Например, механизм шифрования. В некоторых случаях это не обязательно, и есть причины предотвратить это – например, вы должны защитить только одну часть памяти от злоумышленников. Поэтому имеет смысл реализовать одну часть движка и оставить алгоритм шифрования для пользователя. Это только один пример. Если вы привыкли использовать статические функции, вы найдете намного больше.

В php 5.4+ используйте свойство:

 trait StaticExample { public static function instance () { return new self; } } 

и в вашем классе поставили в начале:

 use StaticExample; 

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