PHP中的Python装饰器的等价物是什么?

16 投票
6 回答
16951 浏览
提问于 2025-04-15 14:20

我想要用一个新的函数来包裹一个PHP函数,但保持它原来的名字和参数列表不变。

比如说:

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

输出结果:

Calling A()
inside A()
Finished calling A()

6 个回答

3

也许你在找的是 call_user_func_array

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

从 PHP 5.3 开始,你甚至可以这样说:

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

在你修改了你的问题后,我想说,不,这个是不可能的,但有一些方法可以做到,看看这个问题: 如何在 PHP 中实现装饰器?

9

这是我在PHP中模仿Python装饰器的方法。

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

输出结果:

Before testing
testing 1234
After testing

通过这个实现,你还可以用匿名函数做一些这样的事情:

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

输出结果:

Before testing
Hello!
After testing

最后,如果你愿意的话,你甚至可以这样做。

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

输出结果:

Hello World!

通过上面的构造,你可以根据需要将变量传递给内部函数或包装器。这里是使用匿名内部函数的实现:

$test_val = 'I am accessible!';

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

没有匿名函数时,它的工作方式完全相同:

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

$test_var = 'Hello again!';

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

最后,如果你需要在包装器或被包装的函数内部修改变量,只需通过引用传递变量即可。

没有引用:

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

输出结果:

testing this

有引用:

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

输出结果:

I changed!

我现在就这些,这在很多情况下都非常有用,如果你想的话,甚至可以多次包装它们。

7

看起来 runkit 可能会对你有帮助。

另外,你也可以用面向对象的方式来解决这个问题。把原来的函数放在一个类里,然后把装饰器放在一个扩展的类中。创建一个实例,然后就可以使用了。

撰写回答