Почему в php нельзя уточнить класс аргумента при имплементации интерфейса?
1. Определяем интерфейс
interface TestInterface { public function testMethod(ArgInterface $arg); } |
interface TestInterface { public function testMethod(ArgInterface $arg); }
2. Определяем ArgInterface
interface ArgInterface { } |
interface ArgInterface { }
3. Имплементируем ArgInterface:
class Arg implements ArgInterface { public $id; } |
class Arg implements ArgInterface { public $id; }
4. Имплементируем TestInterface, но $arg уточняем тип с ArgInterface на Arg, который реализует ArgInterface
class Test implements TestInterface { public function testMethod(Arg $arg) { echo $arg->id; } } |
class Test implements TestInterface { public function testMethod(Arg $arg) { echo $arg->id; } }
Запускаем:
<?php interface TestInterface { public function testMethod(ArgInterface $arg); } interface ArgInterface { } class Arg implements ArgInterface { public $id; } class Test implements TestInterface { public function testMethod(Arg $arg) { echo $arg->id; } } $arg = new Arg(); $arg->id = 1; $test = new Test(); $test->testMethod($arg); |
<?php interface TestInterface { public function testMethod(ArgInterface $arg); } interface ArgInterface { } class Arg implements ArgInterface { public $id; } class Test implements TestInterface { public function testMethod(Arg $arg) { echo $arg->id; } } $arg = new Arg(); $arg->id = 1; $test = new Test(); $test->testMethod($arg);
Ничего работать не будет:
Fatal error: Declaration of Test::testMethod(Arg $arg) must be compatible with TestInterface::testMethod(ArgInterface $arg) in /home/user/scripts/code.php on line 18 |
Fatal error: Declaration of Test::testMethod(Arg $arg) must be compatible with TestInterface::testMethod(ArgInterface $arg) in /home/user/scripts/code.php on line 18
Почему? Мы же уточняем тайпхинтинг наследником (имплементирующим классом), то есть принцип Лисков должен пропускать. Наследник же сохраняет всю функциональность родителя.
Есть ли этому поведению рациональное объяснение или это особенность языка?
Дополнительно:
Потому что это бы нарушало принцип подстановки Барбары Лисков.
Интерфейс предполагает, что ты можешь принимать любой объект, который реализует интерфейс ArgInterface.
=> Если ты сузишь тип, то ты уже по факту не сможешь поддерживать контракт.
По тому при реализации интерфейса можно только расширять тип принимаемых аргументов, но не сужать.
LSP нарушается - интерфейс требует чтобы реализация в качестве аргумента могла принимать любую реализацию ArgInterface, а вы пытаетесь ограничится только одной.
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос
В PHP действительно нельзя уточнить класс аргумента при имплементации интерфейса из-за особенностей языка. Однако это не значит, что невозможно обеспечить типизацию аргументов при использовании интерфейсов. Давайте разберемся, почему это происходит и как можно обойти это ограничение.
В PHP интерфейсы определяются как контракты, которые класс должен реализовать. При этом интерфейсы не могут содержать информацию о типах аргументов методов, так как это противоречило бы динамической природе языка. То есть PHP является слабо типизированным языком, и в момент выполнения нельзя гарантировать тип аргумента.
Однако, начиная с версии PHP 7, была добавлена поддержка строгой типизации аргументов и возвращаемых значений. Это позволяет указывать типы для аргументов и возвращаемых значений функций и методов. Но, к сожалению, данная функциональность не распространяется на интерфейсы.
Чтобы обеспечить типизацию аргументов при использовании интерфейсов, можно использовать комментарии PHPDoc. Например, вот как можно указать тип аргумента в PHPDoc комментариях:
interface MyInterface { /** * @param MyClass $arg */ public function myMethod($arg); }
Хотя это не является строгой типизацией и не обеспечивает проверку типов во время выполнения, такой подход позволяет документировать ожидаемые типы аргументов и делает код более понятным для других разработчиков.
Еще одним способом обойти ограничения PHP в этом случае является использование абстрактных классов вместо интерфейсов. В абстрактных классах можно указывать типы аргументов для методов, что облегчает работу с типизацией. Однако, это может быть не всегда удобным, так как классы могут наследоваться только от одного абстрактного класса.
Таким образом, в PHP нельзя уточнить класс аргумента при имплементации интерфейса из-за особенностей языка, но существуют способы обеспечить типизацию аргументов при использовании интерфейсов, такие как использование PHPDoc комментариев или абстрактных классов.