In our recent post we tried to introduce you with the HTTP request life-cycle in Drupal. If you read the post, hopefully you have a better understanding of what stages a request goes through in Drupal. Today we will try to give a specific example of how this understanding can help you in your daily work with Drupal.

Drupal is a modular and extensible content-management system (CMS). Among other things, this also means that a typical complex web-site is built using large number of Drupal modules. Some of these modules even change each-other’s functionality through the use of Hooks.

The spirit of open-source is in “hacking” (by which we mean building your work on top of other people’s genius creations, not - damaging software systems) code. With a system so large and complex - how do you know which piece of code to mess with? When tweaking complex Drupal installations, more often than not, you find yourself wondering the same question:

Where is the code that renders the output for URI /xxx/yyy?

The answer to this question is non-trivial. Drupal allows any module to register callback functions for any URI or a portion of URI. For instance different functions can be handling “/rss/*” and “/rss/xml/*”. Callback handlers matching more specific URIs take precedence. Handlers are registered in menu hook functions within the module source code.

That’s all sweet and cute, but if you have 30 modules installed, how are you going to find the specific handler? An easy solution is to get Drupal’s help and have Drupal log a handler for each request. To achieve this, you need to edit menu_execute_active_handler() function in includes/menu.inc. In the beginning of that function, locate the lines containing the following code:

  // Determine the menu item containing the callback.
  $path = $_GET['q'];

  while ($path && !isset($menu['callbacks'][$path])) {
    $path = substr($path, 0, strrpos($path, '/'));
  }

and insert this code snippet, directly after it:

 if ($path !== '' || isset($menu['callbacks'][$path])) {
    $callback = $menu['callbacks'][$path];
    if ( is_array($callback)  ) {
      $cb = $callback['callback'];
      error_log ("===>  URI Path: \"$path\" is handled by: $cb()");    
    }
  }

Some final comments:

  • Make sure you have PHP Logging properly configured and you know where log files are for a specific virtual host.
  • Please pay attention to the output. Drupal uses URI for both resource and arguments, if Clean URLs are enabled and you may get a response that indicates that request handler matches only part of the URL. For instance, if you are requesting a URI /supertag/programming, the actual resource may be “supertag” and the token “programming” - just an argument. In such case the code above will say something like:\

    ===>  URI Path: "supertag" is handled by: supertag_somefunction()
    
  • If you the output says that request is handled by views_view_page(), it means you are requesting a Drupal Views-driven page and you may want to look for more details in the Views admin user-interface, rather than in the code.