Variance
| Covariant returns | |
| Contravariant params | |
| No property variance | |
| How PHP checks |
Covariant returns
A child method may narrow the return type to a subtype. Callers that expect the parent type keep working, since the subtype fits wherever the parent fits.
Factories and finders use this shape most. The parent promises a broad type, and each child promises a sharper one.
class Animal { } class Cat extends Animal { } class Shelter { public function get(): Animal { return new Animal(); } } class CatShelter extends Shelter { public function get(): Cat { return new Cat(); } }
Contravariant params
A child method may widen a parameter type to a supertype. Callers that pass the narrow type keep working, since the wide type accepts it.
Think of it as accepting more, not less. The child stays usable everywhere the parent was usable.
class Shelter { public function take(Cat $c): void { } } class WideShelter extends Shelter { public function take(Animal $c): void { } }
No property variance
Properties must keep the exact same type in the child. Narrowing and widening are both blocked for property types.
Reads and writes pull in opposite directions, so any change would break one side. PHP asks for an exact match instead.
class Shelter { public Animal $pet; } // public Cat $pet; // fatal error: the type must match exactly
How PHP checks
PHP checks signatures when the child class loads. A bad override stops the program with a fatal error right away.
Run the file once after changing any signature. Fast feedback beats long hunts through child classes.
function adopt(Shelter $s): Animal { return $s->get(); }
Next: Abstract and final
Article author: Arthur Isaev