自动修饰 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
我想在每次使用 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