【问题标题】:How to override trait function and call it from the overridden function?如何覆盖特征函数并从被覆盖的函数中调用它?
【发布时间】:2012-08-09 23:15:54
【问题描述】:

场景:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    use A;

    function calc($v) {
        $v++;
        return A::calc($v);
    }
}

print (new MyClass())->calc(2); // should print 4

这段代码不起作用,我找不到像继承一样调用特征函数的方法。我试过打电话给self::calc($v)static::calc($v)parent::calc($v)A::calc($v) 和以下:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    use A {
        calc as traitcalc;
    }

    function calc($v) {
        $v++;
        return traitcalc($v);
    }
}

没有任何作用。

有没有办法让它工作,或者我必须完全覆盖比这更复杂的特征函数:)

【问题讨论】:

    标签: php traits


    【解决方案1】:

    你的最后一个就快到了:

    trait A {
        function calc($v) {
            return $v+1;
        }
    }
    
    class MyClass {
        use A {
            calc as protected traitcalc;
        }
    
        function calc($v) {
            $v++;
            return $this->traitcalc($v);
        }
    }
    

    特征不是一个类。您不能直接访问其成员。它基本上只是自动复制和粘贴...

    【讨论】:

    • 只是为了澄清——一旦你的类定义了相同的方法,它会自动覆盖特征的。正如@ircmaxell 提到的,当它为空时,该特征会填充方法。
    • @PhillipWhelan 如果您可以添加有关“无法按预期工作”的更多信息,那就太好了。这样写对理解会出现什么样的错误行为没有多大帮助,也不能向我们保证这不是您的暂时错误。也许有一些关于你正在谈论的问题的问题? (最终)谢谢。
    • 问题是特征中的所有其他方法都不再包括在内。
    • 仅供参考:如果您的特征函数是静态的,您可以通过调用A::calc(1)访问该函数
    • 正如 Phillip 提到的(我认为),您将如何为一种特征的一种方法执行此操作,同时仍然像正常一样包含具有相同特征的所有其他方法?最好不要明确引用每种方法。
    【解决方案2】:

    如果类直接实现方法,则不会使用traits版本。也许你在想的是:

    trait A {
        function calc($v) {
            return $v+1;
        }
    }
    
    class MyClass {
        function calc($v) {
            return $v+2;
        }
    }
    
    class MyChildClass extends MyClass{
    }
    
    class MyTraitChildClass extends MyClass{
        use A;
    }
    
    print (new MyChildClass())->calc(2); // will print 4
    
    print (new MyTraitChildClass())->calc(2); // will print 3
    

    因为子类不直接实现该方法,所以如果有其他使用父类的方法,它们将首先使用该特征的方法。

    如果你愿意,特征可以使用父类中的方法(假设你知道方法会在那里)例如

    trait A {
        function calc($v) {
            return parent::calc($v*3);
        }
    }
    // .... other code from above
    print (new MyTraitChildClass())->calc(2); // will print 8 (2*3 + 2)
    

    您还可以提供覆盖方法,但仍按如下方式访问 trait 方法:

    trait A {
        function trait_calc($v) {
            return $v*3;
        }
    }
    
    class MyClass {
        function calc($v) {
            return $v+2;
        }
    }
    
    
    class MyTraitChildClass extends MyClass{
        use A {
          A::trait_calc as calc;
        }
    }
    
    
    class MySecondTraitChildClass extends MyClass{
        use A {
          A::trait_calc as calc;
        }
    
        public function calc($v) {
          return $this->trait_calc($v)+.5;
        }
    }
    
    
    print (new MyTraitChildClass())->calc(2); // will print 6
    echo "\n";
    print (new MySecondTraitChildClass())->calc(2); // will print 6.5
    

    你可以在http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5看到它的工作原理

    【讨论】:

      【解决方案3】:

      如果有兴趣,另一种方法 - 使用额外的中间类来使用正常的 OOO 方式。这简化了 parent::methodname

      的使用
      trait A {
          function calc($v) {
              return $v+1;
          }
      }
      
      // an intermediate class that just uses the trait
      class IntClass {
          use A;
      }
      
      // an extended class from IntClass
      class MyClass extends IntClass {
          function calc($v) {
              $v++;
              return parent::calc($v);
          }
      }
      

      【讨论】:

      • 这种方法将消除您使用traits 所拥有的任何优势。就像在多个类中组合多个特征(例如,一个类中的特征 A、B、另一个类中的特征 B、C、D,另一个类中的特征 A、C 等等)
      • 不,使用这种方法,您仍然拥有拥有特质的优势。您可以在 IntClass 中使用此特性,但如果您愿意,也可以在许多其他类中使用它。如果仅在 IntClass 中使用 Trait,它将毫无用处。在这种情况下,最好将 calc() 方法直接放在该类中。
      • 这完全不适合我。 ScreenablePerson::save() 存在,Candidate 使用 Validating 特征并扩展 ScreenablePerson,并且所有三个类都有 save()
      • 我同意这很有趣。它有效,因为对于扩展类,特征方法实际上存在于父类中。我个人认为,如果您发现自己处于这种情况会很方便,但我不建议您按设计这样做。
      【解决方案4】:

      使用另一个特征:

      trait ATrait {
          function calc($v) {
              return $v+1;
          }
      }
      
      class A {
          use ATrait;
      }
      
      trait BTrait {
          function calc($v) {
              $v++;
              return parent::calc($v);
          }
      }
      
      class B extends A {
          use BTrait;
      }
      
      print (new B())->calc(2); // should print 4
      

      【讨论】:

        【解决方案5】:

        另一种变体:在 trait 中定义两个函数,一个执行实际任务的受保护函数,另一个调用受保护函数的公共函数。

        这只是让类在想要覆盖函数时不必弄乱“use”语句,因为它们仍然可以在内部调用受保护的函数。

        trait A {
            protected function traitcalc($v) {
                return $v+1;
            }
        
            function calc($v) {
                return $this->traitcalc($v);
            }
        }
        
        class MyClass {
            use A;
        
            function calc($v) {
                $v++;
                return $this->traitcalc($v);
            }
        }
        
        class MyOtherClass {
            use A;
        }
        
        
        print (new MyClass())->calc(2); // will print 4
        
        print (new MyOtherClass())->calc(2); // will print 3
        

        【讨论】:

          猜你喜欢
          • 2010-10-25
          • 1970-01-01
          • 2020-04-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-12
          • 1970-01-01
          相关资源
          最近更新 更多