PHP equivalent for a python decorator?

Here is my method of mimicking decorators from python in php.

function call_decorator ($decorator, $function, $args, $kwargs) {

    // Call the decorator and pass the function to it
    $decorator($function, $args, $kwargs);
}

function testing ($args, $kwargs) {
    echo PHP_EOL . 'test 1234' . PHP_EOL;
}

function wrap_testing ($func, $args, $kwargs) {

    // Before call on passed function
    echo 'Before testing';

    // Call the passed function
    $func($args, $kwargs);

    // After call on passed function
    echo 'After testing';
}

// Run test
call_decorator('wrap_testing', 'testing');

Output:

Before testing
testing 1234
After testing

With this implementation you can also do something like this with an anonymous function:

// Run new test
call_decorator('wrap_testing', function($args, $kwargs) {
    echo PHP_EOL . 'Hello!' . PHP_EOL;
});

Output:

Before testing
Hello!
After testing

And lastly you can even do something like this, if you are so inclined.

// Run test
call_decorator(function ($func, $args, $kwargs) {
    echo 'Hello ';
    $func($args, $kwargs);
}, function($args, $kwargs) {
    echo 'World!';
});

Output:

Hello World!

With this construction above, you can pass variables to the inner function or wrapper, if need be. Here is that implementation with an anonymous inner function:

$test_val = 'I am accessible!';

call_decorator('wrap_testing', function($args, $kwargs){
    echo $args[0];
}, array($test_val));

It will work exactly the same without an anonymous function:

function test ($args, $kwargs) {
    echo $kwargs['test'];
}

$test_var = 'Hello again!';

call_decorator('wrap_testing', 'test', array(), array('test' => $test_var));

Lastly, if you need to modify the variable inside either the wrapper or the wrappie, you just need to pass the variable by reference.

Without reference:

$test_var = 'testing this';
call_decorator(function($func, $args, $kwargs) {
    $func($args, $kwargs);
}, function($args, $kwargs) {
    $args[0] = 'I changed!';
}, array($test_var));

Output:

testing this

With reference:

$test_var = 'testing this';
call_decorator(function($func, $args, $kwargs) {
    $func($args, $kwargs);
}, function($args, $kwargs) {
    $args[0] = 'I changed!';

// Reference the variable here
}, array(&$test_var));

Output:

I changed!

That is all I have for now, it is a pretty useful in a lot of cases, and you can even wrap them multiple times if you want to.


Apparently runkit might help you.

Also, you can always do this the OO way. Put the original fun in a class, and the decorator into an extended class. Instantiate and go.


maybe you’re looking for call_user_func_array:

function wrapA() {
  $args = func_get_args();
  return call_user_func_array('A', $args);
}

since PHP 5.3 you could even say:

return call_user_func_array('A', func_get_args());

after you’ve edited your question i would say, no, this is not possible, but there are some ways, see this question: how to implement a decorator in PHP?