
I decided to share my vision and ideas on the implementation of the python-style decorators in PHP.
As zavlekalochki a small example of the use of the image on the right. Displays (after the implementation of the logic of the decorators themselves):
Log: calling b ()
int (42)
The implementation is implemented as a C extension and does not require rebuilding PHP itself. But it will not start on hosting, where you can not download your so'shku.
At the moment, the code is in beta (all the necessary functionality is written, but there are probably bugs and memory leaks :)). So as is. Well, if you want to help in development, I will be glad to accept commits on
github .
A simple example of use:
<?php function double($func) { return function() use($func) { return 2*call_user_func_array($func, func_get_args()); }; } @double function a() { return 21; } var_dump(a());
Decorators are always functions that return functions. The external function takes the first parameter to replace the function. Unlike python, the decorator with parameters is not described as a function that returns a function that returns a function ... Additional parameters are simply passed after the function being replaced:
<?php function add($func, $v=0) { return function() use($func, $v) { return $v+call_user_func_array($func, func_get_args()); }; } @add(1) function a() { return 1; } var_dump(a());
')
Decorators can be combined:
<?php function dec($func, $p='[]') { return function() use($func, $p) { $s = call_user_func_array($func, func_get_args()); return $p[0].$s.$p[1]; }; } @dec function a() { return 'I'; } var_dump(a()); @dec('{}') function b() { return 'am'; } var_dump(b()); @dec @dec('()') @dec('{}') function c() { return 'here'; } var_dump(c());
In this case, they are performed in the opposite order:
@A
@B
@C
function X
turns into
A (B (C (X (...))))
The number of parameters and their types are arbitrary, and the laziness of the calculations is generally a free hand:
<?php class Logger { const INFO = 'INFO'; public static function log($func, $text='', $level=self::INFO, $prefix='') { return function() use($func, $text, $level, $prefix) { printf("%s%s: %s\n", $prefix, $level, $text); return call_user_func_array($func, func_get_args()); }; } } @Logger::log('calling a()', Logger::INFO, date('Ymd H:i:s').': ') function a() { return 'Hello'; } var_dump(a());
The names of decorators should be functions and static methods, moreover, declared at the time of the call, and not in the description of the decorator. Anyway, you can experiment:
<?php class Arr { public static function map($func, $cb) { return function() use($func, $cb) { $v = call_user_func_array($func, func_get_args()); return array_map($cb, $v); }; } } class Foo { @Arr::map(function($i){return -$i;}) public function bar() { return range(1, 3); } } $foo = new Foo(); print_r($foo->bar());
Well, I'm sure everyone here can come up with something more interesting in the context of their tasks.
Technical issues
At the moment I checked the support when executing the code with the decorators through:
- cat file.php | php
- php file.php
- require / include
- eval
Perhaps something else is missing.
From known bugs / features (features can be discussed; I will fix bugs soon):
- If the decorator has parameters, then the opening '(' must be on the same line as the name of the decorator;
- Due to the code modification, __FUNCTION__ and __METHOD__ lose their relevance. It is possible to correct the substitution of constants for strings with totals, but is not sure of the correctness of such a decision;
- __LINE__ must always coincide, although the case of a multi-line description of the parameters of decorators has not yet been worked out;
- If the decorator description syntax errors occur, an exception is thrown from the base class Exception with incorrect file name and line number;
- Comments in the github code in Russian, because My level of written English is not enough, so as not to be ashamed of what is written. I hope, temporarily, and even if someone sends a commit with a good translation - it will be cool!
- Decent IDE swear on an incomprehensible syntax. Is it possible to train PHPStorm at least not to swear?
PS If the survey picks up enough options "Looking for great details", I can write a post with a detailed description of the Sishna implementation of this extension.