Menu system

You are here

  1. 7 includes/ menu
  2. 4.6 includes/ menu
  3. 4.7 includes/ menu
  4. 5 includes/ menu
  5. 6 includes/ menu
  6. 8 core/includes/ menu

Define the navigation menus, and route page requests to code based on URLs.

The Drupal menu system drives both the navigation system from a user perspective and the callback system that Drupal uses to respond to URLs passed from the browser. For this reason, a good understanding of the menu system is fundamental to the creation of complex modules. As a note, this is related to, but separate from menu.module, which allows menus (which in this context are hierarchical lists of links) to be customized from the Drupal administrative interface.

Drupal's menu system follows a simple hierarchy defined by paths. Implementations of hook_menu() define menu items and assign them to paths (which should be unique). The menu system aggregates these items and determines the menu hierarchy from the paths. For example, if the paths defined were a, a/b, e, a/b/c/d, f/g, and a/b/h, the menu system would form the structure:

  • a

    • a/b

      • a/b/c/d
      • a/b/h
  • e
  • f/g

Note that the number of elements in the path does not necessarily determine the depth of the menu item in the tree.

When responding to a page request, the menu system looks to see if the path requested by the browser is registered as a menu item with a callback. If not, the system searches up the menu tree for the most complete match with a callback it can find. If the path a/b/i is requested in the tree above, the callback for a/b would be used.

The found callback function is called with any arguments specified in the "page arguments" attribute of its menu item. The attribute must be an array. After these arguments, any remaining components of the path are appended as further arguments. In this way, the callback for a/b above could respond to a request for a/b/i differently than a request for a/b/j.

For an illustration of this process, see page_example.module.

Access to the callback functions is also protected by the menu system. The "access callback" with an optional "access arguments" of each menu item is called before the page callback proceeds. If this returns TRUE, then access is granted; if FALSE, then access is denied. Default local task menu items (see next paragraph) may omit this attribute to use the value provided by the parent item.

In the default Drupal interface, you will notice many links rendered as tabs. These are known in the menu system as "local tasks", and they are rendered as tabs by default, though other presentations are possible. Local tasks function just as other menu items in most respects. It is convention that the names of these tasks should be short verbs if possible. In addition, a "default" local task should be provided for each set. When visiting a local task's parent menu item, the default local task will be rendered as if it is selected; this provides for a normal tab user experience. This default task is special in that it links not to its provided path, but to its parent item's path instead. The default task's path is only used to place it appropriately in the menu hierarchy.

Everything described so far is stored in the menu_router table. The menu_links table holds the visible menu links. By default these are derived from the same hook_menu definitions, however you are free to add more with menu_link_save().


Namesort descending Location Description
drupal_help_arg includes/ Generates elements for the $arg array in the help hook.
menu_build_tree includes/ Builds a menu tree, translates links, and checks access.
menu_cache_clear includes/ Clears the cached cached data for a single named menu.
menu_cache_clear_all includes/ Clears all cached menu data.
menu_contextual_links includes/ Retrieves contextual links for a path based on registered local tasks.
menu_delete_links includes/ Deletes all links for a menu.
menu_execute_active_handler includes/ Execute the page callback associated with the current path.
menu_get_active_breadcrumb includes/ Gets the breadcrumb for the current page, as determined by the active trail.
menu_get_active_help includes/ Returns the help associated with the active menu item.
menu_get_active_menu_names includes/ Gets the active menu for the current page.
menu_get_active_title includes/ Gets the title of the current page, as determined by the active trail.
menu_get_active_trail includes/ Gets the active trail (path to root menu root) of the current page.
menu_get_ancestors includes/ Returns the ancestors (and relevant placeholders) for any given path.
menu_get_custom_theme includes/ Gets the custom theme for the current page, if there is one.
menu_get_item includes/ Gets a router item.
menu_get_names includes/ Build a list of named menus.
menu_get_object includes/ Gets a loaded object from a router item.
menu_get_router includes/ Gets the menu router.
menu_links_clone includes/ Clones an array of menu links.
menu_link_children_relative_depth includes/ Finds the depth of an item's children relative to its depth.
menu_link_delete includes/ Delete one or several menu links.
menu_link_get_preferred includes/ Looks up the preferred menu link for a given system path.
menu_link_load includes/ Gets a translated, access-checked menu link that is ready for rendering.
menu_link_maintain includes/ Inserts, updates, or deletes an uncustomized menu link related to a module.
menu_link_save includes/ Saves a menu link.
menu_list_system_menus includes/ Returns an array containing the names of system-defined (default) menus.
menu_load_links includes/ Returns an array containing all links for a menu.
menu_local_actions includes/ Returns the rendered local actions at the current level.
menu_local_tabs includes/ Returns a renderable element for the primary and secondary tabs.
menu_local_tasks includes/ Collects the local tasks (tabs), action links, and the root path.
menu_main_menu includes/ Returns an array of links to be rendered as the Main menu.
menu_navigation_links includes/ Returns an array of links for a navigation menu.
menu_primary_local_tasks includes/ Returns the rendered local tasks at the top level.
menu_rebuild includes/ Populates the database tables used by various menu functions.
menu_reset_static_cache includes/ Resets the menu system static cache.
menu_router_build includes/ Collects and alters the menu definitions.
menu_secondary_local_tasks includes/ Returns the rendered local tasks at the second level.
menu_secondary_menu includes/ Returns an array of links to be rendered as the Secondary links.
menu_set_active_item includes/ Sets the active path, which determines which page is loaded.
menu_set_active_menu_names includes/ Sets (or gets) the active menu for the current page.
menu_set_active_trail includes/ Sets the active trail (path to the menu tree root) of the current page.
menu_set_custom_theme includes/ Sets a custom theme for the current page, if there is one.
menu_set_item includes/ Replaces the statically cached item for a given path.
menu_tab_root_path includes/ Returns the router path, or the path for a default local task's parent.
menu_tail_load includes/ Loads the path as one string relative to the current index.
menu_tail_to_arg includes/ Returns a string containing the path relative to the current index.
menu_tree includes/ Renders a menu tree based on the current path.
menu_tree_all_data includes/ Gets the data structure representing a named menu tree.
menu_tree_check_access includes/ Checks access and performs dynamic operations for each link in the tree.
menu_tree_collect_node_links includes/ Collects node links from a given menu tree recursively.
menu_tree_data includes/ Sorts and returns the built data representing a menu tree.
menu_tree_get_path includes/ Gets the path for determining the active trail of the specified menu tree.
menu_tree_output includes/ Returns an output structure for rendering a menu tree.
menu_tree_page_data includes/ Gets the data structure for a named menu tree, based on the current page.
menu_tree_set_path includes/ Sets the path for determining the active trail of the specified menu tree.
menu_unserialize includes/ Unserializes menu data, using a map to replace path elements.
template_preprocess_menu_tree includes/ Implements template_preprocess_HOOK() for theme_menu_tree().
theme_menu_link includes/ Returns HTML for a menu link and submenu.
theme_menu_local_action includes/ Returns HTML for a single local action link.
theme_menu_local_task includes/ Returns HTML for a single local task link.
theme_menu_local_tasks includes/ Returns HTML for primary and secondary local tasks.
theme_menu_tree includes/ Returns HTML for a wrapper for a menu sub-tree.
_menu_build_tree includes/ Builds a menu tree.
_menu_check_access includes/ Checks access to a menu item using the access callback.
_menu_clear_page_cache includes/ Clears the page and block caches at most twice per page load.
_menu_delete_item includes/ Deletes a single menu link.
_menu_find_router_path includes/ Finds the router path which will serve this path.
_menu_item_localize includes/ Localizes the router item title using t() or another callback.
_menu_link_build includes/ Builds a link from a router item.
_menu_link_find_parent includes/ Finds a possible parent for a given menu link.
_menu_link_map_translate includes/ Translates the path elements in the map using any to_arg helper function.
_menu_link_move_children includes/ Updates the children of a menu link that is being moved.
_menu_link_parents_set includes/ Sets the p1 through p9 values for a menu link being saved.
_menu_link_translate includes/ Provides menu link access control, translation, and argument handling.
_menu_load_objects includes/ Loads objects into the map as defined in the $item['load_functions'].
_menu_navigation_links_rebuild includes/ Builds menu links for the items in the menu router.
_menu_router_build includes/ Builds the router table based on the data from hook_menu().
_menu_router_cache includes/ Stores the menu router if we have it in memory.
_menu_router_save includes/ Saves data from menu_router_build() to the router table.
_menu_set_expanded_menus includes/ Updates a list of menus with expanded items.
_menu_site_is_offline includes/ Checks whether the site is in maintenance mode.
_menu_translate includes/ Handles dynamic path translation and menu access control.
_menu_tree_check_access includes/ Sorts the menu tree and recursively checks access for each item.
_menu_tree_data includes/ Builds the data representing a menu tree.
_menu_update_parental_status includes/ Checks and updates the 'has_children' status for the parent of a link.


Namesort descending Location Description
MENU_ACCESS_DENIED includes/ Internal menu status code -- Menu item access is denied.
MENU_CALLBACK includes/ Menu type -- A hidden, internal callback, typically used for API calls.
MENU_CONTEXT_INLINE includes/ Internal menu flag: Local task should be displayed inline.
MENU_CONTEXT_NONE includes/ Internal menu flag: Invisible local task.
MENU_CONTEXT_PAGE includes/ Internal menu flag: Local task should be displayed in page context.
MENU_CREATED_BY_ADMIN includes/ Internal menu flag -- menu item was created by administrator.
MENU_DEFAULT_LOCAL_TASK includes/ Menu type -- The "default" local task, which is initially active.
MENU_FOUND includes/ Internal menu status code -- Menu item was found.
MENU_IS_LOCAL_ACTION includes/ Internal menu flag -- menu item is a local action.
MENU_IS_LOCAL_TASK includes/ Internal menu flag -- menu item is a local task.
MENU_IS_ROOT includes/ Internal menu flag -- menu item is the root of the menu tree.
MENU_LINKS_TO_PARENT includes/ Internal menu flag -- menu item links back to its parent.
MENU_LOCAL_ACTION includes/ Menu type -- An action specific to the parent, usually rendered as a link.
MENU_LOCAL_TASK includes/ Menu type -- A task specific to the parent item, usually rendered as a tab.
MENU_MAX_DEPTH includes/ The maximum depth of a menu links tree - matches the number of p columns.
MENU_MAX_PARTS includes/ The maximum number of path elements for a menu callback
MENU_MODIFIED_BY_ADMIN includes/ Internal menu flag -- menu item can be modified by administrator.
MENU_NORMAL_ITEM includes/ Menu type -- A "normal" menu item that's shown in menu and breadcrumbs.
MENU_NOT_FOUND includes/ Internal menu status code -- Menu item was not found.
MENU_PREFERRED_LINK includes/ Reserved key to identify the most specific menu link for a given path.
MENU_SITE_OFFLINE includes/ Internal menu status code -- Menu item inaccessible because site is offline.
MENU_SITE_ONLINE includes/ Internal menu status code -- Everything is working fine.
MENU_SUGGESTED_ITEM includes/ Menu type -- A normal menu item, hidden until enabled by an administrator.
MENU_VISIBLE_IN_BREADCRUMB includes/ Internal menu flag -- menu item is visible in the breadcrumb.
MENU_VISIBLE_IN_TREE includes/ Internal menu flag -- menu item is visible in the menu tree.


includes/, line 8
API for the Drupal menu system.


After reviewing this API documentation, I went back into my menu to make some major adjustments. I took a spec approach and reoriented the menu to follow a tree structure using the 'parent' attribute for each item level. That not only simplified the menu orientation from a coding perspective, but also seemed to adjust correctly on the navigation.

Here is an example of using the 'parent' attribute:

= array();
//parent and default
$items['root'] = array(
'title' => t('My Account'),
'page callback' => 'drupal_get_form',
'access callback' => TRUE,
'expanded' => TRUE,
$items['root'] = array(
'title' => t('My Account'),
'description' => 'Account',
'page callback' => 'drupal_get_form',
'page arguments' => array('prj_account_edit'),
'access callback' => TRUE,
'file' => '',
'menu_name' => 'navigation',
$items['root/account'] = array(
'title' => t('My Account'),
'page arguments' => array('prj_account_edit'),
'page callback' => 'drupal_get_form',
'file' => '',
'access callback' => TRUE,
'parent' => 'root',
'weight' => -4,

There is a lot of code here. The emphasis is on the 'parent' attribute. Notice how it is pointing to the 'root' entry. This gives the menu system a clear indication which parent this menu entry is pointing to. It also gives the developer a way to keep track of which menu entries are pointing to where without having to syntactically keep track using some proprietary declaration.

Now, lets just say we add more entries then we either take out 'root' altogether or we rename root to something more user friendly. Then add other menu entries and point them around between parents.

Refresh your cache and see what happens. What occurs is what most people get frustrated about when they complain that the menu system doesn't seem to work for them. Not much changes or the menu looks confused.

This is because refreshing the cache doesn't actually refresh the menu entirely. The cache only refreshes the cached version of the menu and loads what is populated in the menu entries. But when the menu entries change name or orientation, the previous menu entry doesn't know to delete based on the new menu entry. A new menu entry is created because essentially, it is just reading the menu hook of menu entries and it doesn't know that it is being replaced, etc.

So, how do we get passed this so that we can properly display our menu adjustments?

For now, here is a database level workaround that you should only perform in your development environment:

FROM menu_link
WHERE router_path LIKE

After executing this database entry, you will see all of your links that you established in your menu system.

From there, take a look at the MENU_LINK.plid field. The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.

You'll notice that the parent link ID (plid) of your menu entry may not be properly pointing to the 'mlid' key of the parent menu entry. It might be pointing to the old parent menu entry or it just might point to 0.

To make the adjustment, update the parent link ID (plid) to point to the 'mlid' identifier of the parent that you are pointing to from the menu hook entry. Refresh your cache and you'll notice everything else adjusts accordingly.

There is not a workaround for production for this type of menu hook adjustment. Unless you have tried this in development and regression tested the heck out of your whole site, you probably wouldn't want to approach this in production. Well, you can if you want, but I'll still be working on the development environment combing through.


I'm confused by the following code:

  $items['root'] = array(
    'title' => t('My Account'),
    'page callback' => 'drupal_get_form',
    'access callback' => TRUE,
    'expanded' => TRUE,
  $items['root'] = array(
    'title' => t('My Account'),
    'description' => 'Account',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('prj_account_edit'),
    'access callback' => TRUE,
    'type' => MENU_NORMAL_ITEM,
    'file' => '',
    'menu_name' => 'navigation',

Is the first "$items['root']" declaration completely redundant, or is there some reason it is being created twice in a row?