A quick recap: In the previous post we talked about the class-based weaving approach followed by Exar. If you didn’t read the previous posts, you might want to do this first. Start here.

The process of weaving is realized by the Exar\Aop\Weaver class, performing the following tasks:

  1. read and parse the original PHP class
  2. weave AOP class / process code
  3. save the woven code und load the created class

The weaver accepts a path to a file where the original PHP class is located. It will then process this file.

Since every PHP class needs to be woven only once, we store all the woven code in a cache directory. This is how the weaver looks like:

Code processing

The goal of code processing is to wrap every single method of the original class. Suppose the following simple method:

Then, the woven code (wrapper method) will look like this:

As you can see, the original method is renamed (with suffix ___exar___generated) and became private. A new method with the original declaration is created instead. If doSomething() is called, the call arrives at the generated method where the original method is called via call_user_func_array():

The generated code before and after this line is needed to call the registered callbacks (see here). These are

  • Before Advice – runs before the advised method is called
  • After Returning Advice – runs after the method finishes execution without exceptions
    $im->afterReturning($ctx, $result)
  • After Throwing Advice – runs after the method throws an exception during execution
  • After Advice – run after the method finishes execution (successful or not)
    $im->after($ctx, $result)

The metadata about the method that is actually called is stored within the InvocationContext object (including target object, class name, method name and parameters of this method call):

InvocationContext also stores the exception object which can be thrown during method execution. In this case, we cannot exit the method and have to call all registered AfterThrowingInterceptor and AfterInvocationInterceptor objects instead.

To process the original code and generate the woven code, we will use PHP-Parser. This is a very impressive library which allows to manipulate the PHP code. PHP-Parser splits the PHP code into statements which can be processed by our weaver:

Now we take a look at the most important functionality during code weaving. The following piece of code generates a wrapper for a PHP method:

After reading the modifiers, the wrapper method body is created:

After that, the original method becomes private and gets a suffix, so every method call will arrive in the generated method wrapper:

Once all method wrappers have been created, the whole processed class has to be stored in a file (we don’t want to generate code every time the class is loaded as this is very time-consuming) which is loaded with the last step of code weaving:

If we run the weaver, it creates a class with the woven code which is stored within the cache folder (_cache by default). This is the final class which can handle advices described in the previous post.

Please note that the generated class hast to be advised to process annotations, so every generated field must therefore contain the following line at the end (e.g. for class \Exar\My\TestClass):

The annotation processor registers all annotations for classes, fields and methods:

Continue with Part 6: Putting It All Together