Почему нарушения LSP в PHP иногда фатальные, а иногда и предупреждения?
Это нарушение 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 ответ
В первом случае это фатальная ошибка, потому что PHP требует совместимости с родительским абстрактным классом:
При наследовании от абстрактного класса... сигнатуры методов должны совпадать.
То же самое верно и во втором случае:
Класс, реализующий интерфейс, должен использовать те же сигнатуры методов, которые определены в интерфейсе. Невыполнение этого приведет к фатальной ошибке.
В третьем случае вы расширяете обычный класс PHP, а не абстрактный. PHP позволяет изменить подпись, хотя и с предупреждением.
Очевидно, что это не очень хорошая практика, и, как вы указываете, нарушает LSP. Просто один из многих способов, которыми PHP дает вам острые объекты, и позволяет вам навредить себе, если вы не будете осторожны. знак равно
Если вы хотите использовать LSP, вам нужно использовать интерфейс, абстрагирование или создать свой метод final
в родительском классе.
Вот пример final
: https://3v4l.org/s42XG
В мае 2019 года RFC с ошибками LSP обновил язык. Начиная с PHP 8, движок всегда будет генерировать фатальную ошибку для несовместимых сигнатур методов.
До этого изменения производный класс мог волей-неволей изменять подпись, игнорировать ошибку и двигаться дальше. Не более: классы, как иabstract class
а также interface
должны соблюдать LSP.