您决定编写自己的框架。它值得吗?





正如经典所说:“我早晚知道我们会来的。” 因此,在与工人的Symfony和在宠物项目中的ReactPHP进行了数年的安静生活之后,我适应了我的框架的创建。



但是他的故事才刚刚开始。那些脑力已经发展到生产水平但仍然是小众解决方案的人呢?我找到了一个知道这个问题答案的人-面向方面的框架的作者和首席开发人员。



你好!这是我的播客“ Between the Brackets”的部分抄本-对来自PHP世界的有趣人物的采访。在这一集中,我们将与亚历山大·利萨琴科(Alexander Lisachenko)进行交流,亚历山大·利萨琴科是基于Java的框架的创建者,该框架通常由Google谷歌搜索。



如果您听的更舒服,可以在音频版本中找到更多技术示例。


很有可能,不是我们所有人都遇到过AOP之类的东西-PHP中的面向方面的编程。 Sasha陷入困境并做出了自己的决定,将其拖到我们的应用程序中。



Sergey Zhuk,Skyeng和DriftPHP:让我们从头开始-什么是AOP,它将解决什么问题?



Alexander Lisachenko,PHP俄罗斯和Go! AOP这个想法并不新鲜。 Xerox发明了AOP来解决端到端功能问题。在Java世界中,该方法被积极地用于检查功能,例如授权,身份验证,缓存,日志记录,管理功能切换和断路器。



使用方面可以解决非常广泛的任务。让我们使用示例:



  • . , . , , . — , .
  • — . , - . , , - .
  • : , , . Xdebug — .
  • , — circuit breaker, , , , .


如果我们看一下应用程序,将会发现到处都是这样的代码片段,使用它并不是很方便。在整个应用程序中都存在这种所谓的端到端功能。



而且,没有什么好方法可以使用传统的面向对象编程将其括起来。



是的,当然,我们可以尝试使用装饰器-但是如果我们希望我们的类实现相同的接口,则需要在装饰器中实现或生成每个方法。也就是说,为我们要缓存的所有类提供装饰器。



谢尔盖·朱克(Sergey Zhuk):好,知道了。但是为此,已经有一些PECL扩展,该框架甚至存在。你为什么决定写自己的?



Aleksandr Lisachenko:是的,您正确地注意到已经有许多解决方案。但是,在我看来,它们有很多缺点。



一些解决方案试图转换源代码,立即将特定建议嵌入特定类中-一段代码从一种方法重复到另一种方法。使用xlt可以生成一个类,并将所有内容放在那里。通常,最好不要完全打开此代码。)



第二类解决方案是扩展。例如,PHP AOP-从原理上讲,它功能齐全,但是在运行时可以完成所有操作。当我们添加一个建议时,似乎一切都很好,但是我们添加了第二个建议-速度开始成比例地降低。因此,在十个技巧上,应用程序开始变慢,如果再添​​加几十个,就可以了-可以保证超时。



碰巧的是,我看到了如何在锂框架中实现称为“过滤器”的想法-这样的现代中间件原型。我们在程序中创建了一些先前已知的点,并且可以在调用之前和之后将过滤器应用于这些点。这个想法似乎太有趣了,以至于我决定编写一个应用程序并使用这些过滤器公开跨领域的功能。我开始研究如何用Java完成它。



而且我意识到在PHP中将无法立即实现它。那可能是最有趣的时刻。



总的来说,编写框架的整个过程一直在与“否”和“不可能”作斗争。看来我无法实现基本的东西,但是处理它们变得更加有趣。



谢尔盖·朱克(Sergey Zhuk):我忍不住要问。为什么要进行AOP?该框架的第一个版本于2013年初发布,当时已经存在Go语言,并且您拥有PHP。就像JavaScript和Java一样,对吗?)



Alexander Lisachenko:第一次提交不等于在我的计算机上进行本地开发。)那时,Go还不流行。我在Google上搜索,发现Google进行了某种内部开发,它占据了自己的一席之地……而且没有打扰。



我根据内部的意愿选择了框架本身的名称。从开始-“执行”,“前进”,“执行”。



谢尔盖·朱克(Sergei Zhuk):那么,有没有重新命名的想法?



Alexander Lisachenko:正式是Go的全名! AOPPHP。但是随着时间的流逝,我删除了PHP,因为它是通过Composer放置的,似乎没有必要使油变得油腻。



到目前为止,我得到了一些额外的流量:很多试图找到Go的AOP的人最终都在我的PHP框架上。也许在将来的版本中,有必要强调一点,那就是Go无关。到目前为止,还没有人在这方面在GitHub上启动过一个问题。也没有来自Google或社区的投诉。



Sergey Zhuk:好的,从客户端代码的角度来看,这是如何实现的?这里有一个应用程序,例如,在Laravel上,有一些与第三方服务的集成,我想记录这些调用。



亚历山大·利萨琴科(Alexander Lisachenko):Laravel已经有一个特殊的模块。他将把您需要的一切都放入系统中,进行配置。您将需要编写一个方面-它将是一项服务,您将对其进行标记,并且AOP核心将自动提取它。在这个方面,您需要了解要实现缓存功能的应用程序的哪些位置,哪些类和方法。您可以直接使用签名指定方法(但是该选项不是很灵活,因此可以重命名该方法,但仍保留在方面中),也可以使用注释标记该方法。第二个选项更加灵活:在需要缓存的地方,只需将其标记为可缓存,引擎将为您进行缓存并调用回调,这在方面的代码中。



在使用Composer加载类时,框架会进行干预,并检查高速缓存中是否具有嵌入式方面的该类的就绪版本。如果存在,他立即将其退还,运行时不进行其他检查。如果没有缓存,那么我们将构建一个AST树,并在该树的顶部创建一个反射类,但不将其加载到内存中。到那时,我们可以按照自己想要的方式对其进行更改,即在该类中编织方面代码。



我不喜欢面条的内在面,因此决定将课程分为两部分。



原始类几乎保持不变-仅名称更改。然后出现带有原始类名称的第二个类,该类扩展了主要类,并且可以在必要时覆盖多个方法。



为什么实际上是继承。有许多方法和类可以将实例返回。例如,众所周知的链方法:我们在对象上调用方法链,它返回$ this。如果我们进行装饰,则第一个调用将起作用,但随后方面将下降。除了继承之外,还保存了内存-因为内存中仍然有一个实例。



谢尔盖·朱克(Sergey Zhuk):所有这些架构,引擎,您从一开始就考虑过一切吗?



亚历山大·利萨琴科(Alexander Lisachenko):有很多事情要深入。例如,我对AST不太熟悉,因此我学习了许多与语法描述有关的学科。而且,如果您看一下我的框架,那么我的切入点将实现完整的语法并具有自己的语法-这可能是一项伟大的成就。您可以编写任意多的复杂表达式,例如,“调用不以资产开头的任何公共方法,在诸如此类的内部空间中实现诸如此类的接口”。



另外,我在PHP中挖了很多东西。我看着扩展在哪里,它们如何工作。我坐在某个地方进行概要分析,优化了某些东西,进行了调整:但是现在,如果您仅连接AOP框架,该应用程序将增加一些有趣的7-10毫秒。在经典的100毫秒响应水平上,甚至根本无法察觉到这样一个巨大的框架在后台被调用。



Sergey Zhuk:不同的PHP框架是否有特定性?



Alexander Lisachenko:原则上,AOP框架被认为是不需要特定绑定的通用库。主要条件是使用Composer。但这与Symfony不太友好。



Symfony中的黑魔法太多,当遇到我框架中的魔法时,最强的Symfony胜出。



一般来说,Symfony的想法是有一个容器,您需要使用它而不是发明单独的框架来获得AOP功能。还有更多传统方法:包括捆绑软件,例如JMS AOP或我的Symfony Go AOP捆绑软件



谢尔盖·朱克(Sergey Zhuk):让我们谈谈社区和竞争对手。你有吗?



亚历山大·利萨琴科(Alexander Lisachenko):据我所知,现在有三个框架。有雷。 Aop,但由于它不知道如何有效地与Composer配合使用,因此对生产无济于事。 Flow的作者使用我们这里提供的AOP调味料来完善他们的框架。在中文框架旁边还有别的东西,在Swoole之上有很多约束-但这一切都在扩展级别,并且出于安全原因,管理员也不能忽略扩展。我仍然有一个经典的框架,并且在任何版本上都可以使用。



至于社区。大概只有四个人能很好地理解和理解:我,一个来自塞尔维亚的人,以及两个我以前工作的人,参与了我所做的一切。自然,我向他们展示了我所有的发展和成果。最近几个月,当我换工作时,投入很少的精力和精力到开源上,但是它可以自主地生活和工作。



, - Z-Engine — c , , PHP.



我计划尽快腾出时间,根据语言本身的内部结构,继续开发Z-Engine,并开发下一个版本的框架。它将几乎像Java的AspectJ一样工作。目标是在PHP 8中达到目标。



Sergey Zhuk:这几乎是框架的完整重写?



亚历山大·利萨琴科(Alexander Lisachenko):不,我把一切都分解了。仅代码注入过程会更改:实际上有几个类负责对特定类进行编辑。在这种情况下,此类不会使缓存中的文件具有不同的结构,但是此刻在运行时将更改OPcache并修改内存中的PHP结构。



Sergey Zhuk:PHP社区对此主题的普遍态度是什么?我喜欢异步PHP,这使很少有人漠不关心。你最近怎么样



亚历山大·利萨琴科(Alexander Lisachenko):总会有人说我们无需AOP就可以做到这一点,为什么在应用程序中需要它。我的回答是,如果您不在企业中工作,那么这些方面对您没有用。而且,如果您认为自己有一家企业,但是只有一个服务和2-3个开发人员,那么这对您也不起作用。)AOP在拥有几十个人的团队中工作得很好,每个人都有自己的风格,通常是多个应用程序,通常是微服务架构。



我肯定知道有很多大型公司在家中使用该框架:法语,俄语。碰巧有一些邮件通过邮件收到:他们说,他们以为我们工作了一个月,但他们挖了您的框架,并在几天内完成了该任务。伙计们节省了开发人员一个月的工作,这很棒。



谢尔盖·朱克(Sergey Zhuk):您进行任何教育活动吗?您的框架已经足够老,但是我很少快速浏览教程。好像问十个人,什么是AOP,九个人会说他们不知道。



亚历山大·利萨琴科(Alexander Lisachenko):是的,是的。



谢尔盖·朱克(Sergey Zhuk):尽管也许他们不知道这是件好事?



亚历山大·利萨琴科(Alexander Lisachenko):这是一个有争议的问题),但是我仍然有一个问题-这是文档。我不喜欢天生写文档。我可以写很酷的解决方案,也可以发明一些不寻常的东西,例如库,但是有了文档,这只是一个痛苦。



甚至有一次来自塞尔维亚的朋友向我建议:让我们写信。甚至开始编写它,但过了一会儿,保险丝也结束了。因此,事实证明,他们的文档并不丰富。



谢尔盖·朱克(Sergey Zhuk):那么,自然选择?最坚持不懈的人,他们会爬起来,挖得更深,他们会使用它。



亚历山大·利萨琴科:是的,这些人的水平足够高,不会被搞砸。



谢尔盖·朱克(Sergey Zhuk):您是否从使用您没有想到的方式使用框架的人那里收到了任何反馈?



亚历山大·利萨琴科(Alexander Lisachenko):是的,有这样的情况。例如,编写AspectMock的Mikhail Bodnarchuk。他采用了该框架,并意识到,有了该框架,他可以解决使最终方法,类甚至函数浸泡的问题。



重构旧应用程序的人抛出了另一个故事,他们没有测试-总的来说,一切都是经典的。在我的框架的帮助下,他们记录了每种特定方法的调用方式,并成为一个全局方面。在调用此文件夹中所有类的所有公共方法之前,已编写了所谓的内容以及返回的内容:什么类型,什么值。然后,他们开始在负载下运行此应用程序,以获取代码的所有可能状态。他们获得了每个方法允许的一组自动值,并返回值。也就是说,他们对整个状态进行了快照,然后设置了相反的方面,即调用方法并检查逻辑是否已更改。



实际上,他们设法实现了自动代码重构,而没有进行测试。



这是一个很酷的主意,一段时间以来,我一直在思考如何为旧版应用程序制作工具,以便冻结所有类,查看它们的名称和名称,然后在重构期间检查现有关系是否中断。但是,以某种可以外包的工具来实施它尚未解决。



ps感谢您阅读和聆听结尾!此处可以找到更多播客片段



All Articles