Untill PHP 7.4, PHP has mostly invariant parameter types and invariant return types. With PHP 7.4, we now can declare Covariant Returns and Contravariant Parameters on interface/class definition.
What is Invariant Parameter/Return Type?
Invariant parameter/return type means, If a method of a super-type has defined a type on parameter/return, during overloading that method in sub-type we must define same type on parameter/return.
<?php
interface Food {
}
interface DogFood extends Food {
}
interface Dog {
public function eat(DogFood $food);
}
interface AnotherDog extends Dog {
// This is allowed
public function eat(DogFood $food);
}
interface YetAnotherDog extends Dog {
// This is not allowed, Type must be DogFood
public function eat(Food $food);
}
// Declaration of YetAnotherDog::eat(Food $food) must be compatible with Dog::eat(DogFood $food)
What is Contravariant Parameter Type?
Contravariant Parameter Type allows us to widen type definition on method parameter. Even if parent interface/class has defined parameter type on its method, we can redefine parameter type in child interface/class as long as that parameter type is its super-type.
<?php
interface Food {
}
interface DogFood extends Food {
}
interface CatFood extends Food {
}
interface Dog {
public function eat(DogFood $food);
}
interface AnotherDog extends Dog {
// This is allowed
public function eat(DogFood $food);
}
interface YetAnotherDog extends Dog {
// This is allowed now
// Because, Food is super-type of DogFood
public function eat(Food $food);
}
// But Other types are not allowed if that type is not super-type
interface Cat extends Dog {
// Not allowed
// because CatFood is not super-type of DogFood
public function eat(CatFood $food);
}
As we can see, changing eat() function to receive Food Type is now possible because Food is super-type of DogFood. But CatFood is not allowed because it just another sub-type of Food like DogFood.
What is Covariant Return Type?
Opposite to Contravariant, Covariant return type helps us to narrow down return type of a method. Consider below situation
<?php
interface Sound {
}
interface Bark extends Sound {
}
interface Meow extends Sound {
}
interface Animal {
public function makeSound(): Sound;
}
interface Dog extends Animal {
// returning Bark is allowed here
// because, Bark is sub-type of Sound
public function makeSound(): Bark;
}
interface Cat extends Animal {
// returning Meow is allowed here
// because, Meow is sub-type of Sound
public function makeSound(): Meow;
}
As our interface Animal has defined makeSound() method to return Sound Type, any interface that extends Animal interface can narrow down return type of makeSound() by defining a sub-type of Sound.
Covariant Returns and Contravariant Parameters help me to follow a procedure I love to follow: "Accept as many thing as you can, Return as little as you can."
Write comment about this article: