PHP 7.4 : Covariant Returns and Contravariant Parameters
20, December 2019

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."