-->

Why are LSP violations in PHP sometimes fatal, and

2019-06-17 04:50发布

问题:

This LSP violation 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) {} }

This LSP violation 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) {} }

While this LSP violation only raises a Warning:

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

Why? Shouldn't they all be fatal since they're all contravariant?

回答1:

In the first case, it is a fatal error because PHP requires you to be compatible with a parent abstract class:

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

The same is true in the second case:

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.

In the third case, you extend a normal PHP class, not abstract. PHP allows you to change the signature, albeit with a warning.

It obviously isn't a good practice, and does violate LSP as you point out. Just one of the many ways PHP gives you sharp objects, and lets you hurt yourself if you aren't careful. =)

If you want LSP enforced, you need to use an interface, abstract, or make your method final in the parent class.

Here's an example of final: https://3v4l.org/s42XG



回答2:

In May 2019, the LSP errors RFC updated the language. As of PHP 8, the engine will always generate a fatal error for incompatible method signatures.

Before this change, a derived class could change a signature willy-nilly, ignore the error, and move on. No more: classes, just like abstract class and interface must abide by LSP.