6 minutes reading time (1245 words)

Adding Interceptor Filters via Annotation in Joomla!

In this article I'm going to show you how you can make use of Interceptor Filters via annotations within a Joomla application. It makes use of the principle of Aspect-Oriented Programming.

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 an '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 hasn't got native support for annotations, but we can use comments for this. In our controller it looks like this.

[php]
/**
* FrontController to manage functionality around the 'Mijn Westlandia' functions
*
* @author Paul de Raaij <This email address is being protected from spambots. You need JavaScript enabled to view it.>
*
* @Filter(\Services\MemberAuthentication)
*/
class IvejoControllerAccount extends IvejoControllerFrontBase {

public function index() {
$this->assign('account', $this->getAccount());
$this->render('view');
}
}
[/php]

That is not too 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's start with explaining the filter. Each filter should implement the interface InterceptorFilter which looks like this:

[php]

/**
*
* @author pderaaij
*/
interface InterceptorFilter {
function setContainer(\Services\DI\Container $container);
function doFilter();
}
[/php]

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.

[php]

/**
* 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;
}

}

[/php]

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 I made 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 of the utmost importance in this use case for Joomla is that each controller that makes 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.

[php]

public function execute($task) {
$discoverer = new InterceptingFilterAnnotationDiscoverer( $this->getContainer() );

if( $discoverer->hasFilterAnnotation($this) ) {
$interceptor = $discoverer->getInterceptor($this);
$interceptor->doFilter();
}
parent::execute($task);
}

[/php]

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 at that class.

[php]

/**
* Class to see if a given class has an @Filter annotation.
*
* @author Paul de Raaij <This email address is being protected from spambots. You need JavaScript enabled to view it.>
*/
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;
}
}
[/php]

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 all there 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 wrote these articles to give you some inspiration and I'd love to get some feedback and be inspired myself. Just don't use this as cut-and-paste code, use it to work on your own implementations.

This is originally posted on my personal weblog.

0
Joomla Forum User Webdongle reaches 20,000 Posts
Google Panda & Penguin - How to Identify Problems ...
 

Comments

Already Registered? Login Here
No comments made yet. Be the first to submit a comment

By accepting you will be accessing a service provided by a third-party external to https://magazine.joomla.org/