PHP中抽象特征方法的实现

时间:2018-05-16 15:26:15

标签: php traits

我有一个类似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. C实现_doStuff(),调用_traitDoStuff()。此时,无论使用_doStuff()的哪个实现,很明显这个方法已经实现,因此T1::_doStuff()定义的契约得到满足,或者不存在。
  5. 然而,当我运行它时,它会出现以下错误:

      

    致命错误:C类包含1个抽象方法,因此必须声明为abstract或实现其余方法(C :: _ doStuff)

    从3v4l可以看出,这从PHP 5.4到7.2无处不在,这有点暗示这不是早期的特征错误。有人可以向我解释一下吗?

    更新

    显然,我只是忘了指定我是别名的方法,即T1::_doStuff as _traitDoStuff

2 个答案:

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