You decided to write your own framework. Was it worth it?





As the classics said, “I knew that sooner or later we would come to that”. So I, after many years of quiet life with Symfony in workers and ReactPHP in pet projects, fit into the creation of my framework.



But his story is just beginning. And what about those whose brainchild has grown to the production level, but has remained a niche solution? I found someone who knows the answer to this question - the author and lead developer of an aspect-oriented framework.



Hello! Here is a partial transcript of my podcast "Between the Brackets" - interviews with interesting people from the PHP world. In this episode, we'll talk to Alexander Lisachenko, the creator of a Java-based framework that is often Google Googled.



If you are more comfortable listening, there are more technical examples in the audio version .


Most likely, not all of us have come across such a thing as AOP - aspect-oriented programming in PHP. And Sasha got stuck and made his own decision to drag this thing into our applications.



Sergey Zhuk, Skyeng and DriftPHP: Let's start from scratch - what is AOP and what problem does it solve?



Alexander Lisachenko, PHP Russia and Go! AOP : The idea is not new. AOP was invented at Xerox to address the end-to-end functionality problem. And in the Java world, the approach is actively used to check functionality such as authorization, authentication, caching, logging, managing feature toggles, circuit breakers.



A very wide range of tasks can be solved using aspects. Let's use examples:



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


If we look at the application, we will see that such code fragments are found everywhere and it is not very convenient to work with it. This so-called end-to-end functionality is present throughout the application.



And there is no good way to somehow bracket it out using traditional object-oriented programming.



Yes, of course, we can try to take a decorator - but if we want our class to implement the same interface, we need to implement or generate each method right in the decorator. That is, to have decorators for all the classes that we want to cache.



Sergey Zhuk: Ok, I got it. But for this there were already some PECL extensions, the framework even existed. Why did you decide to write your own?



Aleksandr Lisachenko: Yes, you have correctly noted that there were already a number of solutions. But, as it seemed to me, they had significant drawbacks.



Some of the solutions tried to transform the source code, immediately embed a specific advice into a specific class - a piece of code that is repeated from one method to another. With xlt they generate one class and put absolutely everything there. In general, it's better not to open this code at all) The



second class of solutions is extensions. For example, PHP AOP - it is, in principle, quite functional, but it does everything at runtime. When we add one advice, it seems like everything is fine, but we add a second one and the speed starts to decrease proportionally. Accordingly, on ten tips, the application starts to slow down, and if we add a couple of dozen more, that's all - timeout is guaranteed.



Somehow it so happened that I saw how an idea called "filters" was implemented in the Lithium framework - such a prototype of modern middlewares. We create some previously known points in the program, and filters can be applied to these points - both before the call and after. This idea seemed so interesting that I decided to write an application and expose cross-cutting functionality using these filters. I started to study how it is done in Java.



And I realized that in PHP it will not be possible to implement it offhand. That was probably the most interesting moment.



In general, the whole process of writing the framework was a constant struggle with "no" and "impossible". It would seem that I could not implement fundamental things, but it was all the more interesting to deal with them.



Sergey Zhuk: I just can't help but ask. Why Go AOP? The first release of the framework took place in early 2013, the Go language already existed then, and you had PHP. It's like with JavaScript and Java, right?)



Alexander Lisachenko: The first commit does not equal the beginning of local development on my computer) At that time, Go was not yet popular. I googled, saw that there is some kind of internal development by Google, it occupies some niche of its own ... And did not bother.



I chose the name of the framework itself, based on the internal, so to speak, wishes. From go - "go", "forward", "do".



Sergei Zhuk: And then there were no thoughts to rename?



Alexander Lisachenko: Formally, the full name of the Go! AOPPHP . But over time, I removed PHP, because it is installed through Composer and it seems there is no point in making the oil oily.



So far, I'm getting some extra traffic: a lot of people trying to find AOP for Go end up on my PHP framework. Perhaps in some future version it will be necessary to emphasize that this is not about Go. So far, no one has started an issue on GitHub in this regard. There were no complaints from Google or the community either.



Sergey Zhuk: Ok, how is this implemented from the point of view of the client code? Here I have an application, for example, on Laravel, there are a couple of integrations with third-party services, I want to log these calls.



Alexander Lisachenko: There is already a special module for Laravel. He will put everything you need into the system, configure it. You will need to write an aspect - it will be a service, you will tag it and it will be automatically picked up by the AOP core. In the very aspect, you need to understand at which points of the application, in which classes and methods, you want to implement the caching functionality. You can specify a method directly with a signature (but the option is not very flexible, so the method can be renamed, but it remains in the aspect), or you can mark the method with an annotation. The second option is more flexible: where it is necessary to cache, just mark it as cacheable, and the engine will do the caching for you and call the callback, which is in the aspect's code.



At the time of loading your class with Composer, the framework intervenes and checks if there is a ready version for this class in the cache with an embedded aspect. If there is, it immediately gives it back, no additional checks are made in runtime. If there is no cache, then we will build an AST-tree, and on top of this tree we will create a reflexion class, but without loading it into memory. And at this moment we can change it the way we want, that is, weave the aspect code inside this class.



I don't like noodles inside aspects and decided to split the class into two parts.



The original class remains virtually unchanged - only the name changes. And a second class appears with the original class name, which extends the main one and can, if necessary, override several methods.



Why, in fact, inheritance. There are many methods and classes that return instance back. For example, the well-known chain method: we call the chain of methods on the object, it returns $ this. If we decorate, then the first call will work, but then the aspect will fall off. Along with inheritance, memory is saved - because there is still one instance in memory.



Sergey Zhuk: All this architecture, the engine, have you thought of everything from the very beginning or?



Alexander Lisachenko:There were many things to dive into. For example, I was not very familiar with AST, so I studied many disciplines related to the description of grammars. And if you look at my framework, then my pointcut implements a full-fledged grammar and has its own syntax - this is probably one of the great achievements. You can write as many complex expressions as you like, for example, “call of any public method that does not start with asset, implements interface such and such inside space of such and such”.



Also I dug a lot inside PHP. I watched where what extensions are, how they work. Somewhere I sat with profiling, optimized something, tuned: but now, if you just connect the AOP framework, the application will add some funny 7-10 milliseconds. At the level of the classic 100 milliseconds of response, it is even imperceptible that such a huge framework is being called under the hood.



Sergey Zhuk: Is there a specificity for different PHP frameworks?



Alexander Lisachenko: In principle, the AOP framework was conceived as a general library that does not require specific binding. The main condition is to use Composer. But it is not very friendly with Symfony.



There is too much black magic in Symfony, and when it clashes with the magic in my framework, the strongest, Symfony, wins.



In general, the idea of ​​Symfony is that there is a container, you need to use it and not invent separate frameworks to get the AOP functionality. There are more traditional ways: include a bundle - say, JMS AOP or my Symfony Go AOP bundle .



Sergey Zhuk: Let's talk about the community and competitors. Do you have them?



Alexander Lisachenko: As far as I know, there are three frameworks now. There is Ray. Aop, but it will not be useful for production, because it does not know how to work effectively with Composer. The authors of Flow serve up their framework with the sauce that we have AOP here. There is something else next to Chinese frameworks, there are straps on top of Swoole - but this is all at the level of extensions, and extensions can not be missed by admins for security reasons. I still have a classic framework, and it stays alive on any version.



As for the community. There are probably only four people who understand and understand well: me, one guy from Serbia, and two people from my former job who participated in everything I did. Naturally, I showed them all my developments and results. The last couple of months, as I changed jobs, put very little effort and energy into open source, but it lives and works autonomously.



, - Z-Engine — c , , PHP.



I plan, as soon as time becomes free, to continue working on the Z-Engine and make the next version of the framework, based on the internal structures of the language itself. It will work almost like Java's AspectJ. The goal is to get there in PHP 8.



Sergey Zhuk: This is almost a complete rewrite of the framework?



Alexander Lisachenko: No, I have everything decomposed. Only the code injection process changes: there are literally a few classes that are responsible for making edits to a particular class. And in this case, this class will not make files in the cache with different structures, but at this moment at runtime will change OPcache and modify PHP structures in memory.



Sergey Zhuk: And what is the general attitude of the PHP community to this topic? I'm fond of asynchronous PHP, it leaves few people indifferent. How are you doing with that?



Alexander Lisachenko: There will always be those who say that we can do this without AOP, why do we need it in applications. My answer is that if you don't work in an enterprise, the aspects are not useful to you. And if you think that you have an enterprise, but you have one service and 2-3 developers, this will not work for you either) AOP works well in teams where there are several dozen people, each of them writes in his own style, there is, typically multiple applications, and typically a microservice architecture.



I know for sure that there are a number of large companies that use the framework at home: French, Russian. It happens that some messages with gratitude arrive in the mail: they say, they thought we had a month of work, but they dug up your framework and completed that task in a couple of days. The guys saved a month of their developers' work, that's great.



Sergey Zhuk: Do you conduct any educational activities? Your framework is old enough, but I did a quick look for tutorials - sparsely. I think if you ask ten people what AOP is, nine will say they don't know.



Alexander Lisachenko: Yes, that's right.



Sergey Zhuk: Although, maybe it's good that they don't know?



Alexander Lisachenko:This is a moot point) But there is a problem that I had and still have - it's the documentation. I don't like writing documentation by nature. I can write cool solutions, I can invent some unusual things, libraries, but with documentation it is just a pain.



Even at one moment a friend from Serbia suggested to me: let's write. And even started writing it, but after a while the fuse also ended ... And so it turned out that they are not rich in documentation, let's say.



Sergey Zhuk: So, natural selection? The most persistent, who climbed, dig deeper, they use it ...



Alexander Lisachenko: Yes, and these are the people who have a sufficient level so as not to screw up.



Sergey Zhuk: Have you received any feedback from people who have used the framework in a way that you wouldn't have guessed?



Alexander Lisachenko: Yes, there were such cases. For example, Mikhail Bodnarchuk, who wrote AspectMock . He took the framework and realized that with it he could solve the problem of getting final methods, classes, and even functions to soak.



Another story was thrown by guys who refactored an old application, and they didn't have tests - in general, everything is classic. With the help of my framework, they recorded what each particular method is called with and made a global aspect. Before calling all public methods in all classes in this folder, it was written what is called and what is returned with: what types, what values. Then they started running this application under load to get all possible states of the code. They got an automatic set of values ​​allowed for each of the methods, and return values. That is, they took a snapshot of the entire state, and then set the reverse aspect, which called the method and checked if the logic had changed.



In fact, they managed to implement automatic code refactoring without covering it with tests.



It was such a cool idea that for some time I thought about how to make a tool for legacy applications so that I could freeze all the classes, see what and how they are called, and then, during refactoring, check that the existing relationships are not broken. But to implement it in some kind of tool that could be outsourced has not yet worked out.



ps Thank you for reading and listening to the end! More podcast episodes can be found here .



All Articles