PHP7接口,返回类型提示和self

PHP7接口,返回类型提示和self,php,interface,return-type,php-7,type-hinting,Php,Interface,Return Type,Php 7,Type Hinting,更新:PHP 7.4现在解决了这个问题中提出的主要问题 我在PHP7中使用返回类型暗示时遇到了一些问题。我的理解是暗示:self意味着您希望实现类返回自身。因此,我在接口中使用了:self来表示这一点,但当我试图实际实现接口时,我得到了兼容性错误 以下是我遇到的问题的简单演示: interface iFoo { public function bar (string $baz) : self; } class Foo implements iFoo { public fun

更新:PHP 7.4现在解决了这个问题中提出的主要问题


我在PHP7中使用返回类型暗示时遇到了一些问题。我的理解是暗示
: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必须与第7行test.PHP中的iFoo::bar(int$baz):iFoo兼容

问题是,Foo是iFoo的一个实现,所以据我所知,这个实现应该与给定的接口完全兼容。我可以通过更改接口或实现类(或两者)来修复此问题,以按名称返回接口提示,而不是使用
self
,但我的理解是,从语义上讲
self
意味着“返回您刚才调用方法的类的实例”。因此,从理论上讲,将其更改为接口意味着我可以返回实现该接口的任何对象的实例,而我的意图是调用的实例将被返回


这是PHP中的一个疏忽还是一个深思熟虑的设计决策?如果是前者,有没有可能在PHP7.1中看到它被修复?如果不是,那么什么是正确的返回方式,暗示您的接口希望您返回刚刚调用链接方法的实例

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

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

您使用
self
相当于:

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

class Foo implements iFoo
{

    public function bar (string $baz) : Foo  {...}
}
这是不允许的


政府有:

继承期间声明的返回类型的强制是不变的;这意味着,当子类型重写父方法时,子类型的返回类型必须与父类型完全匹配,并且不能忽略。如果父级未声明返回类型,则允许子级声明返回类型

该RFC最初提出了协变返回类型,但由于一些问题而改为不变。可以在将来的某个时候添加协变回报类型


至少目前你能做的最好的事情是:

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

class Foo implements iFoo
{

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

这看起来像是我所期望的行为

只需将您的
Foo::bar
方法更改为返回
iFoo
,而不是
self
,就可以了

说明:

界面中使用的
self
表示“类型为
iFoo
的对象”
实现中使用的
self
表示“类型为
Foo
的对象”

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


其中一条评论提到了Java以及您是否会遇到这个问题。答案是肯定的,如果Java允许您编写这样的代码,您也会遇到同样的问题,而Java却不允许。因为Java要求您使用类型的名称而不是PHP的
self
快捷方式,所以您永远不会真正看到这一点。(有关Java中类似问题的讨论,请参阅。)

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

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

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

在这种情况下,当您想从接口强制时,该方法将返回object,但object的类型不是接口的类型,而是类本身,然后您可以这样编写:

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

class Foo implements iFoo {
    public function bar(string $baz): self  {...}
}
它从PHP7.4开始工作。

PHP8将添加“静态返回类型”,这将解决您的问题


查看此RFC:

我认为这是PHP返回类型暗示中的一个错误,也许您应该将其作为一个提示来提出;但是任何补丁都不太可能在这么晚的阶段进入PHP7.1。由于7.1的最后一个测试版几天前已经上线,任何补丁都不太可能出于兴趣而进入7.1.0,你在哪里阅读你对
self
返回类型应该如何工作的解释?@Adam:
self
的意思似乎是“返回你调用的实例,而不是实现相同接口的其他实例”。我似乎记得Java有一个类似的返回类型(尽管我已经有一段时间没有做任何Java编程了)Hi Gordon。除非有文件证明它在某个地方起作用,否则我不会指望什么是合乎逻辑的现实。对于我要描述的情况,我会尽可能声明性地使用iFoo作为返回类型。有没有一种情况下,这实际上是行不通的?(我意识到这是“建议/意见”,而不是“答案”声明
self
,类似于声明
MyClass::class
?@Laser是的。但是如果Foo实现了iFoo,那么Foo是根据类型iFoo的定义,也就是说,我本来希望
static
的返回类型提示起作用,但它甚至没有被识别出来。我想,情况就是这样,我就是这样做的我将要解决这个问题。但是,如果可能的话,我宁愿只使用:self,因为如果一个类正在实现一个接口,那么self的返回值隐式地也是接口实例的返回值。Paul,删除你在这里删除的注释实际上是有害的,因为(a)它会丢失import