home forums resources search newsjoinmembers: 6957
Sections PHP Flash Java Ruby Windows Linux
Carl's picture

Carl | Tue, 2008-10-07 19:02  tags: , , , ,

A hook system in web programming is a way of chaining together functions or methods located in different files and then executing them in succession at some point. This is a very useful and robust way of injecting functionality to a web application when done right. But if it is implemented haphazardly then it can cause bugs and chaos.

For comparison purposes I am going to be using Simple Hooks Plugin Class written by Nemanja Avramovic. This is an excellent implementation of a hook system. Though there are some things that I dislike about it they can be considered trivial. If you are going to create your own system as I plan on doing you would do well to use SHP as a model.

Okay, now that we have that out the way let's dig into Drupal's way of doing things and explore the weaknesses involved in how the hook system is implemented. Showing at the same time how SHP covers most of these shortcomings using OOP and some really slick programming logic.

Though there are many functions in Drupal I am going to concentrate on the ones that drive the hook system , module_implements(), module_invoke(), module_invoke_all() and module_hook(). These functions are the ones that drive the functions that perform specific tasks within the system.

To declare a function as a hook Drupal you need only use the name of the module file and the suffix separated by the underscore character.

( module name ) _ ( suffix ) () {

    // some code here.

}

The blog module file would cause its menu to be registered by

<?php

blog_menu
(){
    
// some code here.
}
?>

Though this is simple enough and may seem harmless implementing registration of a function in this manner carries with many shortcomings and frustrations which only come to light when collaborating on a project or mixing in house code with third party contributions. Doing things this way causes semantics and syntax to become the enemy. Designating a name suffix out of the core means that forgetting to use it or using it without knowing the system will cause an untestable bug in your code. Forgetting to use the proper system prefix or using the wrong prefix will bring on the same problems. Since coding standards are different you may have to rename all your functions if you have some killer code that you want to be part of the Zend Framework. Zends coding standards do not allow for underscores in function and method names.

Registering and Unregistering

Looking at the previous example and text you can see how easy it is to register a hook from the core installation. But what about doing it from a module/plugin? This is when module_invoke() and module_invoke_all() come into play.

<?php

function module_invoke_all() {
  
$args func_get_args();
  
$hook $args[0];
  unset(
$args[0]);
  
$return = array();
  foreach (
module_implements($hook) as $module) {
    
$function $module .'_'$hook;
    
$result call_user_func_array($function$args);
    if (isset(
$result) && is_array($result)) {
      
$return array_merge_recursive($return$result);
    }
    else if (isset(
$result)) {
      
$return[] = $result;
    }
  }
  return 
$return;
}
?>

The problems with these functions are that when they are used there is no way of telling if the functions being called are really hooks or just happen to meet the naming conventions for being a hook. In other words the hooks are not being registered they are merely being placed in a list by the system. This list although not random can be anything based on the developer input given to the function. An example would be if a developer created a series modules with the names "shopping.module" ,"shopping_amazon.module" , "shopping_ebay.module" , "shopping_offline.module". Now there is a hook called (module name)_basket that should be called in each of the modules so that a list of items for a cart can be built. During testing there may be no problem but after distribution of the modules the developer keeps getting emails that the module is not compatible with the e-commerce module. This is because the e-commerce module contains several functions with, yes you got it! (module name)_basket. As the number contributions grow and the size of module projects grow the potential for collisions of this sort grows. Under the present coding standards there is no way to resolve conflicts like this without some heavy negotiations and politics between the project groups.

Voodoo?

There are of course many other problems directly associated with the Drupal hook system. Many of which has lead to developers referring to the strange run-time events and bugs as "Drupal Voodoo". This is one of the main reasons that I find my recent migration away from Drupal to be most pleasing. If I wanted voodoo happenings in my applications then they would not be hard to find in running VB6 COM objects on IIS. I don't want them in my web applications. That being said the problems with hooks are not in the language but in how they are used.

How Simple Hooks Plugin class works

You can download the SHP or take a look at it in the code browser. I will only show snippets of it here to accent some points of discussion. the first thing to know about the SHP is that there are restrictions implemented. These do not take away from the flexibility and are there to make it easier for the developer to find things and to reduce the learning curve to a flat line.

Containers not text

The SHP system works on containers and those containers are initiated with the following code.

<?php

//set hook to which plugin developers can assign functions
$SHP->developer_set_hook('test');
//set multiple hooks to which plugin developers can assign functions
$SHP->developer_set_hooks(array('test1','test2''with_args'));

?>

Using hooks as containers for functions or methods makes a lot of sense. The quantity, names and control of containers is set by the core application and not the plug-ins or modules. But at the same time the centralization of the system make it easy for a developer to find the right code and make quick adjustments as needed. This definably helps with the planning of an application as there is less guess work involved.

One of the things you'll have to do is to ignore the plug-in parts of the SHP application. Since I am talking about the hook system it will remove any confusion. Concentrating on the parts of the following code that adds in a hook makes things easier to discuss.

<?php

//this plug-in will never be loaded because it has no valid file name (*.plugin.php)
$plugin_id basename(__FILE__);
$data['name'] = "Third plugin";
$data['author'] = "Nemanja Avramovic";
$data['url'] = "http://www.avramovic.info/";
function 
plg3() {
    echo 
'echoed from third plugin<br />';
}
function 
njeh() {
    echo 
"<p>from plugin 3... below!?</p>";
}
add_hook('test','plg3');
add_hook('test1','njeh');
echo 
"Plugin 3 LOADED!<br />";
?>

Looking at the plug-in you can see that adding functions to a hook is simple and easy and not dependent on any type of naming conventions. Also a single function can be used in multiple hook instances making for more code reuse and reducing the necessity for copying a function into multiple plug-in files. Notice that the SHP system is written using OOP style code but makes use in procedural coded modules trivial. So there is no reason to assume because modules are written in a procedural manner that the entire core system must be done this way.

So there you have it just as promised (stop emailing me about it now) a comparison of the hook system with a little OOP from Simple Hooks Plugin and how the Drupal system can be improved upon. I won't be using the SHP in FireOrb as I have designed my own plugin system but using the hooks system design is hard to resist. If you know of a hook system nicely done in PHP don't be shy drop me a comment.


Happy Publishing!

Carl's picture
Ten years of experience in web development. Carl is looking for in employment as a Senior PHP/mySQL Developer in Stockholm.
Carl McDade - Systems Developer
Thoughtbox - So what did you think?



a Visitor posted on: Wed, 2008-10-08 13:55.

This is an interesting idea. While the OOP code you point to has some issues (like the use of a global variable rather than using the singleton pattern) it is a novel idea.

I tried to look for a patch or issue to drupal suggesting something like this and I couldn't find one. Did I miss it? If not, why didn't you suggest it?

There is one definite weakness in idea. Developer usability. This is more complicated to search and much more difficult for novice developers to work with. While it's not a big deal to me the post isn't very well rounded with all aspects to this topic.

Carl posted on: Fri, 2008-10-10 06:24.

Developer usability should not be an issue. The use of the present Drupal code is not intuitive or easy to look up in the documentation. There is a totally lack of coding conventions in Drupal which lets everyone put eanything anywhere. Comortable for the originators of the code but a nightmare for those using it or trying to collaborate. Drupal 6 and 7 code is looking very much like the old phpbb code. Lots of things without comment and constant repetition of the same code within functions. This one of the reasons that they don't want people touching the core. You never know what is going to happen when you do.

In comparison this code is easy reading.

 
CMS Comparison Matrix
 
Web Developers Content Management Systems Silverlight Wordpress Web Developers Wordpress Adobe Flex

Newsletter

Get updates on Hiveminds services, articles and downloads by signing up for the newsletter.

Editor's choice

Some of the better articles, stories and tutorials found at Hiveminds.

Find more

Find more of Hiveminds articles, stories, tutorials and user comments by searching.




Picked links

Hand picked websites and articles from around the web that provide quality reading.