PHP trait方法冲突:trait";“继承”;以及特征层次结构


PHP trait method conflicts: trait "inheritance" and trait hierarchies

更新:我并不是唯一一个思考这个问题的人,它似乎确实是一个bug。请参见此处。修复的那一天将是美妙的一天!:)


它最初是I love PHP traits! I'm going to use them everywhere! ^_^,现在已经变成了Thought Exercise / Learning Experience >_<

考虑以下示例:

trait TheErrorOfYourWays{
   public function booboo(){
       echo 'You had a booboo :(';
   }
}
trait SpectacularStuff1 {
    use TheErrorOfYourWays; 
}
trait SpectacularStuff2 {
    use TheErrorOfYourWays;
}
class DoSomethingSpectacular {
    use SpectacularStuff1, SpectacularStuff2;
}

这导致(明显不那么明显):

致命错误:由于DoSomethingSpectacular上存在与其他特征方法的冲突,因此尚未应用特征方法booboo。

所以我的问题是:如何解决特征中的方法冲突?有可能实现重叠性状的"遗传"吗?如果是这样,什么是"正确"的方法

我为什么要这样做:

  1. 我想创建自我包含的特征和类(混搭风格)如果这是可能的,我想说"使用",然后神奇的东西一定会发生。不必挠头思考,"现在这个特性又在哪个命名空间中?"等等
  2. 当我做一些"冒险"的事情,发现自己无意中制造了冲突时,不必快速编辑类别和特征
  3. 当时看来是个好主意

我尝试过的:

  1. PHP手册
  2. 谷歌
  3. 所以包括这个问题->不是这个场景的正确答案
  4. 发现了这个,但我使用的是PHP版本5.5.1。它是固定的,对吧?对吧
  5. 一个由"as"、别名组成的奇妙阵列,甚至在不同的地方、时代、宇宙等。包括但不限于:

    trait SpectacularStuff1 {
       use TheErrorOfYourWays{
          TheErrorOfYourWays::booboo as booboo1;
       }
    }
    trait SpectacularStuff2 {
       use TheErrorOfYourWays{
          TheErrorOfYourWays::booboo as booboo2;
       }
    }
    class DoSomethingSpectacular {
       use SpectacularStuff1, SpectacularStuff2 {
          /* Tried separately, but included here for brevity's sake */
          SpectacularStuff1::booboo as booboo3;
          SpectacularStuff2::booboo as booboo4;
       }
    }
    

    use TheErrorOfYourWays as Erroneous1;
    trait SpectacularStuff1 {
        use Erroneous1{
            Erroneous1::booboo as booboo1;
        }
    }
    use TheErrorOfYourWays as Erroneous2;
    trait SpectacularStuff2 {
        use Erroneous2{
            Erroneous2::booboo as booboo2;
        }
    }
    

我理解:

  1. 我可以将TheErrorOfYourWays更改为一个类,并使booboo()静态,但我想了解这种特定的特征行为
  2. 我可以从特性中删除TheErrorOfYourWays并在类中使用它,但这很难"自给自足"。每次我使用这些特性时,我都必须记住在类中使用TheErrorOfYourWays,即使我没有直接从类中调用booboo()。听起来很危险
  3. 我可能犯了一些新手语法错误,或者未能深入理解混叠。如果是,请。。。解释慢慢地
  4. 也许有更好的方法可以做到这一点。如果是,请。。。解释慢慢地
  5. 我可能过早地对PHP充满热情,而PHP还没有做到这一点。让我轻轻地下来

谢谢

您需要使用关键字insteadof来解决Traits中的冲突。

Source

重写您的

class DoSomethingSpectacular {
   use SpectacularStuff1, SpectacularStuff2 {
      /* Tried separately, but included here for brevity's sake */
      SpectacularStuff1::booboo as booboo3;
      SpectacularStuff2::booboo as booboo4;
   }
}

class DoSomethingSpectacular {
    use SpectacularStuff1, SpectacularStuff2 
    {
     SpectacularStuff1::booboo insteadof SpectacularStuff2;
     SpectacularStuff2::booboo insteadof SpectacularStuff1;
    }
}

将解决冲突。

所以非官方的"官方"答案是:

你可以在没有混叠的情况下完成它,而不是代替或做任何事情!但还没有。。。

我从5.5.1升级到5.5.6,但都是徒劳的。当修复程序可用时,我将更新此答案。值得注意的是,您可以直接调用trait静态函数。以下示例有效:

trait TheErrorOfYourWays{
    public static function booboo($thisTrait){
        echo 'You had a booboo :( in '.$thisTrait.'<br>';
    }
}
trait SpectacularStuff1 {
    public function boobooTest1(){
        TheErrorOfYourWays::booboo(__TRAIT__);
    }
}
trait SpectacularStuff2 {
    public function boobooTest2(){
        TheErrorOfYourWays::booboo(__TRAIT__);
    }
}
class DoSomethingSpectacular {
    use SpectacularStuff1, SpectacularStuff2;
}
$boobooAChoo = new DoSomethingSpectacular();
$boobooAChoo->boobooTest1(); // You had a booboo :( in SpectacularStuff1
$boobooAChoo->boobooTest2(); // You had a booboo :( in SpectacularStuff2

是的,是的,你也可以用一个类来做到这一点,但上一季的类是sooo

我找到了另一种修复临时问题的方法:

trait A {
   public function foo(){
       echo 'foo';
   }
}
trait B {
   public function foofoo(){
       return $this->foo () . 'foo';
   }
}
trait C {
   public function foobar(){
       return $this->foo () . 'bar';
   }
}
class DoSomethingSpectacular {
    use A, B, C;
    public function foobarfoofoo () {
        echo $this->foobar () . $this->foofoo ();
    }
}

它的工作原理是:)

一个小技巧,只需将函数booboo添加到类DoSomethingSpectacular

<?php
trait TheErrorOfYourWays{
   public function booboo(){
       echo 'You had a booboo :(';
   }
}
trait SpectacularStuff1 {
   use TheErrorOfYourWays{
      TheErrorOfYourWays::booboo as booboo1;
   }
}
trait SpectacularStuff2 {
   use TheErrorOfYourWays{
      TheErrorOfYourWays::booboo as booboo2;
   }
}
class DoSomethingSpectacular {
   use SpectacularStuff1, SpectacularStuff2 {
      /* Tried separately, but included here for brevity's sake */
      SpectacularStuff1::booboo as booboo3;
      SpectacularStuff2::booboo as booboo4;
   }
  //very ugly hack
  public function booboo() {}
}

这是在PHP 7.3:3v4l.org/qulMv中修复的。这意味着,在两个或多个派生特征中使用一个或多个子特征,然后在类中使用派生特征是没有问题的!

请注意,不幸的是,PHP 7.3迁移指南中没有记录它,但它存在于变更日志中(相同的特征方法会在组合过程中引发错误)。

实现这一点的一种方法是使用抽象方法。我称之为";扁平化特征";,或者它也可以被称为";性状的依赖性注入";。这更像是一个广义的解决方案,而不是具体的解决方案。

您的示例可以更改为:

trait TheErrorOfYourWays
{
    public function booboo(): string
    {
        return 'booboo';
    }
}
trait SpectacularStuff1
{
    abstract public function booboo(): string;
}
trait SpectacularStuff2
{
    abstract public function booboo(): string;
}
class DoSomethingSpectacular
{
    use TheErrorOfYourWays;
    use SpectacularStuff1;
    use SpectacularStuff2;
}

这就是我个人认为特质变得强大的地方:你可以写一个完全独立的特质,并在任何你想用的地方使用它。换句话说,它们可以完全独立。这就是为什么我喜欢特性(或者至少PHP特性)。

虽然我更喜欢这样使用特征,但可能会有使特征相互依赖的用例。例如,将一些相关的特征分组为一个大特征。