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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP 7接口,返回类型提示和自我相关的知识,希望对你有一定的参考价值。

我在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中修复?如果没有,那么正确的返回提示是什么,你的接口希望你返回刚刚调用方法的实例进行链接的正确方法是什么?

答案

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

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

您使用self相当于:

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

class Foo implements iFoo
{

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

这是不允许的。


Return Type Declarations RFCthis to say

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

...

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


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

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

class Foo implements iFoo
{

    public function bar (string $baz) : iFoo  {...}
}
另一答案

它也可以是一个解决方案,你没有在接口中显式定义返回类型,只在PHPDoc中,然后你可以在实现中定义特定的返回类型:

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

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

这看起来像我预期的行为。

只需更改你的Foo::bar方法返回iFoo而不是self并完成它。

说明:

接口中使用的self表示“iFoo类型的对象”。 实施中使用的self意味着“Foo类型的对象”。

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

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

另一答案

如果你想从接口强制,那个方法会返回对象,但是对象的类型不是接口的类型,而是类本身,那么你可以这样写:

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

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

它从PHP 7.2开始工作。

另一答案

使用PHP 7.3运行一些测试,当我这样做时,我不能让它抱怨(即使是严格的)...

interface A {
 function f() {}
}

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

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

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

以上是关于PHP 7接口,返回类型提示和自我的主要内容,如果未能解决你的问题,请参考以下文章

PHP 7 中的类型提示 - 对象数组

PHP 类型提示与接口和抽象类不相处?

如何从自我生成返回类型提示?

Symfony3 表单组件试图将 null 传递给 PHP 7 中的类型提示方法

PHP中的类型提示返回值/函数

php 字符串和数字比较一些问题