PHP - __call all the time PHP - __call all the time php php

PHP - __call all the time


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


So what if you made all your methods protected or private? (I know this is old and "answered" question)

The __call magic method intercepts all non-existing and non-public methods so having all your methods not public will allow you to intercepts all of them.

public function __call( $func, $args ){  if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");  Handle::eachMethodAction(); // action which will be fired each time a method will be called  return $this->$func( ...$args );}

Thanks to that you will not need to do anything to your code (expect adding __call and doing quick replace all) and if your classes have common parent then you can just add it to parent and not care anymore.

BUT

This solution creates two major problems:

  • The protected/private methods automatically will be available to public
  • The errors will be pointing to __call not the proper file

What can we do?

Custom private/protected

You can add a list of all protected/private methods and check before the call if the method can be return to public:

public function __call( $func, $args ){  $private = [    "PrivateMethod" => null  ];  if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");  if ( isset( $private[$func] )       ) throw new Error("This method is private and cannot be called");  Handle::eachMethodAction(); // action which will be fired each time a method will be called  return $this->$func( ...$args );}

For many this might be deal breaker, but I personally use this approach only in classes with only public methods (which I set to protected). So if you can, you might separate methods into publicClass and privateClass and eliminate this problem.

Custom Errors and Stack

For better errors I have created this method:

/**  *    Get parent function/method details  *  *    @param int counter [OPT] The counter allows to move further back or forth in search of methods detalis  *  *    @return array trace It contains those elements :  *       - function - name of the function  *       - file     - in which file exception happend  *       - line     - on which line  *       - class    - in which class  *       - type     - how it was called  *       - args     - arguments passed to function/method  */protected function getParentMethod( int $counter = 0 ) {  $excep = new \Exception();  $trace = $excep->getTrace();  $offset = 1;  if ( sizeof( $trace ) < 2 ) $offset = sizeof( $trace ) - 1;  return $trace[$offset - $counter];}

Which will return details about the previous method/function which called protected method.

public function __call( $func, $args ){  $private = [    "PrivateMethod" => null  ];  if ( !method_exists( $this, $func ) ) {    $details = (object) $this->getParentMethod();    throw new Error("Method $func does not exist on line " . $details->line . ", file: " . $details->file . " invoked by " . get_class($this) . $details->type . $func . " () ");  }  if ( isset($private[$func]) ) {    $details = (object) $this->getParentMethod();    throw new Error("Method $func is private and cannot be called on line " . $details->line . ", file: " . $details->file . " invoked by " . get_class($this) . $details->type . $func . " () ");  }  return $this->$func( ...$args );}  

This is not much of a problem but might lead to some confusion while debugging.

Conclusion

This solution allows you to have control over any call of private/protected methods FROM OUTSIDE OF CLASS. Any this->Method will omit __call and will normally call private/protected method.

class Test {    public function __call( $func, $args )  {    echo "__call! ";    if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");    return $this->$func( ...$args );  }  protected function Public()  {    return "Public";  }  protected function CallPublic()  {    return "Call->" . $this->Public();  }}$_Test = new Test();echo $_Test->CallPublic(); // result: __call! Call->Public - it uses two methods but __call is fired only once

If you want to add a similar thing to static methods use __callStatic magic method.