Python 装饰器的 PHP 等价物?
Posted
技术标签:
【中文标题】Python 装饰器的 PHP 等价物?【英文标题】:PHP equivalent for a python decorator? 【发布时间】:2010-11-28 08:37:33 【问题描述】:我希望能够用另一个函数包装一个 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()
【问题讨论】:
您的 Wrap_A 示例对那些还不知道 Python 装饰器如何工作的人有点误导,因为您的 Wrap_A 实现明确引用了 A。装饰器的想法是您可以装饰任何函数有了它,但是在您的示例中,您显然不能使用Wrap_A
来包装其他一些功能B
。您介意我编辑您的问题以使其更准确地表示什么是功能装饰吗?
【参考方案1】:
显然runkit 可能help you。
此外,您始终可以以 OO 方式执行此操作。把原来的乐趣放在一个类里,把装饰器放在一个扩展类里。实例化并运行。
【讨论】:
谢谢,runkit
正是我想要的。
您如何像在 python 中一样轻松通用地使用它 - 在这里您在函数定义上方添加装饰(在 .NET 和 Java 中看到类似的构造)。
此答案中的链接现已损坏。
我相信他们现在应该分别是php.net/manual/en/book.runkit7.php和php.net/manual/en/function.runkit7-function-redefine.php【参考方案2】:
这是我在 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!
这就是我现在所拥有的,它在很多情况下都非常有用,如果你愿意,你甚至可以将它们包装多次。
【讨论】:
$kwargs
?这在 php 中不存在。
如果您编写代码来支持它,它可以吗? kwargs 在语法方面与 php 中的键值对数组没有什么不同。【参考方案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());
在你编辑完你的问题后,我会说,不,这是不可能的,但有一些方法,请参阅这个问题:how to implement a decorator in PHP?
【讨论】:
这与我要查找的内容完全无关。我编辑了我的问题以添加说明。【参考方案4】:您可以使用面向方面的编程。但是你需要一些支持 AOP 的框架。
例如 Symfony。 这是 AOP for php 的实现之一 https://github.com/goaop/goaop-symfony-bundle
还有Nette Framework(我用this one)
原则上它是这样工作的:假设您想在用户未登录并尝试访问某个方法时抛出异常。
这只是 “虚构的” 代码来展示它是如何工作的。
你有一些像这样的方面方法:
/** @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()
【讨论】:
【参考方案5】:你不能用 PHP 中的函数来做到这一点。在 Perl 和 Ruby 等其他动态语言中,您可以重新定义以前定义的函数,但是当您尝试这样做时 PHP 会抛出一个致命错误。
在 5.3 中,您可以创建 anonymous function 并将其存储在变量中:
<?php
$my_function = function($args, ...) ... ;
$copy_of_my_function = $my_function;
$my_function = function($arg, ...) /* Do something with the copy */ ;
?>
或者,您可以使用传统的装饰器模式和/或工厂并使用类来代替。
【讨论】:
以上是关于Python 装饰器的 PHP 等价物?的主要内容,如果未能解决你的问题,请参考以下文章