Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 681
bishop Avatar asked Jun 17 '16 19:06

bishop


2 Answers

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

like image 190
jszobody Avatar answered Nov 19 '22 05:11

jszobody


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.

like image 43
bishop Avatar answered Nov 19 '22 07:11

bishop