Page handler include files
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.

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_loadgets called with the parameter arg(1).everyblog_blog_node_loaddetermines the nid to use and then returns a node object usingload_nodeand the nid. Next that node object is passed tonode_page_editbut 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 usingdrupal_get_path('module', 'node'). Perhaps the body of the menu item looks familiar, perhaps not, but its copied fromnode_menuin 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