类方法预调用挂钩

时间:2013-05-25 16:42:40

标签: php class oop

在PHP 5.X中是否有可能在调用类方法时,在调用函数之前执行的类中有一个方法?我需要这个,因为我想对被调用函数中使用的参数进行一些动态验证。

class MyClass {

    protected function preHook(){ echo "You are about to call a method."; }

    public function methodA() { echo "You called methodA."; }

}

$obj = new MyClass();
$obj->methodA(); 
// Output:  "You called methodA."
// Desired output: "You are about to call a method.You called methodA"

另请注意以下事项: “methodA”需要公开,因为在代码中使用反射来检测方法。

2 个答案:

答案 0 :(得分:5)

如评论中所述,这是不可能的,因为只有在未定义具有给定名称的方法时才会调用__call魔术方法:

http://php.net/manual/en/language.oop5.magic.php

然而,下面可能有一个丑陋的黑客解决方案会让你满意。

解决方案1 ​​

需要更改所有方法名称:

class MyClass {

    public function __call($name, $arguments){
        echo "You are about to call $name method.";
        return call_user_func_array(array($this, '_real_' . $name), $arguments);
    }

    private function _real_methodA() { echo "You called methodA."; }

}

$obj = new MyClass();
$obj->methodA(); 

解决方案2

这将需要一个'包装'类:

class MyClass {

    public function methodA() { echo "You called methodA."; }

}

class MyClassWrapper {

    public function __construct(){
        $this->myClass = new MyClass();
    }

    public function __call($name, $arguments){
        echo "You are about to call $name method.";
        return call_user_func_array(array($this->myClass, $name), $arguments);
    }
}
$obj = new MyClassWrapper();
$obj->methodA();

解决方案3

第三种方法是应用装饰器模式并创建一个包装类。

class Decorator
{
    protected $_instance;
    public function __construct($instance)
    {
        $this->_instance = $instance;
    }
    public function __call($method, $args)
    {
        print 'do your stuff here';
        return call_user_func_array(array($this->_instance, $method), $args);
    }
}

$obj = new Decorator(new MyClass);
$obj->methodA();

解决方案4

混合解决方案1并使用反射和“runkit_method_rename”重命名所有方法 http://docs.php.net/manual/en/function.runkit-method-rename.php runkit是实验性的,所以这是相当硬的。

class MyClass {

    public function __call($name, $arguments){
        echo "You are about to call $name method.";
        return call_user_func_array(array($this, '_real_' . $name), $arguments);
    }

    private function methodA() { echo "You called methodA."; }

}

$reflection = new ReflectionClass('MyClass');
$methods = $reflection->getMethods();
foreach ($methods as $method) {
    runkit_method_rename('MyClass', $method->name , '_real_' . $method->name);
}

$obj = new MyClass();
$obj->methodA(); 

答案 1 :(得分:-1)

最后我修改了调度程序来调用prehook(使用__call以便在调用实际方法之前将其隐藏起来但仍然是公共的)。