PHP 7 interfaces, return type hinting and self
我在使用PHP7中的返回类型提示时遇到了一些问题。我的理解是,暗示
以下是我遇到的问题的简单演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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"); |
预期输出为:
Fred
Wilma
Barney
Betty
我实际上得到的是:
PHP Fatal error: Declaration of Foo::bar(int $baz): Foo must be compatible with iFoo::bar(int $baz): iFoo in test.php on line 7
问题是foo是i foo的一个实现,据我所知,实现应该与给定的接口完全兼容。我可以通过更改接口或实现类(或两者都更改)来修复这个问题,用名称来返回接口提示,而不是使用
这是PHP中的一个监督,还是一个深思熟虑的设计决策?如果是前者,是否有机会看到它在php 7.1中被修复?如果不是,那么正确的返回方式是什么,暗示接口期望您返回刚刚调用方法进行链接的实例?
也就是说,PHP中的返回类型声明必须是不变的,而您尝试的是协变的。
您使用
1 2 3 4 5 6 7 8 9 10 | interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : Foo {...} } |
这是不允许的。
返回类型声明RFC的意思是:
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.
目前,你能做的至少是:
1 2 3 4 5 6 7 8 9 10 | interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : iFoo {...} } |
它也可以是一种解决方案,即不在接口中显式定义返回类型,只在phpDoc中定义,然后可以在实现中定义特定的返回类型:
1 2 3 4 5 6 7 8 9 | interface iFoo { public function bar (string $baz); } class Foo implements iFoo { public function bar (string $baz) : Foo {...} } |
用php7.3运行一些测试,当我这样做时,我不能让它抱怨(即使是严格的)。
1 2 3 4 5 6 7 | interface A { function f() {} } interface B { function f():self {} } |
要么我的测试坏了,要么PHP改变了一些东西。一般来说,如果您减少可能的返回类型,那么不会破坏OOP。在任何使用该方法的类中,都可以处理任何返回,包括返回类型的子集。参数的情况大致相反。
他们在7.2中实现了这一点。
如果要从接口强制,该方法将返回对象,但对象类型不是接口类型,而是类本身,则可以这样编写:
1 2 3 4 5 6 7 | interface iFoo { public function bar (string $baz) : object; } class Foo implements iFoo { public function bar (string $baz) : self {...} } |
从php7.2开始就开始工作了。
在我看来,这是预期的行为。
只需更改你的
说明:
接口中使用的
因此,接口和实现中的返回类型显然不同。
其中一条评论提到Java和您是否会有这个问题。答案是肯定的,如果Java允许你编写这样的代码,你会遇到同样的问题,而不是这样。因为Java要求你使用类型的名称而不是PHP的EDCOX1×0的快捷方式,所以你永远看不到这一点。(请参见这里讨论Java中的类似问题)。