装饰器和method_exists

时间:2011-12-04 04:40:13

标签: php

我有以下代码;什么应该添加到装饰器类来传递method_exists()的检查?在实际任务中,我无法更改调用method_exists()的功能 还有任何想法如何欺骗'get_class_methods()'和'get_class_vars()'?

class real {
    function __construct($arg1,$arg2)
    {

    }

    function test()
    {
        echo "test output\n";
    }
}

class decorator
{
    protected $real;

    function __construct()
    {
        eval('$this->real=new real('.implode(',',func_get_args()).');');
    }

    function __call($method,$args)
    {
        echo "called $method\n";
        $ret=call_user_func_array(array($this->real, $method), $args);
        return $ret;
    }

    public function __isset($name)
    {
        echo "isset $name\n";
    }
}
//-----Can't change below
$r=new decorator(1,2);
if (!method_exists($r,'test')) {die ("method 'test' does not exist, Fail :(\n");}
echo $r->test();

有什么想法吗? (我正在考虑通过eval()生成一个包装类

4 个答案:

答案 0 :(得分:1)

首先,__ construct永远不会返回。

其次,要成为一个好的/真正的装饰器模式,decorator类必须装饰real对象或另一个real扩展对象,所以你不应该在构造方法中实例化对象。装饰模式示例将帮助您理解:

$coffee = new Coffee();
$coffe_extra_sugar = new Sugar($coffee); // Here you put more sugar
$coffe_extra_extra_sugar = new Sugar($coffe_extra_sugar); // Here you put more sugar!

class NewCoffe extends Coffee{} // Now a new coffee

$ncoffe = new NewCoffe();
$ncoffe_extra_sugar = new Sugar($ncoffe); // Good Decorator!

第三,您可以在real类中创建一个检查方法是否存在的方法:

public function method_exists($method){
    return method_exists($this,$method);
}

并在装饰器上创建它(不要忘记检查真实对象):

public function method_exists($method){
    return method_exists($this,$method) || $this->real->method_exists($method);
}

现在想想,如果你所有的装饰者都是从一个独特的咖啡装饰者延伸出来的,你就不需要在每个装饰器中定义这个方法,所以在我的coffe例子中:

class CoffeeDecorator {

    protected $real;

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

    public function method_exists($method){
        return method_exists($this,$method) || $this->real->method_exists($method);
    }

    function __call($method,$args){
            echo "called $method\n";
            $ret=call_user_func_array(array($this->real, $method), $args);
            return $ret;
    }

}

// Now create some coffe decorators
class Sugar extends CoffeeDecorator{
    // Methods for sugar decorator
}

答案 1 :(得分:1)

这个答案非常简单:

使装饰器类扩展真实类

<?php
class real {
    function __construct() {
        var_dump( func_get_args());
    }
    protected function test(){
        echo "test output\n";
    }
}
class decorator extends real{
    function __construct(){
        parent::__construct( func_get_args());
    }

    function __call($method,$args){
        echo "called $method\n";
        return call_user_func_array( array( 'parent', $method), $args);
    }

    public function __isset($name) {
        echo "isset $name\n";
    }
}
//-----Can't change below
$r = new decorator(1,2);
if(!method_exists($r,'test')) {die ("method 'test' does not exist, Fail :(\n");}
echo $r->test();

这是一个更好的设计,it works

为了更好地解决检查专有编码程序的根本问题,我还建议使用XDebug's execution_trace

答案 2 :(得分:0)

__construct()的签名是void __construct ([ mixed $args [, $... ]] )

换句话说,__construct()仅返回对象的实例。

我认为你在factory method pattern和装饰者模式之间感到困惑。

答案 3 :(得分:0)

使用override_function

使用apd编译PHP,
一旦你准备好了,
您可以轻松地将内置函数method_exist覆盖为is_callable

<击> 只需将method_exist更改为is_callable: -

$methodVariable = array($r, 'test');
var_dump(is_callable($methodVariable, true, $callable_name));
echo $callable_name, "\n";

输出: -

bool(true)
decorator::test

什么是is_callable?

  

验证变量的内容是否可以作为函数调用。这可以检查一个简单变量是否包含有效函数的名称,或者一个数组是否包含正确编码的对象和函数名称。

要添加,method_exist只是检查,
在你的情况下,方法是从不实际声明,
而is_callable检查方法可以调用,
使用__call,返回true。