What Is Meant by Covariance in PHP?

PHP covariance explained in simple terms

Starting with PHP 7.4, you can specify covariant return types in your class methods. This means that the return type of methods can be narrowed (i.e. made more specific) when overriding the return type of the parent method.

In PHP, a type declaration is considered more specific when you:

  1. Remove a type from a union type;
  2. Adding a type to an intersection type;
  3. Change a class type to a subtype;
  4. Change iterable to array or Traversable;
  5. Narrow an untyped type to be typed.

Please note the following:

  • It is type safe to allow an overriding method to return a more specific type than the method in the base class;
  • PHP does not support covariance for the callable return type;
  • PHP allows covariance in method parameters in class constructors;
  • Covariance rules also work on return types specified in interface methods.

Removing a Type From a Union Type

If, from the overriding child method's return type, you remove a type from a union type, it makes the return type more specific. For example:

// PHP 8+
class A
{
    public function foo(): string|int|float
    {
        // ...
    }
}

class B extends A
{
    public function foo(): string|int
    {
        // ...
    }
}

Adding a Type to an Intersection Type

If, to the overriding child method's return type, you add a type to an intersection type, it makes the return type more specific. For example:

// PHP 8.1+
class A
{
    public function foo(): Bar
    {
        // ...
    }
}

class B extends A
{
    public function foo(): Bar&Qux
    {
        // ...
    }
}

Changing a Class Type to a Subtype

When overriding the parent method's return type, the child class can specify a subtype of the class type specified as the parent method's return type. For example:

// PHP 7.4+
class A
{
    public function foo(): object
    {
        // ...
    }
}

class B extends A
{
    public function foo(): stdClass
    {
        // ...
    }
}

In the example above you can see how the B::foo() method specifies a more specific return type than its parent, of type stdClass (which is a subtype of object).

Changing iterable to array or Traversable

iterable return types can be narrowed by specifying array or the Traversable interface as return types. For example:

// PHP 7.4+
class A
{
    public function foo(): iterable
    {
        // ...
    }
}

class B extends A
{
    public function foo(): array
    {
        // ...
    }
}
// PHP 7.4+
class A
{
    public function foo(): iterable
    {
        // ...
    }
}

class B extends A
{
    public function foo(): Traversable
    {
        // ...
    }
}

Narrowing an Untyped Type to be Typed

When a method has no return type specified (i.e. it is untyped), then any return type you specify in the child method (when overriding) would make it more specific. For example:

// PHP 7.4+
class A
{
    public function foo()
    {
        // ...
    }
}

class B extends A
{
    public function foo(): string
    {
        // ...
    }
}

Hope you found this post useful. It was published (and was last revised ). Please show your love and support by sharing this post.