Blog






Archive for the ‘General’ Category

The power of the Intercepting Filter pattern in PHP

Tuesday, September 1st, 2009

Most of the people I have the pleasure to know and work with, all of them excellent PHP developers, old colleges and friends, don’t know about this amazing pattern: The Intercepting Filter.

Seems that this pattern is not commonly used at all in the PHP world for any reason. However, it’s one of the most powerful pieces within a framework.
So, give me 5 minutes to just introduce you to this pattern.

Imagine common tasks bound to every request in your site, tasks like to switch to a given language or currency, or even to authenticate your users via a cookie.
We must provide our application in order to intercept some information within the request in order to execute a task or the other one.

One of the most common way to do that in PHP, but not the most elegant at all, is to include one or more scripts in every page on our site in charge of parse the request and execute some tasks.
In fact, it’s really common to see a kind of common.php script that every page includes in. Not so maintainable, but very practical, right?

Problem is,

  • all the new pages we add to our site need to include this script? or just some of them?
  • if I change (for whatever reason) the script name and/or location, do I have to change all the pages including it in the entire site?
  • what happens if the script is included at the same time by other script like the typical header.inc that every page includes in order to decorate the site? how can we control the script execution from 2 including levels down?
  • assuming that we have hardcoded the sequence of common tasks in the script, how can we enable or disable some of the individual operations depending on some external factors like the url?

Those problems are appearing as soon as our projects grow. I have seen it in several projects, from the smaller and annoying ones to the most notable PHP projects, projects like word press or even os-commerce. Those projects are using hundred and even thousands of includes to script in order to ensure the execution of some common tasks. It’s more like a kind of includes-spaghetti code (I don’t know if this concept already exists, but I think is pretty illustrative).

So, intercepting filters is when it comes to the rescue:

Intercepting filters are basically classes in charge of intercepting some of the requests in order to execute tasks associated to them before (or even after) the execution of our logic.

Most important values on this pattern is the fact that

  • Filters are individual classes for individual tasks that you can activate or not selectively. Some time filters can be managed declaratively.
  • Filters are executed enchained in a given order defined by us, before and after giving the control to the FrontController and the rest of our application.
  • Filters can be maintained individually.

interceptingfilter

A sample of intercepting filter could be the one you use to intercept a cookie from the client and authenticate the user.

Filter code could be as simple as:

class CookieAuthenticationFilter extends __Filter {

    public function preFilter(__IRequest &$request, __IResponse &$response) {
        if(__AuthenticationManager::getInstance()->isAnonymous()) {
            if($request->hasCookie('auth_cookie')) {
                $cookie_identifier = $request->getCookie('auth_cookie');
                try {
                    $user = __ModelProxy::getInstance()->getUserByAuthCookieId($cookie_identifier);
                    if($user != null) {
                        __AuthenticationManager::getInstance()->setAuthenticatedUser($user);
                    }
                    else {
                        AuthCookieFactory::removeAuthCookie();
                    }
                }
                catch (Exception $e) {
                    AuthCookieFactory::removeAuthCookie();
                    __ApplicationContext::getInstance()->getLogger()->error('Error trying to log-in by cookie: ' . $e->getMessage());
                }
            }
        }
    }
}

And we can associate this filter to all our pages by just declaring it in the filters.xml file as following:

picture-4

You like it? please take a look here:

http://www.lionframework.org/d.filter-1

AJAX was never easier!

Monday, February 16th, 2009

Ajax should be as easy as just write a method in our server-side code and consume it from our client javascript. No more steps!
Well, let’s illustrate it with an example:
Imagine that we have created the following method (a method to log messages):


<?php

class MyEventHandler {

  public function logMessage($message) {
      __ApplicationContext::getInstance()
                          ->getLogger()
                          ->info($message);
  }

}

And we pretend to log messages generated from our client javascript.
According to our intro, it should be as easy as just call the logMessage method from our javascript code:

<script language="javascript">

  logMessage("Message to be logged from the server side");

</script>

In the other hand, we should be able to selectively define which methods are allowed to be consumed from the client (otherwise, imagine what a security issue).
In that sense, we could let the framework know that a method is callable from javascript by just adding a comment-based notation to them:
Let see it in terms of code:

<?php

class MyEventHandler {

  /**
   * @lion remoteService
   */
  public function logMessage($message) {
      __ApplicationContext::getInstance()
                          ->getLogger()
                          ->info($message);
  }

  public function myNonCallableMethod() {
      //whatever php code here
  }

}

With the remoteService annotation we are letting lion framework know that the method foo is callable from javascript code. The framework will perform the rest of the work for us.

As soon as AJAX is asynchronous by definition, a good question could be: what about values returned by server-side methods? How are them handled by the client javascript code?
I’ll explain it in my next post (maybe tomorrow) :)
Regards Antonio