PHP 7 接口,返回类型提示和自我
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39068983/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP 7 interfaces, return type hinting and self
提问by GordonM
UPDATE: PHP 7.4 now does support covariance and contravariancewhich addresses the major issue raised in this question.
更新:PHP 7.4 现在确实支持协变和逆变,这解决了这个问题中提出的主要问题。
I have run into something of an issue with using return type hinting in PHP 7. My understanding is that hinting : self
means that you intend for an implementing class to return itself. Therefore I used : self
in my interfaces to indicate that, but when I tried to actually implement the interface I got compatibility errors.
我在 PHP 7 中使用返回类型提示时遇到了一些问题。我的理解是提示: self
意味着您打算让实现类返回自身。因此,我: self
在我的接口中使用了这一点,但是当我尝试实际实现该接口时,我遇到了兼容性错误。
The following is a simple demonstration of the issue I've run into:
以下是我遇到的问题的简单演示:
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");
The expected output was:
预期的输出是:
Fred Wilma Barney Betty
弗雷德·威尔玛·巴尼饰 Betty
What I actually get is:
我实际得到的是:
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
PHP 致命错误:Foo::bar(int $baz) 的声明:Foo 必须与 iFoo::bar(int $baz) 兼容:第 7 行 test.php 中的 iFoo
The thing is Foo is an implementation of iFoo, so as far as I can tell the implementation should be perfectly compatible with the given interface. I could presumably fix this issue by changing either the interface or the implementing class (or both) to return hint the interface by name instead of using self
, but my understanding is that semantically self
means "return the instance of the class you just called the method on". Therefore changing it to the interface would mean in theory that I could return any instance of something that implements the interface when my intent is for the invoked instance is what will be returned.
事情是 Foo 是 iFoo 的一个实现,所以据我所知,该实现应该与给定的接口完全兼容。我大概可以通过更改接口或实现类(或两者)来通过名称而不是 using 返回提示接口来解决这个问题self
,但我的理解是语义上的self
意思是“返回您刚刚调用该方法的类的实例”。因此,将其更改为接口在理论上意味着我可以返回实现接口的任何实例,当我的意图是被调用的实例是将返回的内容时。
Is this an oversight in PHP or is this a deliberate design decision? If it's the former is there any chance of seeing it fixed in PHP 7.1? If not then what is the correct way of return hinting that your interface expects you to return the instance you just called the method on for chaining?
这是 PHP 中的疏忽还是故意的设计决定?如果是前者,是否有可能在 PHP 7.1 中看到它已修复?如果不是,那么提示您的接口希望您返回刚刚调用该方法进行链接的实例的正确返回方式是什么?
回答by user3942918
self
does not refer to the instance, it refers to the current class. There is no way for an interface to specify that the same instancemust be returned - using self
in the manner you're attempting would only enforce that the returned instance be of the same class.
self
不引用实例,它引用当前类。接口无法指定必须返回相同的实例-self
以您尝试的方式使用只会强制返回的实例属于同一类。
That said, return type declarations in PHP must be invariant while what you're attempting is covariant.
也就是说,PHP 中的返回类型声明必须是不变的,而您正在尝试的是协变的。
Your use of self
is equivalent to:
您的使用self
相当于:
interface iFoo
{
public function bar (string $baz) : iFoo;
}
class Foo implements iFoo
{
public function bar (string $baz) : Foo {...}
}
which is not allowed.
这是不允许的。
The Return Type Declarations RFChas this to say:
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.
继承期间声明的返回类型的执行是不变的;这意味着当子类型覆盖父方法时,子类型的返回类型必须与父方法完全匹配并且不能省略。如果父级未声明返回类型,则允许子级声明返回类型。
...
该 RFC 最初提出协变返回类型,但由于一些问题改为不变。可以在将来的某个时候添加协变返回类型。
For the time being at least the best you can do is:
至少目前你能做的最好的是:
interface iFoo
{
public function bar (string $baz) : iFoo;
}
class Foo implements iFoo
{
public function bar (string $baz) : iFoo {...}
}
回答by Gabor
It also can be a solution, that you don't define explicitly the return type in the Interface, only in the PHPDoc and then you can define the certain return type in the implementations:
它也可以是一个解决方案,您没有在接口中明确定义返回类型,只在 PHPDoc 中定义,然后您可以在实现中定义某些返回类型:
interface iFoo
{
public function bar (string $baz);
}
class Foo implements iFoo
{
public function bar (string $baz) : Foo {...}
}
回答by instead
In case, when You want to force from interface, that method will return object, but type of object won't be the type of interface, but the class itself, then You can write it this way:
如果你想从接口强制,该方法将返回对象,但对象的类型不会是接口的类型,而是类本身,那么你可以这样写:
interface iFoo {
public function bar (string $baz) : object;
}
class Foo implements iFoo {
public function bar (string $baz) : self {...}
}
It's working since PHP 7.4.
它从 PHP 7.4 开始工作。
回答by Moshe Katz
This looks like the expected behavior to me.
这对我来说似乎是预期的行为。
Just change your Foo::bar
method to return iFoo
instead of self
and be done with it.
只需更改您的Foo::bar
方法以返回iFoo
而不是self
并完成它。
Explanation:
解释:
self
as used in the interface means "an object of type iFoo
."self
as used in the implementation means "an object of type Foo
."
self
接口中使用的意思是“一个类型的对象” iFoo
。self
实现中使用的意思是“一个类型的对象” Foo
。
Therefore, the return types in the interface and the implementation are clearly not the same.
因此,接口和实现中的返回类型显然是不一样的。
One of the comments mentions Java and whether you would have this issue. The answer is yes, you would have the same issue if Java allowed you to write code like that -- which it doesn't.Since Java requires you to use the name of the type instead of PHP's self
shortcut, you are never going to actually see this. (See herefor a discussion of a similarissue in Java.)
其中一条评论提到了 Java 以及您是否会遇到此问题。答案是肯定的,如果 Java 允许您编写这样的代码,您会遇到同样的问题——但事实并非如此。由于 Java 要求您使用类型的名称而不是 PHP 的self
快捷方式,因此您永远不会真正看到这一点。(有关Java 中类似问题的讨论,请参见此处。)