【问题标题】:PHP's magic method __call on subclassesPHP的魔术方法__call子类
【发布时间】:2009-10-08 01:40:55
【问题描述】:

我的情况最好用一点代码来描述:

class Foo {
    function bar () {
        echo "called Foo::bar()";
    }
}

class SubFoo extends Foo {
    function __call($func) {
        if ($func == "bar") {
            echo "intercepted bar()!";
        }
    }
}

$subFoo = new SubFoo();

// what actually happens:
$subFoo->bar();    // "called Foo:bar()"

// what would be nice:
$subFoo->bar();    // "intercepted bar()!"

我知道我可以通过在子类中重新定义bar()(以及所有其他相关方法)来实现这一点,但就我的目的而言,如果__call 函数可以处理它们会很好。它只会让事情很多更整洁、更易于管理。

这在 PHP 中可行吗?

【问题讨论】:

    标签: php oop magic-methods


    【解决方案1】:

    __call() 仅在没有以其他方式找到该函数时调用,因此您编写的示例是不可能的。

    【讨论】:

      【解决方案2】:

      它不能直接完成,但这是一种可能的选择:

      class SubFoo { // does not extend
          function __construct() {
              $this->__foo = new Foo; // sub-object instead
          }
          function __call($func, $args) {
              echo "intercepted $func()!\n";
              call_user_func_array(array($this->__foo, $func), $args);
          }
      }
      

      这种东西对调试和测试来说是好事,但是你要在生产代码中尽量避免__call()和朋友,因为它们效率不高。

      【讨论】:

      • 这个。您需要遵循外观模式。有一个“拥有”你想要覆盖所有这些函数的对象的包装类。根据需要使用 __call() 传递方法,并根据需要进行任何额外的工作。除非您的代码被不断调用并且您的应用程序受 CPU 限制(几乎从未如此),否则不要担心性能 - 在决定这种权衡时,程序员的时间几乎总是比性能更重要。
      【解决方案3】:

      您可以尝试的一件事是将您的函数范围设置为私有或受保护。当从类外部调用一个私有函数时,它会调用 __call 魔术方法,您可以利用它。

      【讨论】:

      • 最被低估的答案。
      【解决方案4】:

      如果你需要在父 bar() 中添加一些额外的东西,这可行吗?

      class SubFoo extends Foo {
          function bar() {
              // Do something else first
              parent::bar();
          }
      }
      

      或者这只是一个好奇的问题?

      【讨论】:

      • 问题源于父类可能有一堆函数,我不想在子类中复制它们,只是为了应用相同的行为( // do something else first 部分)给他们所有人
      • @nickf 绝对,这在我看来是非常必要的东西,我不明白为什么它不在 PHP 中。
      【解决方案5】:

      你可以做以下事情来达到同样的效果:

          <?php
      
      class hooked{
      
          public $value;
      
          function __construct(){
              $this->value = "your function";
          }
      
          // Only called when function does not exist.
          function __call($name, $arguments){
      
              $reroute = array(
                  "rerouted" => "hooked_function"
              );
      
              // Set the prefix to whatever you like available in function names.
              $prefix = "_";
      
              // Remove the prefix and check wether the function exists.
              $function_name = substr($name, strlen($prefix));
      
              if(method_exists($this, $function_name)){
      
                  // Handle prefix methods.
                  call_user_func_array(array($this, $function_name), $arguments);
      
              }elseif(array_key_exists($name, $reroute)){
      
                  if(method_exists($this, $reroute[$name])){
      
                      call_user_func_array(array($this, $reroute[$name]), $arguments);
      
                  }else{
                      throw new Exception("Function <strong>{$reroute[$name]}</strong> does not exist.\n");
                  }
      
              }else{
                  throw new Exception("Function <strong>$name</strong> does not exist.\n");
              }
      
          }
      
          function hooked_function($one = "", $two = ""){
      
              echo "{$this->value} $one $two";
      
          }
      
      }
      
      $hooked = new hooked();
      
      $hooked->_hooked_function("is", "hooked. ");
      // Echo's: "your function is hooked."
      $hooked->rerouted("is", "rerouted.");
      // Echo's: "our function is rerouted."
      
      ?>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-30
        • 2019-12-04
        • 2011-06-02
        相关资源
        最近更新 更多