Should concrete class follow the type hint of its interface?

Viewed 541

In this code sample, the interface doesn't seem to care whether the implementing method foo() checks for an array type parameter even if it explicitly type-hinted array only.

<?php

declare(strict_types = 1);

interface MyInterface
{
    public function foo(array $foo);
}


class Bar implements MyInterface
{
    public function foo($foo)
    {
        return $foo;
    }
}


echo (new Bar)->foo('test'); // runs just fine as string

I would expect at least a fatal, incompatible interface error; but there's none.

My questions are:

  1. Is this an expected behavior?
  2. Should the interface not have type hints at all because it's not respected anyway?
1 Answers

Short answer:

This is expected behaviour since 7.2, and interfaces type hints are enforced to an extent; but implementing classes may omit the interface's type declaration (but can't declare a parameter type different from the one declared in the interface).

Long answer:

This was a change introduced on PHP 7.2.

If you try this in PHP where PHP_VERSION_ID >= 7 && PHP_VERSION_ID < 7.2 you get:

Fatal error: Declaration of Bar::foo($foo) must be compatible with MyInterface::foo(array $foo)

But on PHP_VERSION_ID >= 7.2 it "works". The explanation for the changes is documented here, and says:

Parameter type widening

Parameter types from overridden methods and from interface implementations may now be omitted. This is still in compliance with LSP, since parameters types are contravariant.

interface A {
    public function Test(array $input); }

class B implements A {
    public function Test($input){} // type omitted for $input }

You can omit the parameter type, but you can't declare an incompatible type.

E.g. if in your example you tried:

public function foo(string $foo)
{
    return $foo;
}

It would fail all around.

A couple of links for further reading regarding this change:

Related