
This post is an entry in our 20 APIs in 20 Days series. Learn more about how best practices lead to sustainable development at www.trellon.com.
Ask someone what a 'hook' is, and their answer can tell you quite a lot about the person. The word gets used to describe things related to pirates, music, characters in campfire stories, aeronautics, etc. The word also has a specific meaning in programming, however, and serves as an important structural concept in most modern programming languages and frameworks.
Most Drupal developers are well acquainted with Drupal's hook system. Hooks are triggers that operate when certain events occur in the page generation process. They are capable of modifying data while it is being processed and carrying out other actions in response to certain conditions. Some of the more common hooks operate when a node loads, when a form is generated, or when someone saves user information. This part of the 20 APIs in 20 Days series provides an overview of the nature of hooks and how they operate as the foundation of most custom modules.
For those who are familiar with object oriented programming, hooks are conceptually related to the idea of a method. They present standard ways of interacting with data structures that operate independently of specific callbacks. Unlike traditional OO constructs, hooks are not tied to specific objects, they are tied to procedural points in the page generation process. Also, many hooks are state aware, and do different things depending on what exactly is happening at the time the hook is called.
For those with the good sense to avoid the religious war that is OO programming, here is what you really need to know about what Drupal's Hooks API does:
The important thing to understand about this model is it allows developers to modify the operations of Drupal in many small ways instead of having all that code written up in a single place. Having the Hooks API in place means someone can add a module to Drupal to expand the functionality of the system without needing to rewrite any code. It is what we mean when we say that Drupal is modular.
Let's look at this another way. There is a hook in Drupal core called hook_nodeapi. It fires each and every time a node loads in a Drupal site (among other operations, which we will delve into). Loading the node is what we would call an event, it causes hook_nodeapi to look through all the installed modules where hook_nodeapi is registered. Like a series of dominos, Drupal is going to execute the code contained in each hook_nodeapi function in each module, and the result will be a fully formed node that is ready to present to users.
The location module, for instance, uses hook_nodeapi to add address information to nodes when they are loaded. It also saves address information when the node is saved. This is what we mean when we say that many hooks are state aware, they know if you are saving something, or loading something, or deleting something, etc.
Despite their importance, there is sometimes a terribly small amount of documentation about many hooks in Drupal, and this can make learning about them a challenge. For most people, the path to understanding hooks is by reading through people's code and implementing them yourself within your own projects. Also, there are very few people who really understand what all the hooks do in Drupal core or their real implications. At Trellon, when we hire a new developer, we ask them a series of increasingly difficult questions about hooks. In six years of interviews, no one has ever provided a satisfactory answer for what hook_comment does - despite the fact it seems so obvious and has been around for a long time. This speaks to the previous point about how you learn about hooks, people don't find many reasons to implement hook_comment and thus don't always know that much about it.
Hooks have a common naming structure that can be easily recognized once you know how to look for it.
Developers use a PHP function in their code with a special name to register a hook. The special name is constructed this way:
For example, if someone had a module called foo, and they wanted to implement hook_bar, they would put a function in the foo module called foo_bar. It's really not that hard to implement a hook.
Passing in the appropriate data, however, is another matter. Each hook is a PHP function and has it's own arguments for handling data. Many hooks also pass variables in by reference, in order to modify data before it is passed back to Drupal for further processing.
A common mistake custom module developers make is to put in the wrong set of arguments. A handy trick is to keep api.drupal.org open in another window and copy out the examples from the documentation for the hook. This can save a lot of time for people who are just getting started.
There are some hooks that are used more often than others, and are key to creating custom modules. Reading through the code inside these hooks in any custom module will tell you a lot about what the module does and how it does it. These common hooks include:
There is a full list of hooks in Drupal core available at http://api.drupal.org/api/group/hooks. For advanced developers, understanding the implications of certain hooks does become important. Hook_db_rewrite_sql is one that gets used every once in a while, and is generally implemented to provide access restrictions to content on a site. It's one of those ones you want to watch out for, because some modules that implement it can turn out to be incompatible. For instance, organic groups and domain access both use hook_db_rewrite_sql to control access to content, and there are situations where they are not going to work together. This is what is meant by 'implications,' having 2 modules installed that create access restrictions can really gum up the works!
Implementing a hook in your code is really as easy as giving it the right name, passing in the right arguments, and running whatever code you need within the hook to make it work. There are various modules that will build out sets of hooks for you to get you started (we don't want to go into them here, but Google around and you will quickly find some).
Here are some examples of hooks in action:
Example 1: Alter links on a node before they are rendered:
<?php
/**
* Implementation of hook_link_alter().
*/
function mymodulename_link_alter(&$links, $node) {
global $user;
// Modify the forward link for logged user's nodes
if ($user->uid == $node->uid && isset($links['forward_links'])) {
$links['forward_links']['href'] = 'resume/'. $node->nid .'/send';
$links['forward_links']['query'] = '';
}
}
?>Example 2: Contact module's page menu callback implementation:
<?php
/**
* Implementation of hook_menu().
*/
function contact_menu() {
$items['contact'] = array(
'title' => 'Contact',
'page callback' => 'contact_site_page',
'access arguments' => array('access site-wide contact form'),
'type' => MENU_SUGGESTED_ITEM,
'file' => 'contact.pages.inc',
);
return $items;
}
?>You can allow other developers to extend your module's functionality by exposing your own hooks. The simplest way to expose your "hook" is to call the function module_invoke_all('myhookname') (http://api.drupal.org/api/function/module_invoke_all/6) in your module. Then a fellow developer can implement this hook creating a function mymodulename_myhookname(). Let's demonstrate this on the next example.
File: mymodulename.module
<?php
/**
* Sample module function with my own hook.
*/
function mymodulename_foo() {
// Example variable $layouts
$layouts = array();
// Let's call the hook 'mymodulename_layouts'
// The hook will hook other modules on our function
$layouts = module_invoke_all('mymodulename_layouts');
// The returned value is an array of return values of the hook implementations
foreach($layouts as $layout){
// Do stuff with the returned variables
print $layout;
}
}
?>As you probably noticed, we called our hook mymodulename_layouts - it includes the 'mymodulename' string. This is preventing us from interference with other modules - if we just called the hook 'layouts', there could be another module which is exposing hook with the same name.
To test the hook we just created, we will create another module - let's call the module 'test' and put the following function into it:
File: test.module
<?php
/**
* Implementation of hook_mymodulename_layouts().
*/
function test_mymodulename_layouts() {
$layout = 'testing';
return $layout;
}
?>The function module_invoke_all() returns array of return values of the hook implementations. If modules return arrays from their implementations, those are merged into one array. You can also include arguments in your hook, or you can also pass them as a reference, as an example you can take a look at hook_nodeapi, http://api.drupal.org/api/function/hook_nodeapi.
When creating complex modules, these functions can be useful to you:
If this part of the 20 APIs in 20 days was interesting for you, can visit the hooks API documentation for further information at http://api.drupal.org/api/group/hooks. I hope you enjoy the rest of the series!
10 Comments
Hell ya! Nice article!
Hell ya! Nice article!
Thanks Shawngo!
Thanks Shawngo!
Kudos to you for pointing out
Kudos to you for pointing out the down side that one problem with hooks is that multiple modules acting on a hook can create incompatible results. I wish there was some way to easily know which modules affect which hooks so that possible collisions could be known about easily ahead of time.
There is a simple way of
There is a simple way of knowing which modules returned which results from a hook. Something like this does the trick:
foreach (module_implements('myhook') as $module) {$results[$module] = call_user_func_array($module .'_myhook', array($arg1, $arg2));
}
Try 'drush hook ' e.g. "drush
Try 'drush hook ' e.g. "drush hook menu"
BTW the hook command requires
BTW the hook command requires you to install the 'devel' module.
Multiple node access
Multiple node access implementations may be one of the only cases where different implementations of a hook can be incompatible. Or _seem_ incompatible might be a better way to say it. To get multiple node access systems working together you would use the Module Grants module:
http://drupal.org/project/module_grants
Wooohooo! Good job Valdo ;)
Wooohooo! Good job Valdo ;)
i need hooks for data
i need hooks for data module.
i want to call the delete function from data module into my custom module.how could i call it.please help me in this regard.
thanks in advance,
please help me
Excellent tutorial
Excellent tutorial
Post new comment