Могут ли типы параметров быть специализированными в PHP

Скажем, у нас есть следующие два класса:

abstract class Foo { public abstract function run(TypeA $object); } class Bar extends Foo { public function run(TypeB $object) { // Some code here } } 

Класс TypeB расширяет класс TypeA.

Попытка использовать это дает следующее сообщение об ошибке:

Декларация Bar :: run () должна быть совместима с командой Foo :: run ()

Является ли PHP действительно сломанным, когда дело доходит до типов параметров, или я просто пропущу здесь пункт?

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

Кстати, PHP также не поддерживает контравариантность этих типов-подсказок (функция, обычно поддерживаемая на других языках ООП) – скорее всего, причина подозревается выше. Так что это тоже не работает:

 abstract class Foo { public abstract function run(TypeB $object); } class Bar extends Foo { public function run(TypeA $object) { // Some code here } } 

И, наконец, дополнительная информация: http://www.php.net/~derick/meeting-notes.html#implement-inheritance-rules-for-type-hints

Это похоже на большинство руководителей OO. PHP не похож на .Net – он не позволяет вам переопределять членов класса. Любое расширение Foo должно скользить туда, где ранее использовался Foo , что означает, что вы не можете ослаблять ограничения.

Простое решение, очевидно, должно удалить ограничение типа, но если Bar::run() нуждается в другом типе аргументов, то это действительно другая функция и в идеале должна иметь другое имя.

Если TypeA и TypeB имеют что-то общее, переместите общие элементы в базовый класс и используйте это как ограничение аргумента.

Я думаю, что это по дизайну : точка абстрактных определений определяет основное поведение своих методов.

При наследовании от абстрактного класса все методы, помеченные как абстрактные в объявлении класса родителя, должны определяться дочерним элементом; кроме того, эти методы должны быть определены с той же (или менее ограниченной) видимостью.

Всегда можно добавить ограничение в код:

 public function run(TypeA $object) { assert( is_a($object, "TypeB") ); 

Тогда вам придется запомнить или документировать конкретное ограничение по типу. Преимущество состоит в том, что он становится чисто средством разработки, поскольку утверждения, как правило, отключены на производственных серверах. (И действительно, это относится к классу ошибок, которые можно найти при разработке, а не случайным образом нарушать производство).

Хотя вы не можете использовать целые иерархии классов как тип-намек. Вы можете использовать self и parent ключевые слова для обеспечения чего-то подобного в определенных ситуациях.

цитируя r dot wilczek в web-appz dot de из комментариев PHP-руководства:

 <?php interface Foo { public function baz(self $object); } class Bar implements Foo { public function baz(self $object) { // } } ?> 

То, что не упоминалось к настоящему времени, заключается в том, что вы также можете использовать «parent» как typehint. Пример с интерфейсом:

 <?php interface Foo { public function baz(parent $object); } class Baz {} class Bar extends Baz implements Foo { public function baz(parent $object) { // } } ?> 

Bar :: baz () теперь примет любой экземпляр Baz. Если Bar не является наследником какого-либо класса (нет 'extends'), PHP вызывает фатальную ошибку: «Невозможно получить доступ к родительскому объекту, когда текущая область класса не имеет родителя».