Скажем, у нас есть следующие два класса:
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 вызывает фатальную ошибку: «Невозможно получить доступ к родительскому объекту, когда текущая область класса не имеет родителя».