Подсказка типа Php не согласуется с интерфейсами и абстрактными классами?

Я думаю, будет легче увидеть проблему в примере кода, чем писать вопрос в первую очередь. Вот мой php-код:

<?php interface AnInterface { public function method(); } class AClass implements AnInterface { public function method() { echo __METHOD__; } } abstract class AnAbstractClass { abstract public function method( AnInterface $Object ); } class ConcreteClass extends AnAbstractClass { public function method( AClass $Object ) { $Object->method(); } } $Object1 = new ConcreteClass(); $Object2 = new AClass(); $Object1->method( $Object2 ); 

Вышеприведенный код вызывает следующую ошибку:

Неустранимая ошибка: объявление ConcreteClass :: method () должно быть совместимо с объявлением AnAbstractClass :: method ()

Проблема в том, что php, похоже, не распознает подписи метода AnAbstractClass :: method и ConcreteClass :: как совместимые. Я делаю что-то неправильно? Благодаря!

php, похоже, не распознает подписи метода AnAbstractClass::method и ConcreteClass::method как совместимые.

PHP правильный, они несовместимы. AClass только экземпляры AClass (или его дочерних AClass ) для передачи в ConcreteClass::method , вы нарушаете контракт, который предоставляет AnAbstractClass : Любой из его подклассов должен принять AnInterface в качестве аргумента для своего method() .

Если ваш пример работал, и у меня был другой класс BClass реализующий AnInterface , у нас была бы ситуация, когда согласно AnAbstractClass method() должен принимать экземпляры BClass , в то время как, согласно ConcreteClass , он не должен.

Измените свою подпись для ConcreteClass::method чтобы она соответствовала AnAbstractClass::method .

Не считается. Вчера у нас было такое же обсуждение:
Могут ли типы параметров быть специализированными в PHP

Все ваши производные классы должны одинаково реализовывать подписи метода.

Это то, что в идеале должно быть проверено во время выполнения. Но в PHP парсер делает. (Чтобы компенсировать, PHP не проверяет доступ к закрытому / защищенному атрибуту во время разбора, но пусть это скорее взорвется во время выполнения.)

Если вы хотите применить более строгий тип, я бы посоветовал:

  assert( is_a($Object, "AClass") ); 

Вот пример, который показывает, почему это не разрешено:

 <?php class BClass implements AnInterface { } function moo(AnAbstractClass $abstract) { $b = new BClass(); $abstract->method($b); } : <?php class BClass implements AnInterface { } function moo(AnAbstractClass $abstract) { $b = new BClass(); $abstract->method($b); } 

Это был бы допустимый код, но он потерпит неудачу, если вы передадите ConcreteClass в moo, потому что его метод ConcreteClass::method не позволяет BClass .

Это сложно, но это легче понять, если вы видите пример.