Here's a couple of snippets:
Overriding constructor method has an extra parameter.
class Cat { function __construct() {} } class Lion extends Cat { function __construct($param) {} }
Overriding (regular) method has an extra parameter.
class Cat { function doSomething() {} } class Lion extends Cat { function doSomething($param) {} }
The first would work, while the second would throw Declaration of Lion::doSomething() should be compatible with that of Cat::doSomething()
.
Why the special attitude towards constructor methods?
Note: PHP lacks support for declaring multiple constructors of different numbers of parameters for a class unlike languages such as Java. Multiple Constructors: More than one constructor in a single class for initializing instances are available.
PHP allows developers to declare constructor methods for classes. Classes which have a constructor method call this method on each newly-created object, so it is suitable for any initialization that the object may need before it is used.
A constructor allows you to initialize an object's properties upon creation of the object. If you create a __construct() function, PHP will automatically call this function when you create an object from a class.
Explanation. In PHP, the only rule to overriding constructors is that there are no rules! Constructors can be overridden with any signature. Their parameters can be changed freely and without consequence.
To understand why they are treated differently, you have to understand Liskov's Substitution Principle, which states
If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T." - BarbaraLiskov, Data Abstraction and Hierarchy, SIGPLAN Notices, 23,5 (May, 1988).
In a nutshell this means any class using your Lion
or Cat
should be able to reliably call doSomething
on it, regardless of the class being one or the other. If you change the method signature, this is no longer guaranteed (you may widen it, but not narrow it though).
public function doSomethingWithFeline(Cat $feline) { $feline->doSomething(42); }
Since Lion extends Cat
, you established an is-a relationship, meaning doSomethingWithFeline
will accept a Lion
for a Cat
. Now imagine you add a required argument to doSomething
in Lion
. The code above would break because it is not passing that new param. Hence, the need for compatible signatures.
LSP does not apply to constructors though, because subtypes might have different dependencies. For instance if you have a FileLogger and a DBLogger, the ctors (constructors) of the first would require a filename, while the latter would require a db adapter. As such, ctors are about concrete implementations and not part of the contract between classes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With