If you have read my other articles about Joomla and automated binding or Using Doctrine in Joomla then you probably know that I also heavily develop in Java. Java heavily makes use of Annotations which combined with the principle of Aspect-oriented programming gives developers some great power in controlling the flow of the application. One thing we use regularly in our application are called Intercepting Filters. Basically you set a ‘insertion point’ where code can be executed before executing the application code. In this post I’ll show you an implementation like I have done in a hobby project of mine.
First, let me explain the problem. In this case I have a controller which defines some actions to view and edit an account. For this particular application I use the standard Joomla! user and link that to a custom account entity in the application. So the user edits the custom account entity, but we determine the custom account based on the current Joomla! user that is logged in to the website.
A thing we can do is to write this functionality in a service and make use of this service in each action method in the controller. But this clutters the method with code which will be repeated in each method. We can use an interceptor to do this on each call on the controller.
So, how do we define a filter? Well, we use an Annotation for this. Unfortunately php haven’t got native support for annotations, but we can use comments for this. In our controller does it looks like this.
/** * FrontController to manage functionality around the 'Mijn Westlandia' functions * * @author Paul de Raaij <paul@paulderaaij.nl> * * @Filter(\Services\MemberAuthentication) */ class IvejoControllerAccount extends IvejoControllerFrontBase { public function index() { $this->assign('account', $this->getAccount()); $this->render('view'); } }
That is not to hard, we can make use of a filter in a single line. In the value of the annotation we define the class that executes the filter code.
Let start with explaining the filter. Each filter should implement the interface InterceptorFilter which looks like this:
/** * * @author pderaaij */ interface InterceptorFilter { function setContainer(\Services\DI\Container $container); function doFilter(); }
The filter that I’ve used in the example for this post and the hobby project looks like this. Note, it just shows the implementation of the filter. I’ve removed some of the utility methods to keep the implementation clear.
/** * Filter to ensure that logged in Joomla users are matched to accounts in Ivejo. * * @author pderaaij */ class MemberAuthentication implements InterceptorFilter { /** * @var \Services\DI\Container; */ private $container = null; /** * Register the active account if any and make it available * thru the DI container. */ public function doFilter() { $this->container->register('activeAccount', null); $user = $this->getJoomlaUser(); if( $user != null && $this->container != null ) { $account = $this->getAccount( $user ); if( count($account) == 1 ) { $this->container->register('activeAccount', $account[0]); } } } public function setContainer(DI\Container $container) { $this->container = $container; } }
That’s an implementation of a filter. You see it isn’t hard to make use of a filter and you can do anything you would like in a filter. In this example code you see I make use of a DI container. It is a very simple DI container which is based on the Pimple DI container.
Now let us see how the filter annotation will be read and executed. What is from outermost importance in this use case for Joomla is that each controller that make use of this filter interceptor, needs to be extended from a base class. Personally I always do this when I work on a Joomla site, there are somethings that I miss in Joomla and via this base class I can fill in these voids.
In this base class we extend the execute method of JController, so we can see if there are any filters annotations defined. The extended execute method is defined like this in the base class.
public function execute($task) { $discoverer = new InterceptingFilterAnnotationDiscoverer( $this->getContainer() ); if( $discoverer->hasFilterAnnotation($this) ) { $interceptor = $discoverer->getInterceptor($this); $interceptor->doFilter(); } parent::execute($task); }
Each time the execute method will be called via the Joomla! framework we check in the controller if an annotation is defined. The checks to see if an annotation is defined in the class InterceptingFilterAnnotationDiscoverer. Let us take a look on that class.
/** * Class to see if a given class has an @Filter annotation. * * @author Paul de Raaij <paul@paulderaaij.nl> */ class InterceptingFilterAnnotationDiscoverer { /** * @var \Services\DI\Container; */ private $container = null; public function __construct(Container $cont) { $this->container = $cont; } /** * Check if the class has an filter annotation * * @param String $clazz * @return boolean */ public function hasFilterAnnotation( $clazz ) { $reflectionClass = new \ReflectionClass(get_class($clazz)); return strpos($reflectionClass->getDocComment(), '@Filter') !== false; } /** * Get the interceptor which has been set by the @Filter annotation * * @param String $clazz * @return \Services\interceptors|null */ public function getInterceptor( $clazz ) { if( $this->hasFilterAnnotation($clazz) ) { $reflectionClass = new \ReflectionClass(get_class($clazz)); preg_match_all('/@Filter\((.*?)\)/s', $reflectionClass->getDocComment(), $matches); $interceptors = $matches[1]; if(count($interceptors) > 0) { if(class_exists($interceptors[0]) ) { $obj = new $interceptors[0]; $obj->setContainer( $this->container ); return $obj; } } } return null; } }
There are two methods in this class. The check to see if a filter annotation is defined is quite easy. Via reflection we load the comment block of the class. In this comment we check if we find an @Filter definition. In the method ‘getInterceptor()’ we are going to get the value of the annotation to see which interceptor is defined. If the class exists we create a new instance of it and return it. The caller of this method gets an instance of an InterceptorFilter and has to do the execution of doFilter method by itself.
And that is what it is to it. Of course, just like other articles I’ve written, there are many enhancements you can do on this code. Adding some more validation would be mandatory, if you ask me, if you want to use it in a production environment. Also it would be a good enhancement to allow multiple definitions of filters to be defined in a single annotation.
I write these articles to give some inspiration and I’d love to get some feedback and be inspired myself. Just don’t use this a cut-and-paste code, use it to work on your own implementations.
Recent Comments