Это нарушение LSP вызывает фатальную ошибку:
abstract class AbstractService { }
abstract class AbstractFactory { abstract function make(AbstractService $s); }
class ConcreteService extends AbstractService { }
class ConcreteFactory extends AbstractFactory { function make(ConcreteService $s) {} }
Это нарушение LSP также вызывает фатальную ошибку:
interface AbstractService { }
interface AbstractFactory { function make(AbstractService $s); }
class ConcreteService implements AbstractService { }
class ConcreteFactory implements AbstractFactory { function make(ConcreteService $s) {} }
Хотя это нарушение LSP вызывает только Предупреждение:
class Service { }
class Factory { function make(Service $s) {} }
class MyService extends Service { }
class MyFactory extends Factory { function make(MyService $s) {} }
Почему? Разве они не должны быть фатальными, поскольку все они контравариантны?
Ответ 1 · рейтинг 8 · jszobody
В первом случае это фатальная ошибка, поскольку PHP требует совместимости с родительский абстрактный класс:
При наследовании от абстрактного класса... сигнатуры методов должны совпадать.
то же верно во втором случае:
Класс, реализующий интерфейс, должен использовать точно такие же сигнатуры методов, которые определены в интерфейсе. Если этого не сделать, произойдет фатальная ошибка.
В третьем случае вы расширяете обычный класс PHP, а не абстрактный. PHP позволяет изменить подпись, хотя и с предупреждением.
Это, очевидно, не очень хорошая практика и, как вы указываете, нарушает LSP. Это всего лишь один из многих способов, которыми PHP дает вам острые предметы и позволяет вам пораниться, если вы не будете осторожны. знак равно
Если вы хотите, чтобы LSP применялся, вам нужно использовать интерфейс, абстрагироваться или сделать свой метод final в родительском классе.
Вот пример final: https://3v4l.org/s42XG
Ответ 2 · bishop
В мае 2019 г. RFC об ошибках LSP был обновлен язык. Начиная с PHP 8, механизм всегда генерирует фатальную ошибку для несовместимых сигнатур методов.
До этого изменения производный класс мог волей-неволей изменить сигнатуру, проигнорировать ошибку и двигаться дальше. Больше ничего: классы, как и abstract class и interface, должны соблюдать LSP.