繁体   English   中英

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

[英]PHP 7 interfaces, return type hinting and self

更新:PHP 7.4 现在确实支持协变和逆变,这解决了这个问题中提出的主要问题。


我在 PHP 7 中使用返回类型提示时遇到了一些问题。我的理解是提示: self意味着您打算实现 class 以返回自身。 因此,我在我的界面中使用: 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");

预期的 output 是:

弗雷德·威尔玛·巴尼·贝蒂

我实际得到的是:

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

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

这是 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  {...}
}

这是不允许的。


返回类型声明 RFC这样的说法

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

...

该 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  {...}
}

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

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

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

它从 PHP 7.4 开始工作。

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

查看此 RFC: https : //wiki.php.net/rfc/static_return_type

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

interface A {
 function f() {}
}

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

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

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

这对我来说似乎是预期的行为。

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

说明:

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM