Search code examples
phpcallbackmixins

Mixins, call_to_user_func not finding the function


So I have a class which is designed to "mix" other classes in, via what i call a "bridge" class. So you have your sample classes for example:

class A{
    public function __construct(){}

    public function hello_a(){ echo "hello A"; }
}

class B{
    public function __construct(){}

    public function hello_b(){ echo "hello B"; }
}

You might also have a single class called C - which needs to inherit from both A and B, but since PHP doesn't have multiple inheritance we have the following:

class C extends Bridge{
    public function __construct(){
        parent::__construct();
    }

    public function hello_C(){ 
        $this->hello_a(); // Freaks out*
    }
}

class Bridge extends AisisCore_Loader_Mixins{
    public function construct(){
        parent::construct();

        $this->setup(array(
            'A' => array(),
            'B' => array()
        ));
    }
}

And now finally we have our mix-in class which allows all of this to work. Note: this code assumes you have a auto loader using the pear naming standards to load classes for you.

class AisisCore_Loader_Mixins {

    private $_classes;

    private $_class_objects = array();

    private $_methods = array();

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

    public function init(){}

    public function setup($class){
        if(!is_array($class)){
            throw new AisisCore_Loader_LoaderException('Object passed in must be of type $class_name=>$params.');
        }

        $this->_classes = $class;
        $this->get_class_objects();
        $this->get_methods();    
    }

    public function get_class_objects(){
        foreach($this->_classes as $class_name=>$params){
            $object = new ReflectionClass($class_name);
            $this->_class_objects[] = $object->newInstanceArgs($params);
        }
    }

    public function get_methods(){

        foreach($this->_class_objects as $class_object){
            $this->_methods[] = get_class_methods($class_object);
        }

        return $this->_methods;
    }

    public function __call($name, $param = null){
        foreach($this->_methods as $key=>$methods){
            foreach($methods as $method){
                if($name === $method){
                    return $this->isParam($method, $param);
                }
            }
        }

        throw new AisisCore_Loader_LoaderException("Method: " .$name. 
                            " does not exist or it's access is not public");
    }

    private function isParam($method, $param){
        if($param != null){
            call_user_func($method, $param);
        }else{
            call_user_func($method);
        }        
    }
}

You can see in class C how the class above is used, we simply just call hello_a. All is well up to this point until it tries call_user_func() and freaks out saying:

Warning: call_user_func() expects parameter 1 to be a valid callback, function 'hello_a' not found or invalid function name 

Is there a particular reason it cannot find this? the classes are loaded, the methods are stored in the array, it obviously found the method in the array of methods, the method is public. whats going on?


Solution

  • Your call to call_user_func is passing just the method name, so it's looking for a global function. You must pass your class name or instance:

    $a = new A();  // Or however you plan to get your instance of A
    call_user_func(array($a, $method));