Search code examples
phpmethodscallmagic-methods

PHP - __call all the time


Say we have a class with several protected and/or public methods. I need to perform a check each time a method is called. I could do that check each time i call a method :

class Object
{
    // Methods
}

$o = new Object();

if($mayAccess) $o->someMethod();

or

if($mayAccess) $this->someMethod();

But i would like developers neither to have to think about it nor to write it. I've thought about using __call to do :

class Object
{
    public function __call($methodName, $args)
    {
        if($mayAccess) call_user_func_array($this->$methodName, $args);
    }
}

Unfortunatly, if i call the method from inside the class, __call will not invoked as it only works when a non-visible method is called.

Is there a clean way to hide this check for both internal and external calls ? Again the goal is to make sure a developper won't forget to do it when calling a method.

Thanks in advance :)

EDIT :

I have another way of doing this :

class Object
{
    public function __call($methodName, $args)
    {
        if($mayAccess) call_user_func_array($methodName, $args);
    }
}

function someMethod() { }

But i won't be able to use $this anymore, which means no protected methods, which i do need.


Solution

  • No, I dont think so. What you could do though is write a proxy:

    class MayAccessProxy {
    
        private $_obj;
    
        public function __construct($obj) {
            $this->_obj = $obj;
        }
    
        public function __call($methodName, $args) {
            if($mayAccess) call_user_func_array(array($this->_obj, $methodName), $args);
        }
    }
    

    This means you have to instantiate a proxy for every object you want to check:

    $obj = new MayAccessProxy(new Object());
    $obj->someMethod();
    

    Ofcourse you'd also want the proxy to behave exactly like the object itself. So you also have to define the other magic methods.

    To make it a bit easier for the developers you could do something like this:

    class Object {
    
        /**
         * Not directly instanciable.
         */
        private __construct() {}  
    
        /**
         * @return self
         */
        public static function createInstance() {
            $obj = new MayAccessProxy(new self());
            return $obj;
        }
    }
    
    $obj = Object::createInstance();