Я обнаружил несоответствие в PHP при переопределении методов в дочерних классах, используя подтипы в качестве параметра для методов. Проще всего объяснить в коде:
interface IngredientInterface {}
interface TomatoInterface extends IngredientInterface {}
class Tomato implements TomatoInterface {}
class Soup {
public function __construct(IngredientInterface $ingredient) {}
public function foo(IngredientInterface $ingredient) {}
}
class TomatoSoup extends Soup {
public function __construct(TomatoInterface $tomato) { parent::__construct($tomato); }
public function foo(TomatoInterface $ingredient) { parent::foo($ingredient); }
}
Можно было бы ожидать, что поведение сообщения об ошибках будет одинаковым для переопределенных методов __construct () и foo (), но это не так.
Метод __construct () не генерирует ошибок в PHP 5.5.38, 5.6.19 и 7.0.4
Метод foo () генерирует следующую ошибку в 5.5.38 и 5.6.19:
Strict Standards: Declaration of TomatoSoup::foo() should be compatible with Soup::foo(IngredientInterface $ingredient) in H:\webroot\test.php on line 16
и в 7.0.4:
Warning: Declaration of TomatoSoup::foo(TomatoInterface $ingredient) should be compatible with Soup::foo(IngredientInterface $ingredient) in H:\webroot\test.php on line 16
Теперь я не обеспокоен тем, что тип ошибки изменился с E_STRICT на E_WARNING, меня больше беспокоит несогласованность штрафа синтаксического анализа конструктора, но метод foo () — нет.
Это ошибка в PHP? Должен ли я сообщить об этом на bugs.php.net?
Зачем вам нужен подтип, если вы расширяете класс, если у вас есть супертип IngredientInterface, вы можете передать каждый объект, который является подтипом IngredienteInterface, конструктору и методу foo (помните хорошие практики Лискова принцип замещения), и, ¿почему вы хотите расширить интерфейс? (помните принцип разделения интерфейса)
Результат тот же, если вы используете супертип в классе детей, но я думаю, что вам нужно еще немного пересмотреть свой код, принципы SOLID являются хорошей отправной точкой
<?php
interface IngredientInterface {}
interface TomatoInterface extends IngredientInterface {}
class Ingredient implements IngredientInterface {}
class Tomato implements TomatoInterface {}
class Soup {
public function __construct(IngredientInterface $ingredient) {}
public function foo(IngredientInterface $ingredient) {}
}
class TomatoSoup extends Soup {
public function __construct(IngredientInterface $tomato) { parent::__construct($tomato); }
public function foo(IngredientInterface $ingredient) { parent::foo($ingredient); }
}
$tom = new TomatoSoup(new Tomato());
$tom->foo(new Tomato());
$tom2 = new TomatoSoup(new Ingredient());
$tom2->foo(new Ingredient());
Других решений пока нет …