Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

decorator and method_exists

Tags:

php

I have the following code; what should be added to the decorator class to pass the check of method_exists()? In real task I can't change the function that calls method_exists().
Also any idea how to cheat 'get_class_methods()' and '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();

Any ideas ? (I'm thinking of generating a wrapper class by eval())

like image 586
zb' Avatar asked Mar 28 '26 11:03

zb'


2 Answers

This answer is extremely simple:

Make the decorator class extend the real class

<?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();

This is a much better design, and it works!

To better solve the underlying problem of inspecting a proprietary encoded program, I also suggest the use of XDebug's execution_trace.

like image 121
nickb Avatar answered Mar 31 '26 04:03

nickb


First, __construct never return.

Second, to be a good/real decorator pattern, decorator class must decorate real object or another real extended object, so you should not instantiate the object within the construct method. A decorator pattern example will help you understand:

$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!

Third, you can create a method inside the real class who check if method exists:

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

and create it on decorator too (dont forget to check into the real object):

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

Now think with me, if all your decorators extends from a unique Coffee decorator, you don't need to define this method into each decorator, so in my coffe example:

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
}
like image 34
Paulo H. Avatar answered Mar 31 '26 05:03

Paulo H.