PHP 7接口,返回类型提示和s

我在PHP 7中使用返回类型提示遇到了问题。我的理解是,提示self意味着您打算让实现类返回自身。 因此,我在接口中使用了self来表明这一点,但是当我尝试实际实现接口时,出现了兼容性错误。

以下是我遇到的问题的简单演示:

interface iFoo
{
    public function bar (string $baz) : self;
}

class Foo implements iFoo
{

    public function bar (string $baz) : self
    {
        echo $baz . PHP_EOL;
        return $this;
    }
}

(new Foo ()) -> bar ("Fred") 
    -> bar ("Wilma") 
    -> bar ("Barney") 
    -> bar ("Betty");

预期输出为:

弗雷德   威尔玛   巴尼   贝蒂

我实际上得到的是:

PHP致命错误:Foo :: bar(int $ baz)的声明:Foo必须与iFoo :: bar(int $ baz):第7行的test.php中的iFoo兼容

事情是Foo是iFoo的实现,据我所知,该实现应该与给定的接口完全兼容。 我大概可以通过更改接口或实现类(或两者)以按名称返回提示接口的方式来代替使用self来暗示该接口,从而解决此问题,但我的理解是self的语义是“返回您刚刚称为的类的实例 方法”。 因此,从理论上讲,将其更改为接口意味着我可以返回任何实现接口的东西的实例,而当我的意图是要返回的实例时。

这是PHP的疏忽,还是故意的设计决策? 如果是前者,那么有没有机会在PHP 7.1中看到它的修复? 如果不是,那么暗示您的接口希望您返回刚刚调用方法进行链接的实例的正确返回方法是什么?

GordonM asked 2019-10-09T00:07:27Z
5个解决方案
65 votes

self没有引用实例,而是引用了当前类。 接口无法指定必须返回相同的实例-以您尝试的方式使用self仅会强制返回的实例属于同一类。

也就是说,PHP的返回类型声明必须是不变的,而您尝试的是协变的。

您对self的使用等效于:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : Foo  {...}
}

这是不允许的。


返回类型声明RFC表示:

继承过程中声明的返回类型的执行是不变的; 这意味着当子类型覆盖父方法时,子的返回类型必须与父方法完全匹配,并且不能省略。 如果父级未声明返回类型,则允许子级声明一个返回类型。

...

该RFC最初提出了协变返回类型,但由于一些问题,已更改为不变。 将来可能会添加协变返回类型。


暂时至少可以做的是:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : iFoo  {...}
}
Paul answered 2019-10-09T00:08:27Z
8 votes

这也可以是一个解决方案,您不必在接口中仅在PHPDoc中明确定义返回类型,然后可以在实现中定义特定的返回类型:

interface iFoo
{
    public function bar (string $baz);
}

class Foo implements iFoo
{
    public function bar (string $baz) : Foo  {...}
}
Gabor answered 2019-10-09T00:08:52Z
0 votes

这看起来对我来说是预期的行为。

只需将您的self方法更改为返回iFoo而不是self即可完成此操作。

说明:

接口中使用的self表示“类型为iFoo的对象”。
在实现中使用的self表示“类型为Foo的对象”。

因此,接口和实现中的返回类型显然不同。

评论之一提到Java以及您是否会遇到此问题。 答案是肯定的,如果Java允许您编写类似的代码,您将遇到同样的问题,而事实并非如此。 由于Java要求您使用类型的名称而不是PHP的self快捷方式,因此您永远不会真正看到它。 (有关Java类似问题的讨论,请参见此处。)

Moshe Katz answered 2019-10-09T00:09:53Z
0 votes

如果您要从接口强制执行,则该方法将返回对象,但是对象的类型将不是接口的类型,而是类本身,则可以这样编写:

interface iFoo {
    public function bar (string $baz) : object;
}

class Foo implements iFoo {
    public function bar (string $baz) : self  {...}
}

自PHP 7.2起就可以使用。

instead answered 2019-10-09T00:10:26Z
0 votes

使用PHP 7.3运行一些测试,当我这样做时,我不会抱怨(即使严格)。

interface A {
 function f() {}
}

interface B {
 function f():self {}
}

我的测试失败了,或者PHP改变了一些东西。 一般来说,如果减少可能的返回类型,则往往不会破坏OOP。 与在任何类中一样,使用该方法可以处理任何返回,包括返回类型的子集。 参数的情况大致相反。

他们在7.2中实现了这一点。

jgmjgm answered 2019-10-09T00:11:05Z
translate from https://stackoverflow.com:/questions/39068983/php-7-interfaces-return-type-hinting-and-self