Tom
Tom

Reputation: 3331

Narrowing-down the return type of an implemented method

I'm trying to specify two interfaces for class A that returns instances of class B, and for class B itself.

I'm declaring return types on the interface.

Say I've got two interfaces.

Some kind of RepositoryInterface, that has a get() method that returns an object implementing the ElementInterface

<?php

namespace App\Contracts;

interface RepositoryInterface {

    public function get( $key ) : ElementInterface;

}

And an element interface:

<?php

namespace App\Contracts;

interface ElementInterface { }

Now, my implementation of the repository declares a return-type that is a specific class MyElement.

<?php

namespace App\Repositories;

class MyRepository implements RepositoryInterface {

    public function get( $key ) : MyElement {
        // ...
    }

}

Where MyElement is some class implementing ElementInterface.

... this results in a fatal error:

Declaration of MyRepository::get( $key ): MyElement must be compatible with RepositoryInterface::get( $key ): ElementInterface

If I would not specify the return types on the interface, this would work perfectly fine. Yet, I want to constrain the type of class returned by any class implementing the RepositoryInterface.

  1. Is it true that this is not possible in PHP 7.1?
  2. If this is indeed not possible, is that because my pattern is incorrect?
  3. How could I declare the return type of my interface methods without specifying an actual implementation of this type.

Upvotes: 2

Views: 583

Answers (1)

yivi
yivi

Reputation: 47319

This is not possible with any version of PHP lower than 7.4.

If your interface contains:

public function get( $key ) : ElementInterface;

Then your class needs to be:

class MyRepository implements RepositoryInterface {

    public function get( $key ) : ElementInterface {

        returns new MyElement();
        // which in turn implements ElementInterface
    }
}

The declaration of a class implementing an interface has to match exactly the contract laid out by the interface.

By declaring that it has to return a particular interface instead of an specific implementation, you are giving you leeway on how to implement it (now you could return MyElement or AnotherElement as long as both implemented ElementInterface); but the method declaration has to be the same anyway.

See it working here.


Starting with PHP 7.4, due to be released in November 2019, covariance will be supported for return types. By then, this would work.

Upvotes: 2

Related Questions