PHP 7 个接口,return 类型提示和自我
PHP 7 interfaces, return type hinting and self
UPDATE: PHP 7.4 现在 does support covariance and contravariance 解决了这个问题中提出的主要问题。
我 运行 在 PHP 7 中使用 return 类型提示时遇到了一些问题。我的理解是提示 : self
意味着您打算实现 class 到 return 本身。因此,我在我的接口中使用 : 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");
预期输出为:
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 是 iFoo 的一个实现,据我所知,这个实现应该与给定的接口完全兼容。我大概可以通过将接口或实现 class(或两者)更改为 return 通过名称而不是使用 self
提示接口来解决此问题,但我的理解是语义 self
表示 "return the instance of the class you just called the method on"。因此,将其更改为接口在理论上意味着我可以 return 实现该接口的任何实例,而我的意图是调用的实例将被 return 编辑。
这是 PHP 中的疏忽还是故意的设计决定?如果是前者,是否有机会看到它在 PHP 7.1 中得到修复?如果不是,那么 return 暗示您的界面希望您 return 您刚刚调用方法进行链接的实例的正确方法是什么?
编辑说明:以下答案已过时。作为 php PHP7.4.0,以下是完全合法的:
<?php
Interface I{
public static function init(?string $url): self;
}
class C implements I{
public static function init(?string $url): self{
return new self();
}
}
$o = C::init("foo");
var_dump($o);
- 3v4l: https://3v4l.org/VYbGn
原回答:
self
不是指实例,而是指当前的class。接口无法指定相同的 实例 必须 returned - 以您尝试的方式使用 self
只会强制执行returned 实例是相同的 class。
就是说,PHP 中的 return 类型声明必须是不变的,而您尝试的是协变的。
您对 self
的使用等同于:
interface iFoo
{
public function bar (string $baz) : iFoo;
}
class Foo implements iFoo
{
public function bar (string $baz) : Foo {...}
}
这是不允许的。
Return Type Declarations RFC has 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.
目前至少你能做的最好的是:
interface iFoo
{
public function bar (string $baz) : iFoo;
}
class Foo implements iFoo
{
public function bar (string $baz) : iFoo {...}
}
这看起来像是我预期的行为。
只需将您的 Foo::bar
方法更改为 return iFoo
而不是 self
即可完成。
解释:
self
在界面中使用表示 "an object of type iFoo
."
实施中使用的 self
表示 "an object of type Foo
."
因此,接口和实现中的return类型显然不一样。
其中一条评论提到 Java 以及您是否会遇到此问题。答案是肯定的,你会遇到同样的问题 如果 Java 允许你这样写代码——但事实并非如此。 因为 Java 需要你使用类型的名称而不是 PHP 的 self
快捷方式,您将永远不会真正看到它。 (有关 Java 中 类似 问题的讨论,请参见 here。)
它也可以是一个解决方案,您不在接口中明确定义 return 类型,仅在 PHPDoc 中定义,然后您可以在实现中定义特定的 return 类型:
interface iFoo
{
public function bar (string $baz);
}
class Foo implements iFoo
{
public function bar (string $baz) : Foo {...}
}
万一,当你想从接口强制执行时,该方法将 return 对象,但对象的类型将不是接口的类型,而是 class 本身,那么你可以这样写:
interface iFoo {
public function bar(string $baz): object;
}
class Foo implements iFoo {
public function bar(string $baz): self {...}
}
它从 PHP 7.4.
开始工作
PHP 8 将添加“静态 return 类型”,这将解决您的问题。
UPDATE: PHP 7.4 现在 does support covariance and contravariance 解决了这个问题中提出的主要问题。
我 运行 在 PHP 7 中使用 return 类型提示时遇到了一些问题。我的理解是提示 : self
意味着您打算实现 class 到 return 本身。因此,我在我的接口中使用 : 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");
预期输出为:
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 是 iFoo 的一个实现,据我所知,这个实现应该与给定的接口完全兼容。我大概可以通过将接口或实现 class(或两者)更改为 return 通过名称而不是使用 self
提示接口来解决此问题,但我的理解是语义 self
表示 "return the instance of the class you just called the method on"。因此,将其更改为接口在理论上意味着我可以 return 实现该接口的任何实例,而我的意图是调用的实例将被 return 编辑。
这是 PHP 中的疏忽还是故意的设计决定?如果是前者,是否有机会看到它在 PHP 7.1 中得到修复?如果不是,那么 return 暗示您的界面希望您 return 您刚刚调用方法进行链接的实例的正确方法是什么?
编辑说明:以下答案已过时。作为 php PHP7.4.0,以下是完全合法的:
<?php
Interface I{
public static function init(?string $url): self;
}
class C implements I{
public static function init(?string $url): self{
return new self();
}
}
$o = C::init("foo");
var_dump($o);
- 3v4l: https://3v4l.org/VYbGn
原回答:
self
不是指实例,而是指当前的class。接口无法指定相同的 实例 必须 returned - 以您尝试的方式使用 self
只会强制执行returned 实例是相同的 class。
就是说,PHP 中的 return 类型声明必须是不变的,而您尝试的是协变的。
您对 self
的使用等同于:
interface iFoo
{
public function bar (string $baz) : iFoo;
}
class Foo implements iFoo
{
public function bar (string $baz) : Foo {...}
}
这是不允许的。
Return Type Declarations RFC has 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.
目前至少你能做的最好的是:
interface iFoo
{
public function bar (string $baz) : iFoo;
}
class Foo implements iFoo
{
public function bar (string $baz) : iFoo {...}
}
这看起来像是我预期的行为。
只需将您的 Foo::bar
方法更改为 return iFoo
而不是 self
即可完成。
解释:
self
在界面中使用表示 "an object of type iFoo
."
实施中使用的 self
表示 "an object of type Foo
."
因此,接口和实现中的return类型显然不一样。
其中一条评论提到 Java 以及您是否会遇到此问题。答案是肯定的,你会遇到同样的问题 如果 Java 允许你这样写代码——但事实并非如此。 因为 Java 需要你使用类型的名称而不是 PHP 的 self
快捷方式,您将永远不会真正看到它。 (有关 Java 中 类似 问题的讨论,请参见 here。)
它也可以是一个解决方案,您不在接口中明确定义 return 类型,仅在 PHPDoc 中定义,然后您可以在实现中定义特定的 return 类型:
interface iFoo
{
public function bar (string $baz);
}
class Foo implements iFoo
{
public function bar (string $baz) : Foo {...}
}
万一,当你想从接口强制执行时,该方法将 return 对象,但对象的类型将不是接口的类型,而是 class 本身,那么你可以这样写:
interface iFoo {
public function bar(string $baz): object;
}
class Foo implements iFoo {
public function bar(string $baz): self {...}
}
它从 PHP 7.4.
开始工作PHP 8 将添加“静态 return 类型”,这将解决您的问题。