我有一个类似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()
定义的契约得到满足,或者不存在。然而,当我运行它时,它会出现以下错误:
致命错误:C类包含1个抽象方法,因此必须声明为abstract或实现其余方法(C :: _ doStuff)
从3v4l可以看出,这从PHP 5.4到7.2无处不在,这有点暗示这不是早期的特征错误。有人可以向我解释一下吗?
显然,我只是忘了指定我是别名的方法,即T1::_doStuff as _traitDoStuff
。
答案 0 :(得分:3)
缺少类范围解析运算符(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() {}
}
实际发生的事情:对抽象方法进行别名会在类中引入另一个抽象方法。 PHP.net引擎错误消息极具误导性:
致命错误:D类包含1个抽象方法,因此必须声明为abstract或实现其余方法(D :: foo)
但HHVM引擎的信息量更大:
致命错误:未捕获错误:D类包含抽象方法(traitFoo),因此必须声明为抽象或实现其余方法
Horizontal Reuse (aka Trait) RFC没有明确地讨论这个案例,所以这可以说是一个错误。请随时在bugs.php.net报告。
那么为什么添加类解析运算符会修复它呢?
添加类范围解析运算符时,包含:
use T2 { T2::_doStuff as _traitDoStuff; }
你很满意&#34;幻影&#34; abstract protected function _traitDoStuff
介绍:
use T1 { T1::_doStuff as _traitDoStuff; }
如果您删除了别名,例如use T2;
或use T2 { T2::_doStuff as _anotherMethod; }
,您会看到同样的崩溃。
答案 1 :(得分:2)
这样的事可能吗?
<?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();
}
}