call_user_func_array和__call创建无限循环

时间:2019-05-18 22:44:37

标签: php call-user-func-array

我有一个class Aclass B继承自class A,我想在运行函数之前先进行一些检查。

class A {
  public class __call($name, $params) {
     if (method_exists?($this, $name)) {
       $result = call_user_func_array([$this, $name], $params);
       return $result;
     }
  }
}

class B {
  private function hello() {
    echo "Hello"
  }
}

我期待的是,当我打电话时:

$b = new B();
$b->hello();

它将调用__call然后执行private function hello,但它开始无限循环,看起来call_user_func_array再次触发__call。 但是如果我在hello

中创建class A函数,代码就可以工作

这是预期的行为吗? 我有什么办法可以防止这种情况发生?

2 个答案:

答案 0 :(得分:0)

玩了一会儿之后,看来可以将hello函数设置为受保护的,而不是私有的。

您的代码有几个小问题。查看评论。

class A {
  public function __call($name, $params) {
    if (method_exists($this, "{$name}")) {
      $this->before();
      $result = call_user_func_array([$this, "{$name}"], $params);
      $this->after();
      return $result;
    }
  }
  private function before() {
    echo "before\n";
  }
  private function after() {
    echo "after\n";
  }
}

class B extends A {
  protected function hello() {
    echo "Hello\n";
  }
}

$b = new B();
$b->hello();

当我运行它时,这就是我得到的结果。

before
Hello
after

我在PHP 7.0.8上运行它。

简短的回答:父类中的公共方法不能调用子类中的私有方法。

您也可以使用特质。

trait Wrappable
{
  public function __call($name, $params) {
    if (method_exists($this, $name)) {
      $this->before();
      $result = call_user_func_array([$this, $name], $params);
      $this->after();
      return $result;
    }
  }
  private function before() {
    echo "before\n";
  }
  private function after() {
    echo "after\n";
  }

}

class A {

  use Wrappable;

  public function pub()
  {
    echo __METHOD__ . "\n";
  }

}

class B {
  use Wrappable;
  protected function hello() {
    echo "Hello\n";
  }

  protected function protHello()
  {
    echo __METHOD__ . "\n";
    $this->privHello();
  }
  protected function visibilityBridge($f, $a)
  {

  }
  private function privHello()
  {
    echo __METHOD__ . "\n";
  }
}
$a = new A();
$a->pub();
$b = new B();
$b->privHello();

答案 1 :(得分:0)

您需要PHP 7+来进行数组解构。

class A {
  public function __call($name, $params) { //this is a function
     if (method_exists($this, $name)) { // Remove ?
       $result = $this->$name(...$params); //Really calls the function from the context
       return $result;
     }
     // As a suggestion you should throw an exception here for maintainability
  }
}

class B extends A { // You need 'extends A'
  private function hello() {
    echo "Hello"; // ;
  }
}

$b = new B();
$b->hello(); // Hello