Community Documentation

Page handler include files

Last updated April 26, 2008. Created by Crell on May 23, 2007.
Edited by aclight, ax, dww. Log in to edit this page.

The menu system introduced in Drupal 6 also supports conditional loading of an extra include file for each menu entry. That allows module developers to separate off their page handler functions to separate files that are loaded only as-needed. In many modules the majority of the code is page handlers, but on any given page request only one page handler is called in all of Drupal. Separating those functions out to conditional files can save a great deal of overhead from parsing functions that will never be used.

Page handler includes

To tell Drupal that a page handler lives in a separate include file, use the "file" array key. For example:

<?php
  $items
['admin'] = array(
   
'title' => 'Administer',
   
'access arguments' => array('access administration pages'),
   
'page callback' => 'system_main_admin_page',
   
'weight' => 9,
   
'file' => 'system.admin.inc',
  );
?>

The above directive, taken from system_menu(), tells Drupal to include the file "system.admin.inc", located in the same directory as the system.module file, before calling system_main_admin_page(). system_main_admin_page() can then be placed in system.admin.inc, so it never has to be parsed unless it is needed.

Form page handlers

A great many page handlers are actually just calls to drupal_get_form(), with the ID of the form for that page as a callback argument. That is especially true for system configuration pages. drupal_get_form() is always available, of course, but the form callback function can then be placed in a separate include file. Remember to move the form definition function as well as its validate and submit handlers, since they will only be needed if the page for that form is loaded.

Page handlers provided by other modules

In some cases, you will want to use a page callback provided by a different module. In that case, you will need to also specify a "file path" key, like so (taken from node_menu()):

<?php
  $items
['admin/content'] = array(
   
'title' => 'Content management',
   
'description' => "Manage your site's content.",
   
'position' => 'left',
   
'weight' => -10,
   
'page callback' => 'system_admin_menu_block_page',
   
'access arguments' => array('administer site configuration'),
   
'file' => 'system.admin.inc',
   
'file path' => drupal_get_path('module', 'system'),
  );
?>

Drupal will include the file located at "file path"/"file", with the file path based on the Drupal base directory. If no file path is specified, the path of the module defining the callback is used. That is:

<?php
example_menu
() {
 
$items['example/path'] = array(
   
'page callback' => 'example_page',
   
'file' => 'example.pages.inc',
   
'file path' => drupal_get_path('module', 'example'),
  );
}
?>

The "file path" key in the above example is unnecessary, as that is what Drupal does by default.

Inheritance

If a menu item does not define a page callback, then it will inherit the callback from its parent. If it does so, it will also inherit the file directives from its parent. Since they define how Drupal should access the callback function, that should make intuitive sense.

Best practices

Module developers are free to split off page handlers for their modules however they choose. However, the following guidelines and standards are recommended:

  • Any module that has more than ~50 lines of code for page handler functions (including form handling functions if applicable) should split them off into a separate file. That reduces the overhead for PHP when loading modules, and therefore speeds up every single request on the site.
  • Page include files should be named in the form modulename.key.inc, where "modulename" is the name of the module and "key" is a one-word descriptive term for the types of page handlers it includes.
  • For most modules, splitting page handlers into two files -- example.admin.inc (for administrator-only pages) and example.pages.inc (for pages accessible by non-administrator users) -- is sufficient, and is the recommended practice. If a module has no non-administrator pages, it should just have a single example.admin.inc file. If a module has no administrator-only pages, it should just have a single example.pages.inc file.
  • Modules that have a large number of page handlers may choose to separate out page handlers even further. If so, each file should be grouped logically by function (for instance, admin pages related to theming, admin pages related to logging, other admin pages, and user-accessible pages) and clearly marked. Remember that splitting the module's page handlers up too far makes maintenance more difficult, and only one page handler include file is ever loaded regardless of how finely-grained the handler functions are separated.

Comments

emulating node/%node/edit

This took me several hours of digging around to figure out how to do this, but if you have a MENU_LOCAL_TASK (or any type would work I suppose) item that you wish to have act like node/%node/edit this is how you do it:

<?php
function everyblog_menu() {
   
$items['blog/%everyblog_blog_node/edit'] = array(
   
'title' => 'Edit',
   
'page callback' => 'node_page_edit',
   
'page arguments' => array(1),
   
'access callback' => 'node_access',
   
'access arguments' => array('update', 1),
   
'weight' => 1,
   
'file' => 'node.pages.inc',
   
'file path' => drupal_get_path('module', 'node'),       
   
'type' => MENU_LOCAL_TASK,
);

...

return
items;
}
?>

Now there are two key things to do here, first, as you can see I'm using a custom loader %everyblog_blog_node which means that the function everyblog_blog_node_load gets called with the parameter arg(1). everyblog_blog_node_load determines the nid to use and then returns a node object using load_node and the nid. Next that node object is passed to node_page_edit but that function isn't defined in your module! So what do we do? We use the 'file' key and point it to 'node.pages.inc', but that file isn't in the module either, so we use the nifty 'file path' key and point it to the node module using drupal_get_path('module', 'node'). Perhaps the body of the menu item looks familiar, perhaps not, but its copied from node_menu in the node module.

And there you go, you will now have tab saying 'Edit' when you are at the path blog/%everyblog_blog_node which when clicked on acts like the user clicked 'Edit' from a node page.

Hope this saves some other people some work,
NTroutman

Problem with adding tab to user profile page

I'm trying to add a tab to the user profile page. The hook_menu() documentation states that menu items of the type MENU_LOCAL_TASK is rendered as a tab by default. I implemented the following hook_menu() in my module

<?php

//implementation of hook_menu()
function modulename_menu()
{     
$items['user/%user/listing'] = array(
       
'title' => 'Module Name',
       
'page_callback' => '_display_content',
       
'access arguments' => array('access content'), //only users who have the permission 'access content' can access this menu resource
       
'type' => MENU_LOCAL_TASK,
    );   
}
?>

This hook displays the appropriate tab on the user profile page right next to the 'view' and 'edit' tabs. But when I click on the tab I get the following error -

Fatal error: require_once() [function.require]: Failed opening required 'sites/all/modules/modulename/user.pages.inc' (include_path='.;C:\xampp\php\pear\') in C:\xampp\htdocs\irp\includes\menu.inc on line 346

Why does this error show up? And why does the module require a user.pages.inc file? Also does this error have something to do with the missing menu item type MENU_DEFAULT_LOCAL_TASK ?

Please help..

'page_callback' should be

'page_callback' should be 'page callback'

thanks

thanks davepoon :)

What about if a file to be

What about if a file to be included is in a subdirectory? Included files are not always in the same directory as the module itself, but might be in another folder. Suppose I have a file that is in sites/all/modules/example/subdir/example.pages.inc? My guess would be this, correct me if it's wrong:

<?php
//file path usage example for file in the "subdir" subdirectory of "example" module
example_menu() {
 
$items['example/path'] = array(
   
'page callback' => 'example_page',
   
'file' => 'example.pages.inc',
   
'file path' => drupal_get_path('module', 'example') . '/subdir',
  );
}
?>

Develop for Drupal

Drupal’s online documentation is © 2000-2013 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License. Comments on documentation pages are used to improve content and then deleted.
nobody click here