面向方面的PHP编程

大家好!

本月,我们完成了第一套“后端PHP开发人员”课程,并以可能和主要的方式(在假期期间尽可能多地)雇用他们。 许多人可能认识到另一位老师-Evgeny Volosatov对课程进行了补充。 好吧,我们传统上会分享有趣的事情。

走吧

在任何应用程序中,部分代码都同时“跨越”架构的多个部分。

当使用一个功能齐全的框架时,这个问题并不是很明显。 您的问题很可能会广泛存在,并且框架有可能通过牺牲责任划分或通过在框架之上提供抽象来解决该问题。 许多框架使用面向事件的体系结构来解决端到端功能问题。 但是总有片刻时间,框架无法为特定的逻辑提供所需的控制级别。 当使用微框架或通过专门的库进行开发时尤其如此。 您的应用程序对责任分离原则的考虑越多,端到端功能在体系结构中的作用就越大。



面向方面的PHP编程


面向方面的编程(AOP)是一种编程范例,致力于组织和模块化端到端功能。 应用案例-ACL,日志记录,错误处理,缓存。

PHP的内置(内部)假设(当您定义函数/常量/类时,将永远保持定义状态)使AOP范式难以实现。

Li3是第一个使用允许通过闭包过滤方法逻辑的过滤机制来解决端到端功能问题的工具。 为了使该方法可过滤,Li3的实现需要手动添加模板代码。 由于这种限制,AOP技术仅限于过滤技术。

PHP中其他著名的AOP实现:


PECL AOP扩展是一种有趣的方法,但同时也存在风险,因为对PECL扩展的支持并不常见。 另一个选择是Go!库,它是AOP实现,可以即时修复PHP代码,从而可以使用AOP方法。

还有其他实现,但是大多数实现都是基于代理构建的(据我所知),这种方法有很多局限性。

该地区有新人


自动代码生成早已存在于PHP中,并在许多库中使用,例如ProxyManager。 并感谢Composer的采用 表明可以实时编辑代码。

如果仍然可以认为代码生成很简单,则更正代码会更加复杂。 首先,在PHP中没有内置的代码解析器,其次,解决解析PHP代码问题的库很少。 最著名的是PHP-Parser库。 PHP-Parser是一个很棒的工具,但是即使它忽略了生成的抽象语法树中空格的格式。 这使得修复代码变得困难。 实际上,需要修复的代码是实际的可执行代码。 因此,如果您希望回溯准确无误,则需要考虑更正后的文件中的行号。

对于此任务,我们使用Kahlan JIT代码修补程序 。 由于采用了JIT编辑技术,Kahlan是新的Unit&BDD测试框架,该技术允许直接在Ruby或JavaScript中使用存根和猴子补丁代码。 在后台,我们发现该库基于基本的PHP解析器。 但是,尽管如此,它仍然足够快且稳定以适合我们。

过滤器库可在github.com/crysalead/filter上获得,并可按以下方式使用。

首先,应尽快初始化JIT代码修补程序(例如,在打开autoloade作曲器之后立即):

include __DIR__ . '/../vendor/autoload.php'; use Lead\Filter\Filters; Filters::patch(true); 

请注意,代码编辑仅适用于由composer autoload'er加载的类。 如果使用require或include表达式添加了一个类,则在调用Filters :: patch(true)之前已经加载了该类,因此将无法修复。

默认情况下,所有更正后的代码将存储在/ tmp / jit中,但是您始终可以将其更改为自己的代码:

 Filters::patch(true, ['cachePath' => 'my/cache/path/jit']); 

每次更改PHP文件时,缓存文件都会自动恢复。

注意! Filters::patch(true)是配置修补程序的最简单方法,请记住,所有代码都将被修复。 将所有方法包装在代码库中的过滤器封闭中可能要花费很长时间。

幸运的是,如果端到端功能在精心设计的代码中起着至关重要的作用,则项目中仅需要两种方法。 因此,首选方法是仅修复将要过滤的那些方法:

 Filters::patch([ 'A\ClassName', 'An\Example\ClassName::foo', 'A\Second\Example\ClassName' => ['foo', 'bar'], ], [ 'cachePath' => 'my/cache/path/jit', ]); 

因此,您可以选择修复某个类的所有方法,仅修复一个或多个方法。

API过滤器


现在启用了JIT修补程序,创建一个日志过滤器:

 use Chaos\Filter\Filters; use Chaos\Database\Database; use Monolog\Logger; use Monolog\Handler\StreamHandler; $logger = new Logger('database'); $logger->pushHandler(new StreamHandler('my/log/path/db.log', Logger::WARNING)); Filters::apply(Database::class, 'query', function($next, $sql, $data, $options) use ($logger) { $logger->info('Ran SQL query: ' . $sql); return $next($sql, $data, $options); }); 

上面的示例为Chaos数据库库创建一个SQL查询日志过滤器。 有关API过滤器的更多信息,请参见github.com/crysalead/filter

结论


我认为AOP是端到端功能的真正答案。 所有其他抽象同样是多余的,并且在大多数情况下都限于特定的框架。 我不希望有一天,PHP将为面向方面的编程提供内置的API。 但是现在,我认为,JIT代码修补是最好的选择,其优势远远超过了CPU开销,而当未全局应用JIT代码修补时,实际上可以忽略这一点。

结束

与往常一样,我们希望(如果有的话)有一个问题,一个愿望,并邀请您参加一个公开课 ,在这里您可以聆听有趣的演讲,并更好地认识新老师

Source: https://habr.com/ru/post/zh-CN415713/


All Articles