PHP: callback on entry / exit of class methods?

Is there a way to customize callbacks on parameters, entries and exits (or auto-logging) without explicit calls within each method? I basically want to write this information to my logger class (which is static) without having to do it manually for each method.

At the moment I need to call Logger :: logEntry () and Logger :: logExit () for each method. I would not like to do this:

class TestClass {
    public function tester($arg) {
        Logger::logEntry();
        Logger::info('Parameter $arg => ' . $arg);

        // Do some stuff...

        Logger::logExit();
    }
}

      

+1


source to share


3 answers


use a wrapper class. this method has the following advantages:

  • No need to change the main signatures of the class structure / method
  • change log? just update this class
  • calling the update object vs injecting code into every class you want to register


...

class LogWatch {
    function __construct($class)    {
        $this->obj  =   $class;
    }

    function __call($method, $args) {
        if (in_array($method, get_class_methods($this->obj) ) ) {
            Logger::logEntry();
            Logger::info('Parameter '.implode(', ', $args) );

            call_user_func_array(array($this->obj, $method), $args);

            Logger::logExit();

        } else {
            throw new BadMethodCallException();
        }
    }
}

$test = new LogWatch(new TestClass() );
$test->tester();

// you can use instances of `LogWatch()` just like your watched class
// including passing appropriate params:
$test->tester($param1, $param2);

      

+11


source


If you want to do feature logging for debugging, you can look at the Xdebug extension. There is no good way to intercept function calls at runtime, and any automatic interception will add a lot of execution time.

Using XDebug, you could instead enable it as needed, and also get a lot of other stuff.

(This is why XDebug is used with PHPUnit for unit testing and coverage analysis.)



The problem with __call

The __ call might look like an interesting solution to the problem, but there are 3 problems with this, namely

  • Significant execution overhead. your __call -> call_user_func_array, which will literally add not one, but two functions to each execution .

  • Backtraces are getting illegible: the actual function you were trying to call gets lost in the sea of ​​__call and call_user_func_array, making backtracing extremely difficult, especially if your backtraces come with lists of them included.

  • Silly hidden functions: you go back to PHP4 style by "hiding" functions, prefixing them with _ so the user doesn't call it directly or see it, because if the function name is named, wan't, __call is not triggered, so you already have a whole class full of really awful function names that developers would be tempted to call right in anyway in different places. (And if you want to get rid of __call later, you'll have to rename all of these functions to avoid breaking the code!)

Thus, if you use PHP code to implement this, it will result in epic terrible code that any future user of your codebase will NOT work. You are much better off with something (like Xdebug) that can be added transparently when you need it, and save you a lot of dirty code.

+5


source


you can use magic function __call

. It is called when no functions match this name. Rename your methods prefixed with something (ex: underscore) and maybe set them private / protected.

class TestClass {
    public function __call($function, $args) {
        Logger::logEntry();
        Logger::info('Parameters: ' . implode(", ", $args);

        $localFunc = "_" . $function;
        $return = $this->$localFunc($args);

        Logger::logExit();

        return $return;
    }

    private function _tester() {
        // do stuff...
        return "tester called";
    }
}

 $t = new TestClass();
 echo $t->tester();
 // "tester called"

      

-1


source







All Articles