自动修饰 class 中的每个方法

Automatically decorate every method in a class

我想在每次使用 class.

中的方法(不管是私有的、受保护的还是公共的)之前和之后添加一些逻辑

例如:

class Service 
{
    function test1() {
        Log:start(__METHOD__);
        someLogicInThere(); ....
        Log:end(__METHOD__);
    }

    function test2() {
        Log:start(__METHOD__);
        someLogicInThere(); ....
        Log:end(__METHOD__);
    }
    ...
}

我的想法是最终有这样的东西:

/**
 * @LogDecorate
 */
class Service
{
    function test1() {
        someLogicInThere();
    }

    function test2() {
        someLogicInThere();
    }
    ...
}

使用注释并不重要。有什么办法吗?

使用神奇的 __call 方法可能会让您轻松完成此操作:

class Service 
{
    public function test1() {
        echo 'TEST 1', PHP_EOL;
    }

    protected function test2() {
        echo 'TEST 2', PHP_EOL;
    }

    public function __call($method, $args) {
        echo 'Some stuff before', PHP_EOL;
        $returnValue = $this->$method($args);
        echo 'Some stuff after', PHP_EOL;
        return $returnValue;
    }
}

$x = new Service();
$x->test2();
$x->test1();

请注意,如果可以从 class 外部访问该方法(如 test1),则不会调用 __call();它仅在涉及的方法受保护或私有时执行;如果它们在对象内部调用也不会触发

正如您的 question-title 已经建议的那样,您可以为此使用 Decorator Pattern。我不太确定这里是否需要 full-stack 装饰器模式。如果它真的很简单 use-case,这样的东西就足够了。

您可以做的是扩展 class 和 'route' 对扩展 class 的所有调用。然后在前后添加一些逻辑,中间调用parent方法。像这样:

class Service {
    function method1() {
        doSomeFunkyStuff();
    }

    function method2() {
        doSomeOtherFunkyStuff();
    }
}

class DecoratedService extends Service {
    function method1() {
        Log::start(__METHOD__);
        parent::method1();
        Log::end(__METHOD__);
    }

    function method2() {
        Log::start(__METHOD__);
        parent::method2();
        Log::end(__METHOD__);
    }
}

$service = new DecoratedService();
$service->method1();
$service->method2();

现在您可以选择使用原始 Service 或使用 DecoratedService。功能是相同的,如果 Service 发生变化,则不必更改 DecoratedService,假设方法名称不会改变(这实际上是一件坏事)。

但也可以查看 wiki 页面(或任何其他资源)以完全理解装饰器模式的意图。这(以上)可能不是您问题的理想解决方案。

编辑 先生,根据要求更自动一些。

由于您无法更改方法的可见性,因此使用魔术 __call() 不起作用(因为 public 或受保护的 parent 方法可以从 child)。但是,您可以 做的是创建自己的调用方法!

class DecoratedService extends Service {
    function call($method) {
        if(!method_exists(parent, $method)) {
            return false; // OR:
            throw Exception;
            // OR handle this case some other way
        }

        Log::start(array(parent, $method));
        call_user_func(array(parent, $method));
        Log::end(array(parent, $method));
    }        
}

$service = new DecoratedService;
$service->call('method1');

我想,这是一个典型的智能引用模式(代理模式和装饰模式的混合体)。

class A {
    function test1() {
        echo 'TEST 1', PHP_EOL;
    }

    function test2() {
        echo 'TEST 1', PHP_EOL;
    }
}

class ProxyA {
    protected $wrapped;

    public function __construct($wrapped) {
        $this->wrapped = $wrapped;
    }

    public function __call($name, $args) {
        echo 'Log:start';
        $this->wrapped->$name($args);
        echo 'Log:end';
    }
}

$proxy = new ProxyA(new A());
$proxy->test1();

但它仅适用于 public 方法。

将智能引用与来自@giorgio 和@yceruto 的 DecoratedService::call() 方法混合使用可以涵盖所有方法,或者只实现 __call() 两次:

class A {
    public function test1() {
        echo 'TEST 1', PHP_EOL;
    }

    private function test2() {
        echo 'TEST 2', PHP_EOL;
    }

    public function __call($name, $args) {
        if (method_exists($this, $name)) {
            $this->$name($args);
        }
    }
}

class ProxyA {
    protected $wrapped;

    public function __construct($wrapped) {
        $this->wrapped = $wrapped;
    }

    public function __call($name, $args) {
        if (method_exists($this->wrapped, $name)) {
            echo 'Log:start';
            $this->wrapped->$name($args);
            echo 'Log:end';
        }
    }
}

$proxy = new ProxyA(new A());
$proxy->test0(); // Nothing to do
$proxy->test1(); // Done
$proxy->test2(); // Done