What Is Meant by Contravariance in PHP?

Starting with PHP 7.4, you can specify contravariant parameter types in your class methods. This means that the parameter types can be widened (i.e. made less specific) when overriding the parameter types of the parent method.

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

  1. Add a type to a union type;
  2. Removing a type from an intersection type;
  3. Change a child class type to a supertype;
  4. Change array or Traversable to iterable;
  5. Widen a type to be untyped.

Please note the following:

  • It is type safe to allow an overriding method to accept a more general parameter type than the method in the base class;
  • PHP does not support contravariance for the callable parameter type;
  • Contravariance rules also work on parameter types specified in interface methods.

Adding a Type to a Union Type

You can widen child method's parameters when overriding the parent method by adding a type to an existing union type or by creating a union type. This will make the type less specific. For example:

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

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

Removing a Type From an Intersection Type

You can widen child method's parameters when overriding the parent method by removing a type from an existing intersection type. This will make the type less specific. For example:

// PHP 8.1+
class A
{
    public function foo(Bar&Qux $arg)
    {
        // ...
    }
}

class B extends A
{
    public function foo(Bar $arg)
    {
        // ...
    }
}

Changing a Child Class Type to a Supertype

You can specify a supertype of the parameter defined in the parent method's parameter in the child that overrides it. This would widen the parameter type. For example:

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

class B extends A
{
    public function foo(object $obj)
    {
        // ...
    }
}

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

Changing array or Traversable to iterable

Parameter types of array or the Traversable interface can be widened in an overriding child method by specifying iterable as the parameter type. For example:

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

class B extends A
{
    public function foo(iterable $collection)
    {
        // ...
    }
}
// PHP 7.4+
class A
{
    public function foo(Traversable $obj)
    {
        // ...
    }
}

class B extends A
{
    public function foo(iterable $obj)
    {
        // ...
    }
}

Widening a Type to be Untyped

You may widen a parameter type to accept any type of value by making the parameter untyped in the overriding child method. For example:

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

class B extends A
{
    public function foo($value)
    {
        // ...
    }
}

This post was published (and was last revised ) by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.