突发奇想:用 PHPDoc 和 反射 来给 PHP 整一个类似 Python 的装饰器~~
装饰器简单解释:把一个函数传给装饰器,并返回一个函数,替换掉原函数。
举个 Python 的栗子:
import time
def time_recorder(fn):
def wrapper():
start = time.time()
fn()
print(time.time() - start)
return wrapper
@time_recorder
def test():
time.sleep(1)
print('test')
# 上面的装饰器用法,相当于下面的语句
# test = time_recorder(test)
test()
输出:
test
1.00072383881
在 Laravel 中,我们就在控制器中尝试这个吧
首先,重写控制器的 callAction
:
// 获取注释中的各种参数以及对应的值
// 反射居然不能直接获取到各个参数,只能获取到整个注释的字符串
// 直接 copy 一个:https://tomlankhorst.nl/get-phpdoc-parameters-of-a-method/
public function phpdocParams(\ReflectionMethod $method): array
{
// Retrieve the full PhpDoc comment block
$doc = $method->getDocComment();
if ($doc === false) {
return [];
}
// Trim each line from space and star chars
$lines = array_map(function ($line) {
return trim($line, " *");
}, explode("\n", $doc));
// Retain lines that start with an @
$lines = array_filter($lines, function ($line) {
return strpos($line, "@") === 0;
});
$args = [];
// Push each value in the corresponding @param array
foreach ($lines as $line) {
[$param, $value] = explode(' ', $line, 2);
$args[$param][] = $value;
}
return $args;
}
public function callAction($method, $parameters)
{
$methodReflection = new \ReflectionMethod(static::class, $method);
$decorators = $this->phpdocParams($methodReflection)['@decorator'] ?? [];
$decorators = array_reverse($decorators);
$fn = [$this, $method];
foreach ($decorators as $decorator) {
$fn = call_user_func($decorator, $fn);
}
return call_user_func_array($fn, $parameters);
}
定义两个全局辅助函数随便试试:
function my_logger($fn)
{
return function () use ($fn) {
Log::info('start');
$res = $fn(...func_get_args());
Log::info('end');
// 这里不搞个 return,会拿不到控制器的返回结果~~
return $res;
};
}
function time_recorder($fn)
{
return function () use ($fn) {
$start = microtime(true);
$res = $fn(...func_get_args());
Log::info('time spent: '.(microtime(true) - $start));
return $res;
};
}
在控制中使用装饰器:
/**
* @param Request $request
*
* 这里用了两个装饰器
* 包裹(装饰)顺序是从内(下)往外(上),执行顺序是从外(上)往内(下)
*
* @decorator my_logger
* @decorator time_recorder
*
* @return mixed
*/
public function index(Request $request)
{
return response()->json($request->input());
}
浏览器访问:xx.xx.xx/some-route?a=1&b=2
浏览器的结果:{"a":"1","b":"2"}
log 的结果:
[2020-04-07 08:50:47] local.INFO: start
[2020-04-07 08:50:47] local.INFO: time spent: 0.013785123825073
[2020-04-07 08:50:47] local.INFO: end
其实有点像中间件~~
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: