在开发过程中,我们常常需要对代码的操作行为进行记录,在一般的情况下我们都可以通过自带的代码异常处理进行错误记录;但是开发一个后台管理系统的情况下我们也需要对“用户”的行为进行监控和记录。如果一开始编写记录Log的规范可能后续开发就无需再额外处理了,但现在已经生成了大量的代码并无法顾及到全部方法内添加方法,所以想通过一些偷懒的方法去完成这个功能。
该功能暂时没有应用到真实的业务场景上,请自行评估风险
应用到的环境说明
- WordPress 5.5 & PHP7.4
- 已经生成大量代码, 大部分是静态方法的调用
- WordPress的应用上没有统一出入口(虽然可以通过一些Filter去统一)
设计思路
因为历史遗留的问题,我们旧的代码已经越来越大了,单个方法甚至已经操作了2000行,追踪到每个方法内去添加记录Log的函数十分消耗时间且枯燥,但好在我们开发的时候每个操作方法都会定义在一个类内,这样我们可以通过魔术方法 __call()
以及 __callStatic()
调用方法。
但是__call()
以及 __callStatic()
只能应用在未定义的方法上,总不能在每个调用的方法或者类内的方法手动修改一遍吧,这不仅影响代码的可读性并且影响代码补全等功能,所以看起来可能这个方案不太行。
但是,按照这个思路去想,如果能从调用操作方法前已经通过代码修改原有的方法名(需要用到runkit7
扩展),让全部调用原来的方法的地方全部走到该两个魔术方法上,再调用修改后的方法名,这样的执行方法的前后都可以拿到了。根据这个思路先编写测试代码。
<?php
class myClass(){
//用于存在需要监听的原方法名
static $proxy_methods = [];
/*
** 假设需要记录日志的方法事件
*/
public static function run(){
echo __METHOD__
}
/*
** 给类内的方法进行注册改名
*/
public static function _register_log(){
$class = new \ReflectionClass(get_class());
$methods = $class->GetMethods();
foreach($methods as $method){
$method_name = $method->name;
//这里会获取到魔术方法 应该做判断跳过魔术方法和该方法本身
if(in_array($method_name, ['__call', '__callStatic', __METHOD__])){
continue;
}
if(runkit7_method_rename($class, $method_name, $method_name.'_proxy')){
self::$proxy_methods[] = $method->name
}
}
}
/*
** 让方法统一走进魔法函数
*/
public static function __callStatic($method, $args){
if(in_array($method, self::$proxy_methods)){
//这里调用统一日志记录方法
$call_method = $method.'_proxy';
return static::$call_method($args);
}
//这里可以抛出method not found
}
}
根据上面的思路,将注册方法_register_log()
和编写魔术方法__callStatic()
单独写成一个Trait赋予到需要用到到类内上就不用重复编写。
但是就算编写了以上的方法,可能需要在另外一个文件上,调用方法前对该类统一调用方法_register_log()
将其类内方法进行修改后进行调用才能实现我们想要的效果。解决方案可以是在类定义的文件顶部加上这行进行修改。
因为WordPress是不支持自动加载类的,在执行代码的时候已经把需要用的代码通过include
加载进来了,那么可以通过get_declared_classes()
获取到此为止已定义的类,然后依次进行取出需要注册类调用注册方法。不过该方法不适用于自动加载;
一些改善的地方
- 根据上面所说的 将通用的方法整理到Trait方便代码复用
- 可以配合反射类内的方法
getTrait()
配合使用拿到该类是否需要调用注册方法 - 如果使用
get_declared_classes()
去获取类并注册的时候需要考虑到过滤类,可以通过上面的getTrait()
配合使用,也可以通过编写规范注解解析处理,做成注解的可以使扩展性更强。 - 一个类内可能不是每个方法都需要记录日志,所以也可以通过上面这条,在需要记录的方法上编写规范注解,并且可以在注解描述方法功能等。
- 性能问题 这样的操作理论可行,但性能消耗上不确定是否会出现问题,还未测试