为什么 PHP 中的 LSP 违规有时是致命的,有时是警告?

Why are LSP violations in PHP sometimes fatal, and sometimes warnings?

此 LSP 违规 raises a Fatal Error

abstract class AbstractService { }
abstract class AbstractFactory { abstract function make(AbstractService $s); }
class ConcreteService extends AbstractService { }
class ConcreteFactory extends AbstractFactory { function make(ConcreteService $s) {} }

此 LSP 违规 also raises a Fatal Error

interface AbstractService { }
interface AbstractFactory { function make(AbstractService $s); }
class ConcreteService implements AbstractService { }
class ConcreteFactory implements AbstractFactory { function make(ConcreteService $s) {} }

虽然此 LSP 违规仅 raises a Warning:

class Service { }
class Factory { function make(Service $s) {} }
class MyService extends Service { }
class MyFactory extends Factory { function make(MyService $s) {} }

为什么?因为它们都是逆变的,所以它们不应该都是致命的吗?

第一种情况是致命错误,因为PHP requires you to be compatible with a parent abstract class:

When inheriting from an abstract class... signatures of the methods must match.

第二种情况下的same is true

The class implementing the interface must use the exact same method signatures as are defined in the interface. Not doing so will result in a fatal error.

在第三种情况下,你扩展了一个普通的PHP class,而不是抽象的。 PHP 允许您更改签名,但会出现警告。

这显然不是一个好的做法,并且正如您指出的那样确实违反了 LSP。只是 PHP 给你尖锐物体的众多方式之一,如果你不小心,就会伤到自己。 =)

如果你想强制执行 LSP,你需要使用一个接口,抽象,或者在父 class.

中创建你的方法 final

这是 final 的示例:https://3v4l.org/s42XG

2019 年 5 月,LSP errors RFC 更新了语言。从 PHP 8 开始,引擎将 总是 为不兼容的方法签名生成 致命错误

在此更改之前,派生的 class 可以更改签名 willy-nilly,忽略错误并继续。不再:classes,就像abstract classinterface必须遵守LSP。