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();
    }
}

这是这里发生的事情:

    使用
  1. T1::_doStuff(),别名为_traitDoStuff()。根据 PHP 文档,这不会重命名该方法,而只会添加一个别名。所以在这一点上,_doStuff()_traitDoStuff()都作为抽象保护方法存在。
  2. 使用
  3. T2::_doStuff(),别名为_traitDoStuff()。根据 PHP 文档,由于优先级,两者都被 T2 的方法覆盖。所以此时,T1::_doStuff()已经不存在了。即使会,也会被T2::_doStuff().
  4. 执行
  5. 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; },您会看到同样的崩溃。