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

Почему? Разве они не должны быть фатальными, поскольку все они контравариантны?


person bishop    schedule 17.06.2016    source источник


Ответы (2)


В первом случае это фатальная ошибка, поскольку PHP требует совместимости с родительский абстрактный класс:

При наследовании от абстрактного класса... сигнатуры методов должны совпадать.

то же верно во втором случае:

Класс, реализующий интерфейс, должен использовать точно такие же сигнатуры методов, которые определены в интерфейсе. Если этого не сделать, произойдет фатальная ошибка.

В третьем случае вы расширяете обычный класс PHP, а не абстрактный. PHP позволяет изменить подпись, хотя и с предупреждением.

Это, очевидно, не очень хорошая практика и, как вы указываете, нарушает LSP. Это всего лишь один из многих способов, которыми PHP дает вам острые предметы и позволяет вам пораниться, если вы не будете осторожны. знак равно

Если вы хотите, чтобы LSP применялся, вам нужно использовать интерфейс, абстрагироваться или сделать свой метод final в родительском классе.

Вот пример final: https://3v4l.org/s42XG

person jszobody    schedule 17.06.2016
comment
Я слежу за механикой, но не понимаю смысла. Зеев добавил ограничение в 5.0.0-rc2 в соответствии с Журналом изменений, но непонятно, меня, если это было для LSP или по другой причине, и если полиморфные отношения is-a были пропущены для BC с классами PHP 4. - person bishop; 18.06.2016
comment
PHP дает вам острые предметы и позволяет вам пораниться, если вы не будете осторожны. Это точно. Я полагаю, что обоснование теряется в тумане времени. - person bishop; 24.06.2016
comment
В настоящее время обсуждается lsp_errors RFC, предназначенный для PHP 8, чтобы устранить это несоответствие. - person bishop; 09.04.2019

В мае 2019 г. RFC об ошибках LSP был обновлен язык. Начиная с PHP 8, механизм всегда генерирует фатальную ошибку для несовместимых сигнатур методов.

До этого изменения производный класс мог волей-неволей изменить сигнатуру, проигнорировать ошибку и двигаться дальше. Больше ничего: классы, как и abstract class и interface, должны соблюдать LSP.

person bishop    schedule 07.05.2019