This is the last part of our Exar series which explains how the woven code handles method calls and how to write custom interceptors to extend Exar’s functionality.

In case you haven’t read the previous articles, you can catch up here:

  1. Motivation
  2. Reading Annotations
  3. Instantiating Annotation Objects
  4. Weaving Strategies
  5. PHP Class Weaving

You may recall the class weaving strategies described in the previous post. Let’s consider a method used to convert a given string to uppercase.

The woven class will therefore contain the original and the woven (proxy) method:

The original method was renamed and became private, so the method toUppercase can only be invoked from the created proxy method (from line 6 onwards). Proxy methods perform the following tasks during their execution:

  1. create the invocation context (line 8) – used to encapsulate all the information needed to call the actual method functionality (target object, class and method name, and the array with parameters)
  2. retrieve singleton instance of the InterceptorManager (line 9) – used to call the target object according to the information stored within InvocationContext (line 12)
  3. InterceptorManager calls the registered interceptor for associated joinpoints: before (line 11) and after (lines 13, 21, 26) method execution
  4. store any exception thrown by the original method within InvocationContext (line 20)
  5. return the result of the method execution (line 30)

Note that all generated variable names contain a unique hash which prevents conflicts with existing method parameters, e.g. if there is a parameter $ctx already in the original class.

In this way we are able to intercept methods in any classes annotated with @Exar.

Writing Custom Interceptors

To write our own interceptors all we have to do is create a class with namespace Exar\Aop\Interceptor that is subclass of Exar\Annotation\Annotation:

Now we have to decide if our annotation is going to be a class or a method annotation. We use @Target("method") because we want to intercept a single method call only:

Note that we defined just a simple annotation. It doesn’t have any interceptor features so far. We turn this annotation into an interceptor by implementing one or more interfaces, depending on which joinpoints our interceptor will handle.

For example, if we want to take actions upon entering and leaving a method, we need to implement BeforeInvocationInterceptor and AfterInvocationInterceptor:

That’s all! To see out new interceptor in action, we annotate the desired class, e.g.:

What Else?

One important thing we focused on while implementing Exar was performance. It takes a lot of time to parse and weave classes, that’s why we decided to cache woven classes in files so the weaving has only to be performed once per class. There are still some further improvements that can be made to increase performance. For example, all class and method annotations can also be stored within cache files, so that the annotation parsing doesn’t have to be executed on every request.

Currently there is no overview of @Exar annotated files at runtime, that means every AOP-aware class has to be loaded actively. Therefore, there is no way to register global observer classes which would react to specific events.

That’s the main difference to Java EE applications where all classes are read at compile time and the application has the complete overview of all dependencies between all classes. Theoretically, it is possible to realize a kind of pre-compiling in Exar where all classes are woven and cached before the application execution starts. That would mean that Exar has to recognize which classes were changed since the last access, so they haven’t to be woven on next request.

So we might implement this feature in the future if there is a high demand for it.

Exar is open source! Check out the project page or fork Exar from GitHub. If you are using Exar in any of your products just drop us a line and we’ll feature you on the project page.

Join our newsletter – stay in the loop!