SourceForge.net Logo

A PHP5 pluggable framework based on the

POSA2 interceptor design pattern

 

 

Lee Provoost

Software Technology Group

Department of Information and Computing Sciences

University of Utrecht, P.O. Box 80089 3508 TB, Utrecht, The Netherlands

{l.provoost} @students.uu.nl

 

 

Abstract

 

The POSA2 interceptor design pattern by Schmidt et. al allows us to plug in extra functionality in a framework using predefined hooks. The original implementation is programmed in C++ and can also be easily done in Java. With the growing popularity of dynamic typed scripting languages like PHP5, it is interesting to see if such languages are mature enough to implement complex design patterns from the object-oriented world. The goal of this paper is twofold; first of all demonstrate how the interceptor pattern works, but also to test PHP5Õs new object-oriented support. I want to show that we can build complex applications using the former ÒtoyÓ scripting language. Several language specific issues are taken in account, like the lack of language built-in support for events in PHP5, dynamic typing, etc. To make the framework more dynamic (and interesting), I implemented the implicit interceptor pattern variant, which frees developers from the burden of explicitly registering the plug-ins.

 

 

Keywords

 

Interceptor design pattern, PHP5, framework

 

 

Introduction

 

The book ÒPattern-oriented software architecture, Volume 2Ó by Schmidt et. al [1] is quite famous among developers of distributed systems. Originally implemented in C++, there are several implementations in Java available. With the growing popularity and maturity of dynamic-typed scripting languages, it is interesting to see how well we can implement complex patterns in these languages. IÕll take for this project, PHP5 as an example, but it could have been Ruby on Rails or Python also.

 

In this paper I will discuss the implementation of the interceptor design pattern in PHP5. The interceptor pattern allows applications and frameworks to be extended with extra services without requiring changes to the core implementation. These services can be added through predefined hooks. Because we can easily plug new services in our framework, I will often refer to these extra services as plug-ins.

 

This project wants to address two aspects. First of all trying to find out whether these dynamic-typed scripting languages are suitable for developing complex frameworks, but also showing people how you can implement your own framework that dynamically loads plug-ins based on the interceptor pattern.

 

 

 

1. POSA2 INTERCEPTOR PATTERN

 

1.1 Problem

 

We want to build an extensible framework with the following three aspects:

á       Extending the framework with extra services shouldnÕt require modifications to the core architecture.

á       Adding new services to the framework should not affect or rely on other services.

á       The added services should be able to access and modify the frameworkÕs internals.

 

 

1.2 Solution

 

We will make a framework where extra services can be registered through predefined interfaces or hooks. Services have to register themselves with a dispatcher who will notify them when certain events occur. Services should be independent and should not be aware of or take in account that other services might be registered with the same dispatcher. To open up our implementation and allow changes, we will provide context objects to the services that can be read and manipulated. These context objects contain the data that is flowing in our framework.

 

 

1.3 Implicit interceptor pattern variant

 

The original interceptor pattern requires that when you extend the application with some concrete interceptors, you have to register these interceptors with the dispatcher. This makes sense at first, but limits the dynamic behavior of your framework/application. It would be nice to just drop some plug-in into a directory and let the application find them and register them automatically. This can be accomplished with the implicit interceptor pattern variant.

 

A lot of famous applications that allow plug-ins like Adobe Photoshop and Eclipse have dedicated plug-in or extension directories where you can copy your plug-in or extension and they will be detected and loaded at application load-time. But this dynamic behavior comes at a cost: performance. Everyone can agree that starting up Photoshop or Eclipse takes a while. However this performance issue is only present at starting-up and once up and running, the program performs like a breeze.

 

Developing web applications, however, requires a total different approach. Languages like PHP are stateless and are interpreted at every invocation. When you apply the dynamic approach as earlier described with Photoshop and Eclipse, then your web application will detect and load each time your plugin directory. It is not hard to see that this causes a serious performance penalty.

 

To minimize the load time while still allowing dynamic loading of plug-ins, I opted for an intermediate approach. I reserved a special plug-in directory where plugin writers can drop their code, but they have to additionally configure a configuration file. Instead of scanning a directory and figuring out how to use the plug-ins, I require plug-in writers to provide all the information immediately in the config.xml file.

 

 

 

 

 

 

 

 

<plugin>

    <class>Logging</class>

    <method>onPreSubmit</method>

    <hook>onPreSubmit</hook>

    <interceptor>Post</interceptor>

    <filters>

        <filter>BlogEntry</filter>

        <filter>BlogComment</filter>

    </filters>

</plugin>

 

Example of plug-in configuration

 

The semantics of this configuration file will be explained further in the paper when IÕll discuss the implmentation details.

 

Although this intermediate solution is quite nice, it still puts some overhead in the execution of the scripts. Parsing XML files is usually an expensive operation. Some developers shifted from XML configuration files to code configuration files. This means that you require the plug-in writers to supply the information in PHP code. This has the advantage that no parsing is needed anymore. The downside of this approach is that such solutions are usually a little harder to understand and more error-prone. If you develop your web application with performance in mind, IÕd suggest using this code configuration file approach or just making your application less dynamic. As I said before, everything comes at a costÉ

 

<?PHP

    $plugin[0]["class"]     = "Logging";

    $plugin[0]["method"]    = "onPreSubmit";

    $plugin[0]["hook"]             = "onPreSubmit";

    $plugin[0]["interceptor"]      = "Post";

    $plugin[0]["filters"][0]       = "BlogEntry";

    $plugin[0]["filters"][1]       = "BlogComment";

?>

 

Example of code configuration file

 

 

1.4 Interceptor pattern != Aspect-Oriented Programming

 

An often-made mistake is that the interceptor pattern is confused with aspect-oriented programming. At first sight, they look very similar, but they have some significant differences.

 

 

1.4.1 How does intercepting take place?

 

Both the intercepting pattern and AOP are talking about intercepting, the former about events and the latter about calls. The interceptor pattern requires some predefined hooks in the framework where we can attach plug-ins too. So your whole application is made with the goal of allowing other people to attach code and changing and retrieving values to and from your context objects. AOP has a whole different approach. AOP does not require being ÒAOP readyÓ. We can easily apply AOP techniques on existing software, which were never written with AOP in mind. IÕll give an example from the AspectJ project [2].

 

 

 

 

 

 

public class Demo {

    static Demo d;

    public static void main(String[] args){

        new Demo().go();

    }

    void go(){

        // implementation details left out

    }

}

 

Example of a standard Java class

 

aspect GetInfo {

   static final void println(String s){ System.out.println(s); }

   pointcut goCut(): cflow(this(Demo) && execution(void go()));

   pointcut demoExecs(): within(Demo) && execution(* *(..));

 

   Object around(): demoExecs() && !execution(* go()) && goCut() {

      println("Intercepted message: " + thisJoinPointStaticPart.getSignature().getName());

      printParameters(thisJoinPoint);

      println("Running original method: \n" );

      Object result = proceed();

      println("  result: " + result );

      return result;

   }

 

   static private void printParameters(JoinPoint jp) {

       // implementation details left out 

   }

}

 

Example of an aspect that weaves around the go method

 

As you can easily see in the above example, the class Demo is just a plain old java class with a method void go(). The aspect GetInfo wraps around the void go() method as you can see in the bold line.

 

 

1.4.2 When does intercepting take place?

 

Another big difference between the interceptor pattern and AOP is when intercepting kicks in. the POSA2 interceptor pattern definition [1] states that intercepting takes place at run-time. This makes sense because interceptors are triggered by certain events in the system. Interceptors in AOP are usually woven into the system right in the source code at compile-time or sometimes at startup-time of the application. AspectJ for instance has a dedicated compiler that ÒinjectsÓ the aspects in the class files.

 

The AOPHP project [3] aims to bring aspect-oriented programming to the PHP world. They have a different approach. They canÕt inject the aspects in some compiled PHP files because PHP scripts are interpreted and not compiled. (LetÕs not consider caching systems like the Zend Optimizer.) Their approach is to make use of the Apache mod_rewrite module to redirect calls to PHP files to the AOPHP Java program that does some code weaving. The authors are aware of the performance overhead caused by this Java approach and are working on native C module for Apache2.

 

 

2. PHP5

 

2.1 New language features

 

When PHP5 was introduced in 2004, it came with an impressive new object model. It was finally possible to program decent object-oriented programs with PHP. The predecessor, version 4, has only very basic support for object-oriented programming. Basically it has just the concept of classes and the most advanced OO operation you can do with PHP4 is extending from a base class.

 

PHP5 introduced interfaces, abstract classes, destructors, private and protected members and methods and much more. Although it still doesnÕt reach the same level as Java, it is a very big leap forward and we can finally build complex object-oriented applications.

 

My interceptor framework would have been very hard to implement with PHP4. It would have been possible but then we wouldnÕt have a generic and easy to extend framework.

 

For an extensive overview of the new language features IÕd like to refer to the website of Zend [4].

 

 

2.2 Dynamic typing

 

There are huge debates going on whether dynamic typing is a good thing or not. Scripting languages like Ruby, Perl and PHP tend to have dynamic typing, but also compiled languages like Visual Basic. A big difference with static typing is that type checking takes place at runtime because variables can acquire different types depending on the execution path. There is no explicit defining of types while programming and this can lead sometimes to hard to find bugs. The PHP engine for instance converts variables from string to integer type and vice versa where necessary.

 

Although this loosely typed aspect works fine, in an enterprise environment you prefer to have a mechanism to enforce stricter type checking to be 100 % sure. The new PHP5 introduces a feature called Òclass-type hintsÓ where you can declare the expected class of objects that are passed as parameters to a method. I applied this technique as much as possible and while developing my framework I discovered a small bug in my code when I introduced class-type hints.

 

Interface PostInterceptor {

             

    static function onPreSubmit (Event_Notification $notification);

    static function onPostSubmit (Event_Notification $notification);        

}

 

Example of class-type hints

 

In the above example you can see that we have an interface PostInterceptor with two static methods onPreSubmit and onPostSubmit, which require both one argument. Although PHP programmers usually just give a variable name, I prefer to use the class-type hint Event_Notification to indicate that this method requires an Event_Notification object. This is checked at run-time.

 

However I couldnÕt apply this class-type hinting to all my methods. Unfortunately this does not work with built-in types like strings and integers. This would have saved me some time because at a certain moment I was sure that I passed a string while I was actually passing a SimpleXMLElement object. An example when dynamic typing can get nasty is when I printed that SimpleXMLElement object to my console for testing purpose. Because my SimpleXMLElement object only contained a string, PHP automatically converted it to a string, because the echo function of PHP triggered the conversion to a string type.

 

 

 

 

 

 

foreach ($plugin->filters->filter as $filter) {

    if (in_array($filter, $allowed)) {

        // register the allowed interceptors

        $this->_bd->register(

            (string) $plugin->class,

            (string) $plugin->method,

            (string) $plugin->hook);

        echo $plugin->class;

        var_dump($plugin->class);

    }

}

 

Example where dynamic typing can go wrong

 

Logging

 

object(SimpleXMLElement)#14 (1) {

  [0]=>

  string(7) "Logging"

}

 

Output of the previous code

 

In the above example you can see that echo $plugin->class nicely prints the string value due to the conversion by the PHP engine. When you however use the var_dump() function, then you see what $plugin->class really contains. As the code snippet shows, we can easily solve this by explicitly casting the type of $plugin->class from SimpleXMLElement to the built-in string primitive. So putting type hinting of primitive built-in types would be a nice thing to put on the PHP6 feature wish listÉ

 

 

2.3 Event_Dispatcher

 

One of the key aspects of the interceptor pattern is that services are triggered automatically when certain events occur. Unfortunately PHP does not have built in support for events like Java.  Luckily the Frenchman Bertrand Mansion has released an Event_Dispatcher package in the PEAR repository [5] that adds support for events in PHP. Although still in beta version (version 0.9.1 at moment of writing), it is quite complete and most important bug free.

 

Basically the Event_Dispatcher package was implemented with the observer design pattern or publisher/subscriber design pattern in mind. The Event_Dispatcher acts like a notification dispatch table where client objects can register themselves as observers of specific notifications by other objects.

 

The nice thing about the package is that it acts more or less as the dispatcher described in the interceptor pattern. It provides methods to register and remove observers and the Event_Dispatcher object contains already a list of the interested parties, if I may describe it like that. So this package really helped me in implementing the PHP based interceptor framework and saved me some headaches.

 

Event_Dispatcher::getInstance() -- Create a new Event_Dispatcher object

Event_Dispatcher::getName() -- Get the name of the dispatcher.

Event_Dispatcher::addObserver() -- Add a new observer.

Event_Dispatcher::removeObserver() -- Remove an observer.

Event_Dispatcher::setNotificationClass() -- Set the class that is used as notification.

Event_Dispatcher::post() -- Post a notification.

Event_Dispatcher::addNestedDispatcher() -- Add a nested dispatcher.

Event_Dispatcher::removeNestedDispatcher() -- Remove a nested dispatcher.

Event_Notification -- Container class for notifications.

Event_Notification::Event_Notification() -- Create a new notification object.

Event_Notification::getNotificationName() -- Get the name of the notification.

Event_Notification::getNotificationObject() -- Get the object that sent the notification.

Event_Notification::getNotificationInfo() -- Get additional information from the notification.

Event_Notification::getNotificationCount() -- Get number of observers notified.

Event_Notification::cancelNotification() -- Cancel the notification.

Event_Notification::isNotificationCancelled() -- Check, whether notification has been cancelled.

 

Event_Dispatcher and Event_Notification methods available in version 0.9.1

 

In the above list you can find an overview of the available functionality as of version 0.9.1 of the Event_Dispatcher PEAR package. I only needed four of the methods: Event_Dispatcher::getInstance() to get a singleton instance of the Event_Dispatcher object. Event_Dispatcher::addObserver() and Event_Dispatcher::removeObserver() to add and remove interceptors and Event_Dispatcher::post() to define the interceptor points.

 

To make the integration of the Event_Dispatcher package in my framework more transparent and to stick more to the definition of the interceptor design pattern as described in the POSA2 book by Schmidt et. al, I wrapped this up in a Dispatcher class in my framework. More details can be found in the next chapter that takes a closer look at the implementation.

 

 

3. FRAMEWORK IMPLEMENTATION

 

3.1 Overview

 

I tried to stick as much as possible to the original POSA2 interceptor pattern definition. However I reorganized the code structure a little bit to make it more clear and extensible. My PHPInterceptor framework takes a simple blog post action as a case study to demonstrate how the framework works. I should note, however, that this code is specific to my case study. You should adapt the code to your own needs, because it is impossible to make a generic interceptor pattern that can be used for every single application.

 

The class diagram from my framework is more or less the same as the general POSA2 definition class diagram. However I adapted the definition a little bit to my needs and here is the directory structure that perfectly represents the internal structure of my framework.

 

phpinterceptor

  \-- ContextObjects

       \-- post.php

  \-- Dispatchers

       \-- post.php

       \-- dispatcher.php

  \-- Interceptors

       \-- post.php

  \-- Library

       \-- Event

            \-- Dispatcher.php

            \-- Notification.php

       \-- library.php

  \-- Plugins

       \-- config.xml

       \-- logging.php

       \-- mailer.php

       \-- preprocessor.php

  \-- blogentry.php

  \-- blogframework.php

 

Framework directory structure

 

The reason why I have all these directories is that you can group common functionality together. In this case you can group the ContextObjects, Dispatchers and Interceptors together. The concrete interceptors are in the plug-in directory together with the XML based configuration file. The Library directory contains helper functions and the Event_Dispatcher library. In the root level, we have our framework with one or more specific applications.

 

 


Class diagram PHPInterceptor framework

 


The above class diagram gives a good overview on the relations between the components in my framework. There are a few things that I want to add. First of all, next to Logging and PreProcessor, we also have a Mailer interceptor, but this was so trivial that I left it out. Then three things that I could not do with the UML tool: The $_plugins variable in the BlogFramework is of the type SimpleXML. The attachInterceptors method in the same class should not have just a string argument, but an array of strings. Last but not least, the Dispatcher class has a variable $_event_dispatcher. This should be of the type Event_Dispatcher

 

 

 

 

 

 

 

3.2 blogframework.php & blogentry.php

 

The blogframework.php and blogentry.php act as the Concrete Framework and Application from the POSA2 definition. The purpose of this distinction is to put common code and functionality in the blogframework.php and the application specific code in blogentry.php.

 

PHP does not know the concept of packages like Java. We have to explicitly include files with other code if we want to access that code. The framework takes care of loading all the files. On the one hand it loads specific files using the include_once function, but it also loads whole directories using the static Library::includeDir function from the frameworkÕs library.

 

include_once("Library/library.php");

include_once("Library/Event/Dispatcher.php");

Library::includeDir("Interceptors");

Library::includeDir("Plugins");

Library::includeDir("Dispatchers");

Library::includeDir("ContextObjects");

 

Loading the files in the framework

 

Two aspects are important here. First of all the loading of single files. In PHP you can load files in four different ways: include, include_once, require and require_once. The difference between include and require is that the former loads the files in a lazy fashion, i.e. only when it needs them. The latter loads the files eagerly, so immediately at the declaration of the require function. I opted for the lazy loading, because at initialization, my framework reads in already the XML configuration file, so that would add more loading time. The difference between the normal loading functions and the functions with the _once attached is that include_once and require_once load files only one time, no matter how many times it is loaded. This is very useful when you include several files that can include other files. The problem is that when youÕre not using the _once version, you can load the files twice, resulting in conflicts. PHP will sometimes complain that you try to define twice the same variable or creating twice the same object. The second aspect is the loading of whole directories with the Library::includeDir function. Instead of explicitly loading all the files separately, my function scans the whole directory for proper files. This adds of course much to the dynamic behavior of the framework.

 

The blogframework.php contains also a constructor that initializes the dispatcher and context objects and loads the configuration file.

 

function __construct() {

    // create message dispatcher

    $this->_bd = new PostDispatcher();

    // create message context

    $this->_bc = new PostContext();

    // read in the plugins config file

    $this->_plugins = simplexml_load_file('Plugins/config.xml');

}

 

Framework constructor that initializes objects and configuration file

 

An interesting change between PHP5 and PHP4 is the __construct() keyword. When you want to use a constructor in PHP4, you have to use a function with the same name as the class name. So in this case, the class name is BlogFramework, so my constructor had to be defined as: function BlogFramework().

 

The application BlogEntry extends the BlogFramework and can hereby use the functionality provided in the BlogFramework. In the application you take care of configuring the interceptor attaching mechanism and calling the methods from your application.

 

function main () {                

    $this->attachInterceptors(array("BlogEntry"));

    // put info in message context

    $this->_bc->setOwner("admin");

    $this->_bc->setMessage("<b>HELLO WORLD</b>");

    // preSend stuff

    $this->_bd->preSubmit($this->_bc);

    // send message

    $this->submitEntry($this->_bc);

    // postReceive stuff

    $this->_bd->postSubmit($this->_bc);

}     

 

Main execution code from the application

 

The $this->attachInterceptors(array("BlogEntry")) function allows you to load the interceptors for this application, while supplying some filters as argument. As earlier described, you can supply some filters in the XML configuration file. A filter can be very useful to limit the appliance of interceptors. Imagine that you have a mailer interceptor and you only want certain applications to access this. Then you can define in the configuration file the allowed classes. It can also work the other way around. You can specify a filter ÒmailerÓ and say in your application that you are only interested in mailer interceptors. Then you would be using something like: $this->attachInterceptors(array("mailer")). The reason why you can supply an array, is that you can specify more filters. You can say that you are interested in the logging and mailer interceptors instead of only the mailer interceptor. This can be very powerful to limit on the on hand the usage of the interceptors, but on the other hand we can define a whole library of interceptors and just say in the application what interceptors we want to use.

 

 

3.3 ContextObjects

 

An important way to open up your implementation for the interceptors is the usage of context objects. These are objects, containing data from the framework that can be passed to and can be manipulated by the interceptors. Basically you can compare it with a plain old Java object with getters and setters.

 

class PostContext {        

    private $_owner;

    private $_message;

    // omitted some code          

    function getMessage () {                    

        return $this->_message;

    }        

    function setMessage ($message) {            

        $this->_message = $message;

    }               

}

 

Context object containing information about the owner and content of a post.

 

 

3.4 Dispatchers

 

Our dispatcher configures and triggers the concrete interceptors (or plug-ins). We have to define a dispatcher for each type of interceptor. If we have a user centric interceptor, then we have to define a user dispatcher. If we have a post centric interceptor, then we have to define a dispatcher. Do not confuse with the concrete interceptors! You do not have to define a dispatcher for each concrete interceptor or plug-in!

 

In the PHPInterceptor framework, the dispatchers share some common functionality like the constructor and the register/remove functions. Therefore we put these three functions in a base class and the specific dispatcher extends from this base class and adds its own specific code to it.

 

class Dispatcher {         

   protected $_event_dispatcher

   function __construct() {

     $this->_event_dispatcher = Event_Dispatcher::getInstance();

   }         

   function register ($target_classname, $target_methodname, $hook) {       

     $this->_event_dispatcher->addObserver(array($target_classname,$target_methodname),$hook);

   }

   function remove ($target_classname, $target_methodname, $hook) {  

     $this->_event_dispatcher->removeObserver(array($target_classname,$target_methodname),$hook);    

   }

}

 

Dispatcher base class with common functionality

 

Our dispatcher is, as you can see, some kind of wrapper class around the Event_Dispatcher class from Bertrand Mansion. The constructor gets a singleton instance from the Event_Dispatcher::getInstance() method. The register and remove methods call the appropriate addObserver and removeObserver methods from the Event_Dispatcher package.

 

Each specific dispatcher extends this base class now and adds the hooks for the interceptors.

 

class PostDispatcher extends Dispatcher {       

    function preSubmit (PostContext $pc) {

        $this->_event_dispatcher->post($pc, 'onPreSubmit');

    }

    function postSubmit (PostContext $pc) {            

        $this->_event_dispatcher->post($pc, 'onPostSubmit');

    }        

}

 

Concrete PostDispatcher that extends from the Dispatcher base class

 

The preSubmit and postSubmit methods are called in the application. Every time these methods are called, we notify the observers who are interested in the onPreSubmit and onPostSubmit event. As you can see, we supply also the PostContext object with more information about the message. In PHP4 we have to use the ampersand (&) sign to say that we want a Òpass-by-referenceÓ. In PHP5, objects are passed around by default Òby referenceÓ. If you want to pass-by-value, then you have to make use of the object cloning mechanism of PHP5 to pass a copy of the object. Because this framework is fully targeted at PHP5 environments, we omit the & sign. The usage of & is still allowed, but is discouraged because this will be deprecated in the future.

 

 

3.5 Interceptors and concrete interceptors

 

The POSA2 interceptor pattern definition requires us to define an interceptor interface that the concrete interceptors have to implement. For our PostInterceptor, it looks like this:

 

Interface PostInterceptor {       

    static function onPreSubmit (Event_Notification $notification);

    static function onPostSubmit (Event_Notification $notification);

}

 

Interceptor interface as required by POSA2

Each plug-in has to implement these two methods. As you might have noticed, what we define is here what happens at what kind of events.

 

class PreProcessor implements PostInterceptor  {       

    static function onPreSubmit (Event_Notification $notification) {        

        $postContext = $notification->getNotificationObject();

        // convert dangerous characters like < to &lt;

        $postContext->setMessage( htmlentities( $postContext->getMessage() ) );

    } 

    static function onPostSubmit (Event_Notification $notification) {       

        $postContext = $notification->getNotificationObject();

    }        

}

 

PreProcessor plug-in

 

The above example is a preprocessor plug-in that transforms dangerous HTML characters. The lower then Ô<Õ sign will be transformed to Ô&lt;Õ. Instead of parsing the HTML code, the tags will be displayed Òas isÓ. The onPreSubmit method requires an Event_Notification object as argument and also forces this with the class-type hinting mechanism. Because objects are passed by-reference by default, we can manipulate the original content of the object. The get our PostContext object out of the Event_Notification object, we need to execute the getNotificationObject() on the Event_Notification object. Then we can execute the methods available in our context object (usually the getters and setters). Here we transform the message content with the PHP htmlentities() function and overwrite the original message with our transformed message.

 

Now that we have made a concrete interceptor or plug-in, we still need to configure it in our framework. As earlier described, this can be done with the confix.xml file located in the Plugins directory.

 

<plugin>

    <class>Logging</class>

    <method>onPreSubmit</method>

    <hook>onPreSubmit</hook>

    <interceptor>PostInterceptor</interceptor>

    <filters>

        <filter>BlogEntry</filter>

        <filter>Logging</filter>

    </filters>

</plugin>

 

Entry in our config.xml file

 

The configuration file is quite straightforward, but very powerful. First of all you have to define the location of the plug-in or concrete interceptor. You supply the class name and the method name. You need to associate the plug-in with an event also. This event that we are interested in, is the hook. In this case I opted to use the same name for the method and hook because this is the easiest way, but this does not need to be the same. The interceptor defines, as the name implies, to which interceptor you want to associate this concrete interceptor. In your application code, you extend from this interceptor then.

 

Last but not least we have the filters that we mentioned already earlier. These are very powerful and you can do almost anything you want with it. I choose here to provide filters that tell what application can use the filter and what kind of filter it is. The reason is that at plug-in load time I can for instance say that I want to register all the plug-ins allowed for this application, or that IÕm just interested in the logging plug-ins. You can define here as much filters as you want. 

 

 

 

3.6 How to get started with the PHPInterceptor framework?

 

Of course it makes sense that you first try to understand the POSA2 interceptor pattern. Once you reached that point, basically you just need to understand how to make plug-ins and configure them properly. The easiest way is to piggyback on the current code base. Once plug-ins are done deal, itÕs quite straightforward to make your own framework based on my code base and on this paper.

 

 

4. FUTURE WORK

 

I have now a fully working version of the framework available. I still need to add decent documentation using the phpDoc standards and supply unit tests. The current code base could be extended with some nice user examples. It would be interesting to see what kind of applications will be built on top of the framework. I am a little worried about the performance in a high-load web environment, but IÕm pretty sure that the PHPInterceptor framework would operate perfectly in a PHP+GTK desktop application environment. Because I believe that this code can be useful for other people, I will be released soon on sourceforge.net. (http://sourceforge.net/projects/phpinterceptor)

 

 

5. CONCLUSION

 

Applying the interceptor pattern in a framework is very interesting if you want to support plug-ins. With some adaptations, as in the PHPInterceptor, you can make it really dynamic and extensible. This project/paper shows that developing complex applications is possible with PHP5, although it requires a thorough knowledge of the language and object-oriented programming. I believe that this framework has a future in desktop-based PHP+GTK applications. If you are willing to sacrifice some of the dynamic behaviors of the framework, then it can be applied also in a high-load web application environment.

 

 

References

 

[1] Pattern Oriented Software Architecture, patterns for concurrent and networked objects, Volume 2. Douglas Schmidt, Michael Stal, Hans Rohnert, Frank Buschman. ISBN 0471606952. John Wiley & Sons. 2000. 101-125

[2] Eclipse AspectJ project. http://eclipse.org/aspectj/

[3] Aspect Oriented PHP: A PHP extension for aspect oriented programming. http://www.aophp.net/

[4] Changes in PHP5/Zend Engine II by php.net. http://www.zend.com/php5/articles/engine2-php5-changes.php

[5] Bertrand Mansion. PEAR Event_Dispatcher package. http://pear.php.net/manual/en/package.event.event-dispatcher.php