Це довга, сумна історія.
Коли 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
vs static
у New Self vs. New 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();
Це чудово працює. Ви, очевидно, не можете назвати себе: B (), але статичний :: B () - це добре.
Твердження Расмуса про те, що код у його прикладі "добре працює" є помилковим; як відомо, воно накидає суворий режим попередження. Я думаю, що він тестував без включеного суворого режиму. Незважаючи на те, розгублений Расмус запит помилково закрив як "фальшивий".
І тому попередження все ще є в мові. Це може бути не цілком задовольняючим поясненням - ви, мабуть, прийшли сюди, сподіваючись, що існує раціональне обґрунтування попередження. На жаль, в реальному світі іноді вибір народжується з приземлених помилок і неправильних міркувань, а не з раціонального прийняття рішень. Це просто один із тих часів.
На щастя, оцінюваний Микита Попов видалив попередження з мови в PHP 7 як частина повідомлення PHP RFC: Перекласифікуйте повідомлення E_STRICT . Зрештою, домінує розум, і коли PHP 7 випущений, ми всі можемо радісно використовувати, abstract static
не отримуючи цього дурного попередження.