【问题标题】:Implementation of abstract trait method in PHPPHP中抽象特征方法的实现
【发布时间】:2018-05-16 15:26:15
【问题描述】:

我有一个看起来像 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. 使用T2::_doStuff(),别名为_traitDoStuff()。根据 PHP 文档,由于优先级,两者都被 T2 的方法覆盖。所以此时,T1::_doStuff() 不再存在。即使会,也会由T2::_doStuff()实现。
  3. C 实现 _doStuff(),它调用 _traitDoStuff()。至此,不管_doStuff()的哪个实现,显然都实现了这个方法,所以T1::_doStuff()定义的契约满足,或者不存在。

然而,当我运行它时,它给出了以下错误:

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

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

更新

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

【问题讨论】:

  • 不应该将 T1 实现为接口,而不是 trait 吗?
  • 不确定这是什么意思,@bassxzero。
  • 我知道接口是什么。我不知道你的句子是什么意思。我有一个特质。我不明白将特征实现为接口意味着什么。
  • C 类不应该实现 T1 接口。将你的抽象函数do_stuff 放入一个名为T1 的接口中,然后让C 类实现T1 接口。

标签: php traits


【解决方案1】:

缺少类范围解析运算符(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 个抽象方法,因此必须声明为抽象方法或实现其余方法 (D::foo)

但 HHVM 引擎提供的信息要多得多:

致命错误:未捕获错误:D 类包含抽象方法 (traitFoo),因此必须声明为抽象方法或实现其余方法

Horizontal Reuse (aka Trait) RFC 没有明确讨论这种情况,所以这可以说是一个错误。请随时通过bugs.php.net 举报。


那么为什么添加类解析运算符会修复它?

当您添加类范围解析运算符时,其中包含:

use T2 { T2::_doStuff as _traitDoStuff; }

你满足了由以下人介绍的“幻影”abstract protected function _traitDoStuff:

use T1 { T1::_doStuff as _traitDoStuff; }

如果您删除了别名,例如 use T2; 或 use T2 { T2::_doStuff as _anotherMethod; },您会看到同样的崩溃。

【讨论】:

  • 谢谢你的解释!!
  • 感谢您的解释!
【解决方案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();
    }
}

【讨论】:

  • 我添加了T1:: 和T2::。我在phptester.net 上尝试了此代码,但没有收到任何错误。希望对你有所帮助
  • 但我不确定为什么您的第一个代码不起作用。我认为这两种语法都是正确的,所以如果有人知道为什么,请发帖解释:)
  • 是的。我也想知道_doStuff as _traitDoStuff到底是做什么的。
  • 我会解释... 第一个代码没有工作,因为在 T1 特征中,您已将 function _doStuff() 声明为 abstract 并且您在 class C 中使用的抽象方法未实现。这就是它背后的原因。
  • @Kozame 我对explanation 进行了攻击。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-03
  • 1970-01-01
  • 1970-01-01
  • 2020-04-21
  • 2012-11-26
相关资源
最近更新 更多