那么,如果您将所有方法设为受保护或私有呢?(我知道这是一个古老且“已回答”的问题)
__call 魔术方法拦截所有不存在和非公共的方法,因此让所有方法不公开将允许您拦截所有方法。
public function __call( $func, $args )
{
  if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
  Handle::eachMethodAction(); // action which will be fired each time a method will be called
  return $this->$func( ...$args );
}
多亏了这一点,你不需要对你的代码做任何事情(期望添加 __call 和做 quick replace all),如果你的类有共同的父类,那么你可以将它添加到父类而不关心了。
但
该解决方案产生了两个主要问题:
- 受保护/私有方法将自动对公众可用
- 错误将指向 __call 不是正确的文件
我们能做些什么?
自定义私有/受保护
您可以添加所有受保护/私有方法的列表,并在调用之前检查该方法是否可以返回公共:
public function __call( $func, $args )
{
  $private = [
    "PrivateMethod" => null
  ];
  if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
  if ( isset( $private[$func] )       ) throw new Error("This method is private and cannot be called");
  Handle::eachMethodAction(); // action which will be fired each time a method will be called
  return $this->$func( ...$args );
}
对于许多人来说,这可能会破坏交易,但我个人只在只有公共方法(我设置为受保护)的类中使用这种方法。因此,如果可以,您可以将方法分离publicClass并privateClass消除此问题。
自定义错误和堆栈
为了更好的错误,我创建了这个方法:
/**
  *    Get parent function/method details
  *
  *    @param int counter [OPT] The counter allows to move further back or forth in search of methods detalis
  *
  *    @return array trace It contains those elements :
  *       - function - name of the function
  *       - file     - in which file exception happend
  *       - line     - on which line
  *       - class    - in which class
  *       - type     - how it was called
  *       - args     - arguments passed to function/method
  */
protected function getParentMethod( int $counter = 0 ) {
  $excep = new \Exception();
  $trace = $excep->getTrace();
  $offset = 1;
  if ( sizeof( $trace ) < 2 ) $offset = sizeof( $trace ) - 1;
  return $trace[$offset - $counter];
}
它将返回有关称为受保护方法的先前方法/函数的详细信息。
public function __call( $func, $args )
{
  $private = [
    "PrivateMethod" => null
  ];
  if ( !method_exists( $this, $func ) ) {
    $details = (object) $this->getParentMethod();
    throw new Error("Method $func does not exist on line " . $details->line . ", file: " . $details->file . " invoked by " . get_class($this) . $details->type . $func . " () ");
  }
  if ( isset($private[$func]) ) {
    $details = (object) $this->getParentMethod();
    throw new Error("Method $func is private and cannot be called on line " . $details->line . ", file: " . $details->file . " invoked by " . get_class($this) . $details->type . $func . " () ");
  }
  return $this->$func( ...$args );
}
  
这不是什么大问题,但可能会在调试时导致一些混乱。
结论
此解决方案允许您从 CLASS 外部控制对私有/受保护方法的任何调用。Anythis->Method将省略__call并且通常会调用私有/受保护的方法。
class Test {
  
  public function __call( $func, $args )
  {
    echo "__call! ";
    if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
    return $this->$func( ...$args );
  }
  protected function Public()
  {
    return "Public";
  }
  protected function CallPublic()
  {
    return "Call->" . $this->Public();
  }
}
$_Test = new Test();
echo $_Test->CallPublic(); // result: __call! Call->Public - it uses two methods but __call is fired only once
如果您想在静态方法中添加类似的东西,请使用__callStatic魔术方法。