Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP equivalent for a python decorator?

I want to be able to wrap a PHP function by another function, but leaving its original name/parameter list intact.

For instance:

function A() {
    print "inside A()\n";
}

function Wrap_A() {
    print "Calling A()\n";
    A();
    print "Finished calling A()\n";
}

// <--- Do some magic here (effectively "A = Wrap_A")

A();

Output:

Calling A()
inside A()
Finished calling A()
like image 803
Fragsworth Avatar asked Sep 15 '09 05:09

Fragsworth


People also ask

What is a decorator in PHP?

Decorator is a structural pattern that allows adding new behaviors to objects dynamically by placing them inside special wrapper objects, called decorators. Using decorators you can wrap objects countless number of times since both target objects and decorators follow the same interface.

Are decorators Pythonic?

Recall that a decorator is just a regular Python function. All the usual tools for easy reusability are available. Let's move the decorator to its own module that can be used in many other functions. Note: You can name your inner function whatever you want, and a generic name like wrapper() is usually okay.

Are Python decorators necessary?

Needless to say, Python's decorators are incredibly useful. Not only can they be used to slow down the time it takes to write some code, but they can also be incredibly helpful at speeding up code. Not only are decorators incredibly useful when you find them about, but it is also a great idea to write your own.

What is the syntax of decorators in Python?

A Decorator in Python is a function that takes another function and extends the behavior of the latter function without explicitly modifying it. Functions are first-class objects in Python. We can easily decorate a function using the @decorator syntax.


2 Answers

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.

like image 194
DevinMcBeth Avatar answered Oct 05 '22 23:10

DevinMcBeth


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.

like image 40
Zed Avatar answered Oct 06 '22 00:10

Zed