15

我希望能够用另一个函数包装 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()
4

5 回答 5

8

显然runkit可能会帮助你

此外,您始终可以以 OO 方式执行此操作。把原来的乐趣放在一个类里,把装饰器放在一个扩展类里。实例化并继续。

于 2009-09-15T06:12:14.010 回答
8

这是我在 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));

最后,如果您需要修改 wrapper 或 wrappie 中的变量,您只需要通过引用传递变量。

无参考:

$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!

这就是我现在所拥有的,它在很多情况下都非常有用,如果你愿意,你甚至可以多次包装它们。

于 2015-12-04T19:04:29.560 回答
2

也许您正在寻找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 中实现装饰器?

于 2009-09-15T05:54:06.463 回答
2

您可以使用面向方面的编程。但是你需要一些支持 AOP 的框架。

例如Symfony。这是 php 的 AOP 的实现之一 https://github.com/goaop/goaop-symfony-bundle

还有 Nette 框架(我用这个

原则上它是这样工作的:假设如果用户未登录并尝试访问某个方法,您想抛出异常。

这只是“虚构”的代码来展示它是如何工作的。

你有一些像这样的方面方法:

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

然后你可以@isUserLogged在你的服务中使用注释,这可能会注册在一些 DI 容器中。

/**
 * @isUserLogged()
 */
public function changeUserInfo() {

}
于 2019-12-27T19:15:23.213 回答
1

你不能用 PHP 中的函数来做到这一点。在其他动态语言中,例如 Perl 和 Ruby,您可以重新定义以前定义的函数,但是当您尝试这样做时 PHP 会抛出一个致命错误。

在 5.3 中,您可以创建一个匿名函数并将其存储在一个变量中:

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

或者,您可以使用传统的装饰器模式和/或工厂并使用类来代替。

于 2009-09-15T06:11:10.127 回答