PHP 7 interfaces, return type hinting and self
editorial note: the answer below is outdated. as php PHP7.4.0, the following is perfectly legal:
<?php
Interface I{
public static function init(?string $url): self;
}
class C implements I{
public static function init(?string $url): self{
return new self();
}
}
$o = C::init("foo");
var_dump($o);
- 3v4l: https://3v4l.org/VYbGn
original answer:
self
does not refer to the instance, it refers to the current class. There is no way for an interface to specify that the same instance must be returned - using self
in the manner you're attempting would only enforce that the returned instance be of the same class.
That said, return type declarations in PHP must be invariant while what you're attempting is covariant.
Your use of self
is equivalent to:
interface iFoo
{
public function bar (string $baz) : iFoo;
}
class Foo implements iFoo
{
public function bar (string $baz) : Foo {...}
}
which is not allowed.
The Return Type Declarations RFC has this to say:
The enforcement of the declared return type during inheritance is invariant; this means that when a sub-type overrides a parent method then the return type of the child must exactly match the parent and may not be omitted. If the parent does not declare a return type then the child is allowed to declare one.
...
This RFC originally proposed covariant return types but was changed to invariant because of a few issues. It is possible to add covariant return types at some point in the future.
For the time being at least the best you can do is:
interface iFoo
{
public function bar (string $baz) : iFoo;
}
class Foo implements iFoo
{
public function bar (string $baz) : iFoo {...}
}
It also can be a solution, that you don't define explicitly the return type in the Interface, only in the PHPDoc and then you can define the certain return type in the implementations:
interface iFoo
{
public function bar (string $baz);
}
class Foo implements iFoo
{
public function bar (string $baz) : Foo {...}
}
In case, when You want to force from interface, that method will return object, but type of object won't be the type of interface, but the class itself, then You can write it this way:
interface iFoo {
public function bar(string $baz): object;
}
class Foo implements iFoo {
public function bar(string $baz): self {...}
}
It's working since PHP 7.4.