Почему нарушения 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.

Другие вопросы по тегам