1 menu.inc menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail = FALSE, $expand_all = FALSE)

Gets the data structure for a named menu tree, based on the current page.

The tree order is maintained by storing each parent in an individual field, see http://drupal.org/node/141866 for more.


$menu_name: The named menu links to return.

$max_depth: (optional) The maximum depth of links to retrieve.

bool $only_active_trail: (optional) Whether to only return the links in the active trail (TRUE) instead of all links on every level of the menu link tree (FALSE). Defaults to FALSE. Internally used for breadcrumbs only.

bool $expand_all: (optional) Since 1.5.0. Expand the entire menu tree. If set to TRUE, the value of $only_active_trail is disregarded.

Return value

An array of menu links, in the order they should be rendered. The array: is a list of associative arrays -- these have two keys, 'link' and 'below'. 'link' is a menu item, ready for rendering as a link. 'below' represents the submenu below the link if there is one, and it is a subtree that has the same structure described for the top-level array.

Related topics


core/includes/menu.inc, line 1333
API for the Backdrop menu system.


function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail = FALSE, $expand_all = FALSE) {
  $tree = &backdrop_static(__FUNCTION__, array());

  // Check if the active trail has been overridden for this menu tree.
  $active_path = menu_tree_get_path($menu_name);
  // Load the menu item corresponding to the current page.
  if ($item = menu_get_item($active_path)) {
    if (isset($max_depth)) {
      $max_depth = min($max_depth, MENU_MAX_DEPTH);
    // Generate a cache ID (cid) specific for this page.
    $cid = 'links:' . $menu_name . ':page:' . $item['href'] . ':' . $GLOBALS['language']->langcode . ':' . (int) $item['access'] . ':' . (int) $max_depth;

    // Adjust the cache ID if building a fully expanded tree or active trail.
    if ($expand_all) {
      $only_active_trail = FALSE;
      $cid .= ':all';

    // The active trail can be derived from an already-cached menu, so if we
    // already have one generated, there's no need to make a separate cache
    // just for the active trail. See menu_set_active_trail().
    if ($only_active_trail) {
      // Use the tree based on the normally expanded tree.
      if (isset($tree[$cid])) {
        // Use the current CID.
        //$cid = $cid;
      // Or use the existing fully expanded tree.
      elseif (isset($tree[$cid . ':all'])) {
        $cid .= ':all';
      // Or if neither exists, create a new cache entry just for the trail.
      else {
        $cid .= ':trail';

    if (!isset($tree[$cid])) {
      // If the static variable doesn't have the data, check {cache_menu}.
      $cache = cache('menu')->get($cid);
      if ($cache && isset($cache->data)) {
        // If the cache entry exists, it contains the parameters for
        // menu_build_tree().
        $tree_parameters = $cache->data;
      // If the tree data was not in the cache, build $tree_parameters.
      if (!isset($tree_parameters)) {
        $tree_parameters = array(
          'min_depth' => 1,
          'max_depth' => $max_depth,
        // Parent mlids; used both as key and value to ensure uniqueness.
        // We always want all the top-level links with plid == 0.
        $active_trail = array(0 => 0);

        // If the item for the current page is accessible, build the tree
        // parameters accordingly.
        if ($item['access']) {
          // Find a menu link corresponding to the current path. If $active_path
          // is NULL, let menu_link_get_preferred() determine the path.
          if ($active_link = menu_link_get_preferred($active_path, $menu_name)) {
            // The active link may only be taken into account to build the
            // active trail, if it resides in the requested menu. Otherwise,
            // we'd needlessly re-run _menu_build_tree() queries for every menu
            // on every page.
            if ($active_link['menu_name'] == $menu_name) {
              // Use all the coordinates, except the last one because there
              // can be no child beyond the last column.
              for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
                if ($active_link['p' . $i]) {
                  $active_trail[$active_link['p' . $i]] = $active_link['p' . $i];
              // If we are asked to build links for the active trail only, skip
              // the entire 'expanded' handling.
              if ($only_active_trail) {
                $tree_parameters['only_active_trail'] = TRUE;
          $parents = $active_trail;

          // Check whether the current menu has any links set to be expanded.
          $menu_names_with_expanded_items = state_get('menus_containing_expanded_items');
          $menu_contains_expanded = $menu_names_with_expanded_items && in_array($menu_name, $menu_names_with_expanded_items);
          if ($expand_all || ($menu_contains_expanded && !$only_active_trail)) {
            // Collect all the links set to be expanded, and then add all of
            // their children to the list as well.
            do {
              $query = db_select('menu_links', NULL, array('fetch' => PDO::FETCH_ASSOC));
              $query->fields('menu_links', array('mlid'));
              $query->condition('menu_name', $menu_name);
              if (!$expand_all) {
                $query->condition('expanded', 1);
              $query->condition('has_children', 1);
              $query->condition('plid', $parents, 'IN');
              $query->condition('mlid', $parents, 'NOT IN');
              $query->condition('langcode', array($GLOBALS['language']->langcode, 'und'), 'IN');
              try {
                $result = $query->execute();
              // If the menu links cannot be built, allow the page to render
              // nonetheless, absent the menu links.
              catch (PDOException $e) {
                $result = array();
              $num_rows = FALSE;
              foreach ($result as $item) {
                $parents[$item['mlid']] = $item['mlid'];
                $num_rows = TRUE;
            } while ($num_rows);
          $tree_parameters['expanded'] = $parents;
          $tree_parameters['active_trail'] = $active_trail;
        // If access is denied, we only show top-level links in menus.
        else {
          $tree_parameters['expanded'] = $active_trail;
          $tree_parameters['active_trail'] = $active_trail;

        if ($expand_all) {
          $tree_parameters['expanded'] = NULL;

        // Cache the tree building parameters using the page-specific cid.
        cache('menu')->set($cid, $tree_parameters);

      // Build the tree using the parameters; the resulting tree will be cached
      // by _menu_build_tree().
      $tree[$cid] = menu_build_tree($menu_name, $tree_parameters);
    return $tree[$cid];

  return array();