Aspect Oriented Programming in PHP with DingTweet
What is Aspect Oriented Programming and how we can do it in PHP
Aspect Oriented Programming (just AOP from now on), is a very powerful tool we can use to implement the infamous cross cutting concerns we have in our applications (auditing, logging, security, etc). In this article, I'll show how we can easily use AOP in a very simple language like PHP, by using the Ding container.
How AOP works in PHP by using the Ding DI container
Ding uses 2 patterns to achieve AOP in PHP:
The interceptor pattern is used to provide 2 kind of interceptions for methods: one to allow code to be executed before (and after) an intercepted method, and another one that only gets executed when the intercepted method throws an uncatched exception. Thus, when defining aspects and pointcuts, you will have to decide which one of them you will use. The general rule:
- Method Interceptors: Use them when you want your code to be run before (and after) the interesting method.
- Exception Interceptors: Only useful if you want to catch unhandled exceptions. Your code will be called only when the method throws an exception.
When you call a method that is not intercepted, the call is just executed by PHP directly, nothing special there. But when calling a method of a bean that has 1 or more aspects applied to it, you are in fact calling a proxy method that will call a dispatcher. The dispatcher will call all the aspects in order, if all these aspects proceed with the call, the last method in this chain will be the original intercepted method.
The IDispatcher interface and the DispatcherImpl implementation take care of handling and dispatching the chained calls to advices and original methods. This is automatically done by the container when it finds that a bean (or some beans) have any aspects associated with them.
Each method call generates a MethodInvocation object. This is the only argument that advices receive. Thus, your interceptor code, would look like (the names are just for illustrative purposes):
And that is pretty much you need to add AOP to your projects :)
How to apply the Proxy pattern in PHP to achieve AOP
In environments like the JVM, AspectJ manipulates the bytecode at runtime to achieve doing lots of cool things. In PHP we cant do that (or at least, I cant do that.. ). So we need to create a proxy, that will call the dispatcher instead of the original implementation.
Unfortunately, we cant create proxies by using the native reflection api provided with PHP, so we only have one option: generate php code that will actually be a proxy class for the class of the bean where we want to apply our aspects. This is also done automatically by the container, by using the Proxy class.
So for each bean that has aspects applied, ding generates the necessary proxies. These proxies can (and should) be cached. See the configuration section of the container in the ding manual to see how.
Defining Aspects and Pointcuts in your PHP Code
When defining an Aspect, you will need:
- A bean, where the advice code is. Effectively, the code you want to be run and intercept the target methods with.
- Type of interception: method or exception.
- A PCRE expression used to specify class names. All beans that match this class will have the aspects applied. This is kind of a "global" aspect definition, because it will trap all classes of a given name.
- You can avoid having the PCRE expression and declare the aspect right there inside the bean definition(s) you're interested in (vs. global aspects, that will apply to more beans if the class names matches).
When defining a Pointcut, you will need to specify:
- A PCRE expression that will be used to match method names.
- The name of the method to invoke when this pointcut matches.
You can then associate pointcuts to aspects in the configuration files (if using xml or yaml). You can reuse aspect definitions and pointcut definitions as well.
Where to find examples of doing PHP Aspect Oriented Programming with XML, YAML, and Annotations
So how about some AOP in PHP?
AOP is really great. Definitely not a silver bullet, but a tool that will just fit in certain ocassions.