PHP 抽象特征方法的实现
Implementation of abstract trait method in PHP
我有一个 class 看起来像 this。我也将其粘贴在下面以供参考:
<?php
trait T1
{
abstract protected function _doStuff();
}
trait T2
{
protected function _doStuff()
{
echo "Doing stuff in trait\n";
}
}
class C
{
use T1 {
_doStuff as _traitDoStuff;
}
use T2 {
_doStuff as _traitDoStuff;
}
protected function _doStuff()
{
echo "Doing stuff in class\n";
$this->_traitDoStuff();
}
}
这是这里发生的事情:
使用T1::_doStuff()
,别名为_traitDoStuff()
。根据 PHP 文档,这不会重命名该方法,而只会添加一个别名。所以在这一点上,_doStuff()
和_traitDoStuff()
都作为抽象保护方法存在。
使用T2::_doStuff()
,别名为_traitDoStuff()
。根据 PHP 文档,由于优先级,两者都被 T2
的方法覆盖。所以此时,T1::_doStuff()
已经不存在了。即使会,也会被T2::_doStuff()
. 执行
C
实现 _doStuff()
,它调用 _traitDoStuff()
。此时不管用_doStuff()
的哪个实现,很明显是实现了这个方法,所以满足T1::_doStuff()
定义的契约,否则不存在
然而,当我 运行 这样做时,它给出了以下错误:
Fatal error: Class C contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (C::_doStuff)
从 3v4l 可以看出,这在 PHP 5.4 到 7.2 的任何地方都有体现,这有点暗示这不是早期特征错误。有人可以给我解释一下吗?
更新
显然,我只是忘记指定我使用别名的方法,即 T1::_doStuff as _traitDoStuff
。
也许是这样的?
<?php
trait T1
{
abstract protected function _doStuff();
}
trait T2
{
protected function _doStuff()
{
echo "Doing stuff in trait\n";
}
}
class C
{
use T1 {
T1::_doStuff as _traitDoStuff;
}
use T2 {
T2::_doStuff as _traitDoStuff;
}
protected function _doStuff()
{
echo "Doing stuff in class\n";
$this->_traitDoStuff();
}
}
缺少 class 范围解析运算符(T1::
和 T2::
)掩盖了一个更深层次的问题。考虑这些 simpler cases:
trait A {
abstract public function foo();
}
class B {
use A; // works
public function foo() {}
}
class C {
use A { A::foo as traitFoo; } // works, provided this:
public function traitFoo() {} // <-- is present
public function foo() {}
}
class D {
use A { A::foo as traitFoo; } // does not work by itself
public function foo() {}
}
实际发生了什么:别名抽象方法在您的 class 中引入了 另一个 抽象方法。 PHP.net 引擎错误消息具有极大的误导性:
Fatal error: Class D contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (D::foo)
但是 HHVM 引擎提供的信息要多得多:
Fatal error: Uncaught Error: Class D contains abstract method (traitFoo) and must therefore be declared abstract or implement the remaining methods
Horizontal Reuse (aka Trait) RFC does not explicitly discuss this case, so this is arguably a bug. Feel free to report it at bugs.php.net.
那么为什么添加 class 解析运算符可以修复它?
当您添加 class-scope 解析运算符时,其中包含:
use T2 { T2::_doStuff as _traitDoStuff; }
您满足了 "phantom" abstract protected function _traitDoStuff
介绍的:
use T1 { T1::_doStuff as _traitDoStuff; }
如果您删除了别名,例如 use T2;
或 use T2 { T2::_doStuff as _anotherMethod; }
,您会看到同样的崩溃。
我有一个 class 看起来像 this。我也将其粘贴在下面以供参考:
<?php
trait T1
{
abstract protected function _doStuff();
}
trait T2
{
protected function _doStuff()
{
echo "Doing stuff in trait\n";
}
}
class C
{
use T1 {
_doStuff as _traitDoStuff;
}
use T2 {
_doStuff as _traitDoStuff;
}
protected function _doStuff()
{
echo "Doing stuff in class\n";
$this->_traitDoStuff();
}
}
这是这里发生的事情:
-
使用
T1::_doStuff()
,别名为_traitDoStuff()
。根据 PHP 文档,这不会重命名该方法,而只会添加一个别名。所以在这一点上,_doStuff()
和_traitDoStuff()
都作为抽象保护方法存在。
使用T2::_doStuff()
,别名为_traitDoStuff()
。根据 PHP 文档,由于优先级,两者都被T2
的方法覆盖。所以此时,T1::_doStuff()
已经不存在了。即使会,也会被T2::_doStuff()
. 执行
C
实现_doStuff()
,它调用_traitDoStuff()
。此时不管用_doStuff()
的哪个实现,很明显是实现了这个方法,所以满足T1::_doStuff()
定义的契约,否则不存在
然而,当我 运行 这样做时,它给出了以下错误:
Fatal error: Class C contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (C::_doStuff)
从 3v4l 可以看出,这在 PHP 5.4 到 7.2 的任何地方都有体现,这有点暗示这不是早期特征错误。有人可以给我解释一下吗?
更新
显然,我只是忘记指定我使用别名的方法,即 T1::_doStuff as _traitDoStuff
。
也许是这样的?
<?php
trait T1
{
abstract protected function _doStuff();
}
trait T2
{
protected function _doStuff()
{
echo "Doing stuff in trait\n";
}
}
class C
{
use T1 {
T1::_doStuff as _traitDoStuff;
}
use T2 {
T2::_doStuff as _traitDoStuff;
}
protected function _doStuff()
{
echo "Doing stuff in class\n";
$this->_traitDoStuff();
}
}
缺少 class 范围解析运算符(T1::
和 T2::
)掩盖了一个更深层次的问题。考虑这些 simpler cases:
trait A {
abstract public function foo();
}
class B {
use A; // works
public function foo() {}
}
class C {
use A { A::foo as traitFoo; } // works, provided this:
public function traitFoo() {} // <-- is present
public function foo() {}
}
class D {
use A { A::foo as traitFoo; } // does not work by itself
public function foo() {}
}
实际发生了什么:别名抽象方法在您的 class 中引入了 另一个 抽象方法。 PHP.net 引擎错误消息具有极大的误导性:
Fatal error: Class D contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (D::foo)
但是 HHVM 引擎提供的信息要多得多:
Fatal error: Uncaught Error: Class D contains abstract method (traitFoo) and must therefore be declared abstract or implement the remaining methods
Horizontal Reuse (aka Trait) RFC does not explicitly discuss this case, so this is arguably a bug. Feel free to report it at bugs.php.net.
那么为什么添加 class 解析运算符可以修复它?
当您添加 class-scope 解析运算符时,其中包含:
use T2 { T2::_doStuff as _traitDoStuff; }
您满足了 "phantom" abstract protected function _traitDoStuff
介绍的:
use T1 { T1::_doStuff as _traitDoStuff; }
如果您删除了别名,例如 use T2;
或 use T2 { T2::_doStuff as _anotherMethod; }
,您会看到同样的崩溃。