PHP equivalent for a python decorator?

前端 未结 5 592
夕颜
夕颜 2020-12-29 09:04

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() {
    p         


        
相关标签:
5条回答
  • 2020-12-29 09:29

    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?

    0 讨论(0)
  • 2020-12-29 09:39

    You can use Aspect Oriented Programming. But you need some framework that support AOP.

    For example Symfony. This is one of implementations of AOP for php https://github.com/goaop/goaop-symfony-bundle

    There is also the Nette Framework (I use this one)

    In principle it works like this: let's say you want to throw an exception if user is not logged in and tries to access some method.

    This is just "fictive" code to show how it works.

    You have some aspect method like this:

    /** @AroundMethod(annotataion="isUserLogged()") */
    public function checkUser(Method $method) {
        if ($this->user->isLogged()) {
            return $method->call();
        } else {
            throw new \Exception('User must be logged');
        }
    }
    

    And then you can use the annotation @isUserLogged in your services which will be probably registered in some DI container.

    /**
     * @isUserLogged()
     */
    public function changeUserInfo() {
    
    }
    
    0 讨论(0)
  • 2020-12-29 09:49

    You can't do this with functions in PHP. In other dynamic languages, such as Perl and Ruby, you can redefine previously defined functions, but PHP throws a fatal error when you attempt to do so.

    In 5.3, you can create an anonymous function and store it in a variable:

    <?php
        $my_function = function($args, ...) { ... };
        $copy_of_my_function = $my_function;
        $my_function = function($arg, ...) { /* Do something with the copy */ };
    ?>
    

    Alternatively, you can use the traditional decorator pattern and/or a factory and work with classes instead.

    0 讨论(0)
  • 2020-12-29 09:52

    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.

    0 讨论(0)
  • 2020-12-29 09:53

    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.

    0 讨论(0)
提交回复
热议问题