diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index a1c8a01..8844ae5 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -332,13 +332,7 @@ function _menu_link_translate(&$item) {
  *   be rendered by drupal_render().
  */
 function menu_tree($menu_name) {
-  $menu_output = &drupal_static(__FUNCTION__, array());
-
-  if (!isset($menu_output[$menu_name])) {
-    $tree = menu_tree_page_data($menu_name);
-    $menu_output[$menu_name] = menu_tree_output($tree);
-  }
-  return $menu_output[$menu_name];
+  return \Drupal::service('menu_link.tree')->getRenderedTree($menu_name);
 }
 
 /**
@@ -356,62 +350,7 @@ function menu_tree($menu_name) {
  *   A structured array to be rendered by drupal_render().
  */
 function menu_tree_output($tree) {
-  $build = array();
-  $items = array();
-
-  // Pull out just the menu links we are going to render so that we
-  // get an accurate count for the first/last classes.
-  foreach ($tree as $data) {
-    if ($data['link']['access'] && !$data['link']['hidden']) {
-      $items[] = $data;
-    }
-  }
-
-  foreach ($items as $data) {
-    $class = array();
-    // Set a class for the <li>-tag. Since $data['below'] may contain local
-    // tasks, only set 'expanded' class if the link also has children within
-    // the current menu.
-    if ($data['link']['has_children'] && $data['below']) {
-      $class[] = 'expanded';
-    }
-    elseif ($data['link']['has_children']) {
-      $class[] = 'collapsed';
-    }
-    else {
-      $class[] = 'leaf';
-    }
-    // Set a class if the link is in the active trail.
-    if ($data['link']['in_active_trail']) {
-      $class[] = 'active-trail';
-      $data['link']['localized_options']['attributes']['class'][] = 'active-trail';
-    }
-
-    // Allow menu-specific theme overrides.
-    $element['#theme'] = 'menu_link__' . strtr($data['link']['menu_name'], '-', '_');
-    $element['#attributes']['class'] = $class;
-    $element['#title'] = $data['link']['title'];
-    // @todo Use route name and parameters to generate the link path, unless
-    //    it is external.
-    $element['#href'] = $data['link']['link_path'];
-    $element['#localized_options'] = !empty($data['link']['localized_options']) ? $data['link']['localized_options'] : array();
-    $element['#below'] = $data['below'] ? menu_tree_output($data['below']) : $data['below'];
-    $element['#original_link'] = $data['link'];
-    // Index using the link's unique mlid.
-    $build[$data['link']['mlid']] = $element;
-  }
-  if ($build) {
-    // Make sure drupal_render() does not re-order the links.
-    $build['#sorted'] = TRUE;
-    // Add the theme wrapper for outer markup.
-    // Allow menu-specific theme overrides.
-    $build['#theme_wrappers'][] = 'menu_tree__' . strtr($data['link']['menu_name'], '-', '_');
-    // Set cache tag.
-    $menu_name = $data['link']['menu_name'];
-    $build['#cache']['tags']['menu'][$menu_name] = $menu_name;
-  }
-
-  return $build;
+  return \Drupal::service('menu_link.tree')->output($tree);
 }
 
 /**
@@ -435,52 +374,7 @@ function menu_tree_output($tree) {
  *   An tree of menu links in an array, in the order they should be rendered.
  */
 function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) {
-  $tree = &drupal_static(__FUNCTION__, array());
-  $language_interface = \Drupal::languageManager()->getCurrentLanguage();
-
-  // Use $mlid as a flag for whether the data being loaded is for the whole tree.
-  $mlid = isset($link['mlid']) ? $link['mlid'] : 0;
-  // Generate a cache ID (cid) specific for this $menu_name, $link, $language, and depth.
-  $cid = 'links:' . $menu_name . ':all:' . $mlid . ':' . $language_interface->id . ':' . (int) $max_depth;
-
-  if (!isset($tree[$cid])) {
-    // If the static variable doesn't have the data, check {cache_menu}.
-    $cache = \Drupal::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,
-      );
-      if ($mlid) {
-        // The tree is for a single item, so we need to match the values in its
-        // p columns and 0 (the top level) with the plid values of other links.
-        $parents = array(0);
-        for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
-          if (!empty($link["p$i"])) {
-            $parents[] = $link["p$i"];
-          }
-        }
-        $tree_parameters['expanded'] = $parents;
-        $tree_parameters['active_trail'] = $parents;
-        $tree_parameters['active_trail'][] = $mlid;
-      }
-
-      // Cache the tree building parameters using the page-specific cid.
-      \Drupal::cache('menu')->set($cid, $tree_parameters, Cache::PERMANENT, array('menu' => $menu_name));
-    }
-
-    // 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 \Drupal::service('menu_link.tree')->buildAllData($menu_name, $link, $max_depth);
 }
 
 /**
@@ -501,11 +395,7 @@ function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) {
  *   The path to use when finding the active trail.
  */
 function menu_tree_set_path($menu_name, $path = NULL) {
-  $paths = &drupal_static(__FUNCTION__);
-  if (isset($path)) {
-    $paths[$menu_name] = $path;
-  }
-  return isset($paths[$menu_name]) ? $paths[$menu_name] : NULL;
+  return \Drupal::service('menu_link.tree')->setPath($menu_name, $path);
 }
 
 /**
@@ -519,7 +409,7 @@ function menu_tree_set_path($menu_name, $path = NULL) {
  *   menu_tree_set_path(), NULL is returned.
  */
 function menu_tree_get_path($menu_name) {
-  return menu_tree_set_path($menu_name);
+  return \Drupal::service('menu_link.tree')->getPath($menu_name);
 }
 
 /**
@@ -545,120 +435,7 @@ function menu_tree_get_path($menu_name) {
  *   same structure described for the top-level array.
  */
 function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail = FALSE) {
-  $tree = &drupal_static(__FUNCTION__, array());
-
-  $language_interface = \Drupal::languageManager()->getCurrentLanguage();
-
-  // Check if the active trail has been overridden for this menu tree.
-  $active_path = menu_tree_get_path($menu_name);
-  // Load the request corresponding to the current page.
-  $request = \Drupal::request();
-  $system_path = NULL;
-  if ($route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) {
-    // @todo https://drupal.org/node/2068471 is adding support so we can tell
-    // if this is called on a 404/403 page.
-    $system_path = $request->attributes->get('_system_path');
-    $page_not_403 = 1;
-  }
-  if (isset($system_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:' . $system_path . ':' . $language_interface->id . ':' . $page_not_403 . ':' . (int) $max_depth;
-    // If we are asked for the active trail only, and $menu_name has not been
-    // built and cached for this page yet, then this likely means that it
-    // won't be built anymore, as this function is invoked from
-    // template_preprocess_page(). So in order to not build a giant menu tree
-    // that needs to be checked for access on all levels, we simply check
-    // whether we have the menu already in cache, or otherwise, build a minimum
-    // tree containing the active trail only.
-    // @see menu_set_active_trail()
-    if (!isset($tree[$cid]) && $only_active_trail) {
-      $cid .= ':trail';
-    }
-
-    if (!isset($tree[$cid])) {
-      // If the static variable doesn't have the data, check {cache_menu}.
-      $cache = \Drupal::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 this page is accessible to the current user, build the tree
-        // parameters accordingly.
-        if ($page_not_403) {
-          // 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;
-
-          $expanded = \Drupal::state()->get('menu_expanded');
-          // Check whether the current menu has any links set to be expanded.
-          if (!$only_active_trail && $expanded && in_array($menu_name, $expanded)) {
-            // Collect all the links set to be expanded, and then add all of
-            // their children to the list as well.
-            do {
-              $query = \Drupal::entityQuery('menu_link')
-                ->condition('menu_name', $menu_name)
-                ->condition('expanded', 1)
-                ->condition('has_children', 1)
-                ->condition('plid', $parents, 'IN')
-                ->condition('mlid', $parents, 'NOT IN');
-              $result = $query->execute();
-              $parents += $result;
-            } while (!empty($result));
-          }
-          $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;
-        }
-        // Cache the tree building parameters using the page-specific cid.
-        \Drupal::cache('menu')->set($cid, $tree_parameters, Cache::PERMANENT, array('menu' => $menu_name));
-      }
-
-      // 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();
+  return \Drupal::service('menu_link.tree')->menu_tree_all_data($menu_name, $max_depth, $only_active_trail);
 }
 
 /**
@@ -686,84 +463,7 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail =
  *   A fully built menu tree.
  */
 function menu_build_tree($menu_name, array $parameters = array()) {
-  // Build the menu tree.
-  $data = _menu_build_tree($menu_name, $parameters);
-  // Check access for the current user to each item in the tree.
-  menu_tree_check_access($data['tree'], $data['node_links']);
-  return $data['tree'];
-}
-
-/**
- * Builds a menu tree.
- *
- * This function may be used build the data for a menu tree only, for example
- * to further massage the data manually before further processing happens.
- * menu_tree_check_access() needs to be invoked afterwards.
- *
- * @see menu_build_tree()
- */
-function _menu_build_tree($menu_name, array $parameters = array()) {
-  // Static cache of already built menu trees.
-  $trees = &drupal_static(__FUNCTION__, array());
-  $language_interface = \Drupal::languageManager()->getCurrentLanguage();
-
-  // Build the cache id; sort parents to prevent duplicate storage and remove
-  // default parameter values.
-  if (isset($parameters['expanded'])) {
-    sort($parameters['expanded']);
-  }
-  $tree_cid = 'links:' . $menu_name . ':tree-data:' . $language_interface->id . ':' . hash('sha256', serialize($parameters));
-
-  // If we do not have this tree in the static cache, check {cache_menu}.
-  if (!isset($trees[$tree_cid])) {
-    $cache = \Drupal::cache('menu')->get($tree_cid);
-    if ($cache && isset($cache->data)) {
-      $trees[$tree_cid] = $cache->data;
-    }
-  }
-
-  if (!isset($trees[$tree_cid])) {
-    $query = \Drupal::entityQuery('menu_link');
-    for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) {
-      $query->sort('p' . $i, 'ASC');
-    }
-    $query->condition('menu_name', $menu_name);
-    if (!empty($parameters['expanded'])) {
-      $query->condition('plid', $parameters['expanded'], 'IN');
-    }
-    elseif (!empty($parameters['only_active_trail'])) {
-      $query->condition('mlid', $parameters['active_trail'], 'IN');
-    }
-    $min_depth = (isset($parameters['min_depth']) ? $parameters['min_depth'] : 1);
-    if ($min_depth != 1) {
-      $query->condition('depth', $min_depth, '>=');
-    }
-    if (isset($parameters['max_depth'])) {
-      $query->condition('depth', $parameters['max_depth'], '<=');
-    }
-    // Add custom query conditions, if any were passed.
-    if (isset($parameters['conditions'])) {
-      foreach ($parameters['conditions'] as $column => $value) {
-        $query->condition($column, $value);
-      }
-    }
-
-    // Build an ordered array of links using the query result object.
-    $links = array();
-    if ($result = $query->execute()) {
-      $links = menu_link_load_multiple($result);
-    }
-    $active_trail = (isset($parameters['active_trail']) ? $parameters['active_trail'] : array());
-    $data['tree'] = menu_tree_data($links, $active_trail, $min_depth);
-    $data['node_links'] = array();
-    menu_tree_collect_node_links($data['tree'], $data['node_links']);
-
-    // Cache the data, if it is not already in the cache.
-    \Drupal::cache('menu')->set($tree_cid, $data, Cache::PERMANENT, array('menu' => $menu_name));
-    $trees[$tree_cid] = $data;
-  }
-
-  return $trees[$tree_cid];
+  return \Drupal::service('menu_link.tree')->buildTree($menu_name, $parameters);
 }
 
 /**
@@ -775,18 +475,7 @@ function _menu_build_tree($menu_name, array $parameters = array()) {
  *   An array in which to store the collected node links.
  */
 function menu_tree_collect_node_links(&$tree, &$node_links) {
-  foreach ($tree as $key => $v) {
-    if ($tree[$key]['link']['router_path'] == 'node/%') {
-      $nid = substr($tree[$key]['link']['link_path'], 5);
-      if (is_numeric($nid)) {
-        $node_links[$nid][$tree[$key]['link']['mlid']] = &$tree[$key]['link'];
-        $tree[$key]['link']['access'] = FALSE;
-      }
-    }
-    if ($tree[$key]['below']) {
-      menu_tree_collect_node_links($tree[$key]['below'], $node_links);
-    }
-  }
+  return \Drupal::service('menu_link.tree')->collectNodeLinks($tree, $node_links);
 }
 
 /**
@@ -799,47 +488,7 @@ function menu_tree_collect_node_links(&$tree, &$node_links) {
  *   menu_tree_collect_node_links().
  */
 function menu_tree_check_access(&$tree, $node_links = array()) {
-  if ($node_links) {
-    $nids = array_keys($node_links);
-    $select = db_select('node_field_data', 'n');
-    $select->addField('n', 'nid');
-    // @todo This should be actually filtering on the desired node status field
-    //   language and just fall back to the default language.
-    $select->condition('n.status', 1);
-
-    $select->condition('n.nid', $nids, 'IN');
-    $select->addTag('node_access');
-    $nids = $select->execute()->fetchCol();
-    foreach ($nids as $nid) {
-      foreach ($node_links[$nid] as $mlid => $link) {
-        $node_links[$nid][$mlid]['access'] = TRUE;
-      }
-    }
-  }
-  _menu_tree_check_access($tree);
-}
-
-/**
- * Sorts the menu tree and recursively checks access for each item.
- */
-function _menu_tree_check_access(&$tree) {
-  $new_tree = array();
-  foreach ($tree as $key => $v) {
-    $item = &$tree[$key]['link'];
-    _menu_link_translate($item);
-    if ($item['access'] || ($item['in_active_trail'] && strpos($item['href'], '%') !== FALSE)) {
-      if ($tree[$key]['below']) {
-        _menu_tree_check_access($tree[$key]['below']);
-      }
-      // The weights are made a uniform 5 digits by adding 50000 as an offset.
-      // After _menu_link_translate(), $item['title'] has the localized link title.
-      // Adding the mlid to the end of the index insures that it is unique.
-      $new_tree[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $tree[$key];
-    }
-  }
-  // Sort siblings in the tree based on the weights and localized titles.
-  ksort($new_tree);
-  $tree = $new_tree;
+  return \Drupal::service('menu_link.tree')->checkAccess($tree, $node_links);
 }
 
 /**
@@ -869,44 +518,7 @@ function _menu_tree_check_access(&$tree) {
  *     greater than or equal to $depth.
  */
 function menu_tree_data(array $links, array $parents = array(), $depth = 1) {
-  // Reverse the array so we can use the more efficient array_pop() function.
-  $links = array_reverse($links);
-  return _menu_tree_data($links, $parents, $depth);
-}
-
-/**
- * Builds the data representing a menu tree.
- *
- * The function is a bit complex because the rendering of a link depends on
- * the next menu link.
- */
-function _menu_tree_data(&$links, $parents, $depth) {
-  $tree = array();
-  while ($item = array_pop($links)) {
-    // We need to determine if we're on the path to root so we can later build
-    // the correct active trail.
-    $item['in_active_trail'] = in_array($item['mlid'], $parents);
-    // Add the current link to the tree.
-    $tree[$item['mlid']] = array(
-      'link' => $item,
-      'below' => array(),
-    );
-    // Look ahead to the next link, but leave it on the array so it's available
-    // to other recursive function calls if we return or build a sub-tree.
-    $next = end($links);
-    // Check whether the next link is the first in a new sub-tree.
-    if ($next && $next['depth'] > $depth) {
-      // Recursively call _menu_tree_data to build the sub-tree.
-      $tree[$item['mlid']]['below'] = _menu_tree_data($links, $parents, $next['depth']);
-      // Fetch next link after filling the sub-tree.
-      $next = end($links);
-    }
-    // Determine if we should exit the loop and return.
-    if (!$next || $next['depth'] < $depth) {
-      break;
-    }
-  }
-  return $tree;
+  return \Drupal::service('menu_link.tree')->buildTreeData($links, $parents, $depth);
 }
 
 /**
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuTree.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuTree.php
new file mode 100644
index 0000000..9bd54c3
--- /dev/null
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuTree.php
@@ -0,0 +1,716 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_link\MenuTree.
+ */
+
+namespace Drupal\menu_link;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\KeyValueStore\StateInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+class MenuTree {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   *   The database connection.
+   */
+  protected $database;
+
+  /**
+   * The cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cache;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languagerManager;
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * The menu link storage controller.
+   *
+   * @var \Drupal\menu_link\MenuLinkStorageControllerInterface
+   */
+  protected $menuLinkStorage;
+
+  /**
+   * The entity query factory.
+   *
+   * @var \Drupal\Core\Entity\Query\QueryFactory
+   */
+  protected $queryFactory;
+
+  /**
+   * The state.
+   *
+   * @var \Drupal\Core\KeyValueStore\StateInterface
+   */
+  protected $state;
+
+  /**
+   * A list of active trail paths keyed by $menu_name.
+   *
+   * @var array
+   */
+  protected $trailPaths;
+
+  /**
+   * Stores the rendered menu output keyed by $menu_name.
+   *
+   * @var array
+   */
+  protected $menuOutput;
+
+  /**
+   * Stores the menu menu tree keyed by a cache ID.
+   *
+   * This cache ID is built using the $menu_name, the current language and
+   * some parameters passed into an entity query.
+   */
+  protected $menuTree;
+
+  /**
+   * Stores the full menu tree data keyed by a cache ID.
+   *
+   * This cache ID is built with the menu name, a passed in root link ID, the
+   * current language as well as the maximum depth.
+   *
+   * @var array
+   */
+  protected $menuFullTrees;
+
+  /**
+   * Stores the menu tree data on the current page keyed by a cache ID.
+   *
+   * This contains less information than a tree built with buildAllData.
+   *
+   * @var array
+   */
+  protected $menuPageTrees;
+
+  public function __construct(Connection $database, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, RequestStack $request_stack, EntityManagerInterface $entity_manager, QueryFactory $entity_query_factory, StateInterface $state) {
+    $this->database = $database;
+    $this->cache = $cache_backend;
+    $this->languageManager = $language_manager;
+    $this->requestStack = $request_stack;
+    $this->menuLinkStorage = $entity_manager->getStorageController('menu_link');
+    $this->queryFactory = $entity_query_factory;
+    $this->state = $state;
+  }
+
+  /**
+   * Gets the data structure representing a named menu tree.
+   *
+   * Since this can be the full tree including hidden items, the data returned
+   * may be used for generating an an admin interface or a select.
+   *
+   * @param string $menu_name
+   *   The named menu links to return
+   * @param array $link
+   *   A fully loaded menu link, or NULL. If a link is supplied, only the
+   *   path to root will be included in the returned tree - as if this link
+   *   represented the current page in a visible menu.
+   * @param int $max_depth
+   *   Optional maximum depth of links to retrieve. Typically useful if only one
+   *   or two levels of a sub tree are needed in conjunction with a non-NULL
+   *   $link, in which case $max_depth should be greater than $link['depth'].
+   *
+   * @return array
+   *   An tree of menu links in an array, in the order they should be rendered.
+   */
+  public function buildAllData($menu_name, $link = NULL, $max_depth = NULL) {
+    $language_interface = $this->languageManager->getCurrentLanguage();
+
+    // Use $mlid as a flag for whether the data being loaded is for the whole tree.
+    $mlid = isset($link['mlid']) ? $link['mlid'] : 0;
+    // Generate a cache ID (cid) specific for this $menu_name, $link, $language, and depth.
+    $cid = 'links:' . $menu_name . ':all:' . $mlid . ':' . $language_interface->id . ':' . (int) $max_depth;
+
+    if (!isset($this->menuFullTrees[$cid])) {
+      // If the static variable doesn't have the data, check {cache_menu}.
+      $cache = $this->cache->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,
+        );
+        if ($mlid) {
+          // The tree is for a single item, so we need to match the values in its
+          // p columns and 0 (the top level) with the plid values of other links.
+          $parents = array(0);
+          for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
+            if (!empty($link["p$i"])) {
+              $parents[] = $link["p$i"];
+            }
+          }
+          $tree_parameters['expanded'] = $parents;
+          $tree_parameters['active_trail'] = $parents;
+          $tree_parameters['active_trail'][] = $mlid;
+        }
+
+        // Cache the tree building parameters using the page-specific cid.
+        $this->cache->set($cid, $tree_parameters, Cache::PERMANENT, array('menu' => $menu_name));
+      }
+
+      // Build the tree using the parameters; the resulting tree will be cached
+      // by _menu_build_tree()).
+      $this->menuFullTrees[$cid] = $this->buildTree($menu_name, $tree_parameters);
+    }
+
+    return $this->menuFullTrees[$cid];
+  }
+
+  /**
+   * 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.
+   *
+   * @param string $menu_name
+   *   The named menu links to return.
+   * @param int $max_depth
+   *   (optional) The maximum depth of links to retrieve.
+   * @param 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.
+   *
+   * @return array
+   *   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 theming 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.
+   */
+  public function buildPageData($menu_name, $max_depth = NULL, $only_active_trail = FALSE) {
+    $language_interface = $this->languageManager->getCurrentLanguage();
+
+    // Check if the active trail has been overridden for this menu tree.
+    $active_path = $this->getPath($menu_name);
+    // Load the request corresponding to the current page.
+    $request = $this->requestStack->getCurrentRequest();
+    $system_path = NULL;
+    if ($route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) {
+      // @todo https://drupal.org/node/2068471 is adding support so we can tell
+      // if this is called on a 404/403 page.
+      $system_path = $request->attributes->get('_system_path');
+      $page_not_403 = 1;
+    }
+    if (isset($system_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:' . $system_path . ':' . $language_interface->id . ':' . $page_not_403 . ':' . (int) $max_depth;
+      // If we are asked for the active trail only, and $menu_name has not been
+      // built and cached for this page yet, then this likely means that it
+      // won't be built anymore, as this function is invoked from
+      // template_preprocess_page(). So in order to not build a giant menu tree
+      // that needs to be checked for access on all levels, we simply check
+      // whether we have the menu already in cache, or otherwise, build a minimum
+      // tree containing the active trail only.
+      // @see menu_set_active_trail()
+      if (!isset($this->menuPageTrees[$cid]) && $only_active_trail) {
+        $cid .= ':trail';
+      }
+
+      if (!isset($this->menuPageTrees[$cid])) {
+        // If the static variable doesn't have the data, check {cache_menu}.
+        $cache = $this->cache->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 this page is accessible to the current user, build the tree
+          // parameters accordingly.
+          if ($page_not_403) {
+            // 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 = $this->menuLinkGetPreferred($menu_name, $active_path)) {
+              // 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;
+
+            $expanded = $this->state->get('menu_expanded');
+            // Check whether the current menu has any links set to be expanded.
+            if (!$only_active_trail && $expanded && in_array($menu_name, $expanded)) {
+              // Collect all the links set to be expanded, and then add all of
+              // their children to the list as well.
+              do {
+                $query = $this->queryFactory->get('menu_link')
+                  ->condition('menu_name', $menu_name)
+                  ->condition('expanded', 1)
+                  ->condition('has_children', 1)
+                  ->condition('plid', $parents, 'IN')
+                  ->condition('mlid', $parents, 'NOT IN');
+                $result = $query->execute();
+                $parents += $result;
+              } while (!empty($result));
+            }
+            $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;
+          }
+          // Cache the tree building parameters using the page-specific cid.
+          $this->cache->set($cid, $tree_parameters, Cache::PERMANENT, array('menu' => $menu_name));
+        }
+
+        // Build the tree using the parameters; the resulting tree will be cached
+        // by _menu_build_tree().
+        $this->menuPageTrees[$cid] = $this->buildTree($menu_name, $tree_parameters);
+      }
+      return $this->menuPageTrees[$cid];
+    }
+
+    return array();
+  }
+
+  /**
+   * Sets the path for determining the active trail of the specified menu tree.
+   *
+   * This path will also affect the breadcrumbs under some circumstances.
+   * Breadcrumbs are built using the preferred link returned by
+   * menu_link_get_preferred(). If the preferred link is inside one of the menus
+   * specified in calls to menu_tree_set_path(), the preferred link will be
+   * overridden by the corresponding path returned by menu_tree_get_path().
+   *
+   * Setting this path does not affect the main content; for that use
+   * menu_set_active_item() instead.
+   *
+   * @param $menu_name
+   *   The name of the affected menu tree.
+   * @param $path
+   *   The path to use when finding the active trail.
+   */
+  public function setPath($menu_name, $path = NULL) {
+    if (isset($path)) {
+      $this->trailPaths[$menu_name] = $path;
+    }
+  }
+
+  /**
+   * Gets the path for determining the active trail of the specified menu tree.
+   *
+   * @param $menu_name
+   *   The menu name of the requested tree.
+   *
+   * @return
+   *   A string containing the path. If no path has been specified with
+   *   menu_tree_set_path(), NULL is returned.
+   */
+  public function getPath($menu_name) {
+    return isset($this->trailPaths[$menu_name]) ? $this->trailPaths[$menu_name] : NULL;
+  }
+
+  /**
+   * Renders a menu tree based on the current path.
+   *
+   * @param string $menu_name
+   *   The name of the menu.
+   *
+   * @return array
+   *   A structured array representing the specified menu on the current page, to
+   *   be rendered by drupal_render().
+   */
+  public function getRenderedTree($menu_name) {
+
+    if (!isset($this->menuOutput[$menu_name])) {
+      $tree = $this->buildPageData($menu_name);
+      $this->menuOutput[$menu_name] = $this->output($tree);
+    }
+    return $this->menuOutput[$menu_name];
+  }
+
+  /**
+   * Returns a rendered menu tree.
+   *
+   * The menu item's LI element is given one of the following classes:
+   * - expanded: The menu item is showing its submenu.
+   * - collapsed: The menu item has a submenu which is not shown.
+   * - leaf: The menu item has no submenu.
+   *
+   * @param array $tree
+   *   A data structure representing the tree as returned from menu_tree_data.
+   *
+   * @return array
+   *   A structured array to be rendered by drupal_render().
+   */
+  public function output($tree) {
+    $build = array();
+    $items = array();
+
+    // Pull out just the menu links we are going to render so that we
+    // get an accurate count for the first/last classes.
+    foreach ($tree as $data) {
+      if ($data['link']['access'] && !$data['link']['hidden']) {
+        $items[] = $data;
+      }
+    }
+
+    foreach ($items as $data) {
+      $class = array();
+      // Set a class for the <li>-tag. Since $data['below'] may contain local
+      // tasks, only set 'expanded' class if the link also has children within
+      // the current menu.
+      if ($data['link']['has_children'] && $data['below']) {
+        $class[] = 'expanded';
+      }
+      elseif ($data['link']['has_children']) {
+        $class[] = 'collapsed';
+      }
+      else {
+        $class[] = 'leaf';
+      }
+      // Set a class if the link is in the active trail.
+      if ($data['link']['in_active_trail']) {
+        $class[] = 'active-trail';
+        $data['link']['localized_options']['attributes']['class'][] = 'active-trail';
+      }
+
+      // Allow menu-specific theme overrides.
+      $element['#theme'] = 'menu_link__' . strtr($data['link']['menu_name'], '-', '_');
+      $element['#attributes']['class'] = $class;
+      $element['#title'] = $data['link']['title'];
+      // @todo Use route name and parameters to generate the link path, unless
+      //    it is external.
+      $element['#href'] = $data['link']['link_path'];
+      $element['#localized_options'] = !empty($data['link']['localized_options']) ? $data['link']['localized_options'] : array();
+      $element['#below'] = $data['below'] ? $this->output($data['below']) : $data['below'];
+      $element['#original_link'] = $data['link'];
+      // Index using the link's unique mlid.
+      $build[$data['link']['mlid']] = $element;
+    }
+    if ($build) {
+      // Make sure drupal_render() does not re-order the links.
+      $build['#sorted'] = TRUE;
+      // Add the theme wrapper for outer markup.
+      // Allow menu-specific theme overrides.
+      $build['#theme_wrappers'][] = 'menu_tree__' . strtr($data['link']['menu_name'], '-', '_');
+      // Set cache tag.
+      $menu_name = $data['link']['menu_name'];
+      $build['#cache']['tags']['menu'][$menu_name] = $menu_name;
+    }
+
+    return $build;
+  }
+
+  /**
+   * Builds a menu tree, translates links, and checks access.
+   *
+   * @param string $menu_name
+   *   The name of the menu.
+   * @param array $parameters
+   *   (optional) An associative array of build parameters. Possible keys:
+   *   - expanded: An array of parent link ids to return only menu links that are
+   *     children of one of the plids in this list. If empty, the whole menu tree
+   *     is built, unless 'only_active_trail' is TRUE.
+   *   - active_trail: An array of mlids, representing the coordinates of the
+   *     currently active menu link.
+   *   - only_active_trail: Whether to only return links that are in the active
+   *     trail. This option is ignored, if 'expanded' is non-empty.
+   *   - min_depth: The minimum depth of menu links in the resulting tree.
+   *     Defaults to 1, which is the default to build a whole tree for a menu
+   *     (excluding menu container itself).
+   *   - max_depth: The maximum depth of menu links in the resulting tree.
+   *   - conditions: An associative array of custom database select query
+   *     condition key/value pairs; see _menu_build_tree() for the actual query.
+   *
+   * @return array
+   *   A fully built menu tree.
+   */
+  public function buildTree($menu_name, array $parameters = array()) {
+    // Build the menu tree.
+    $data = $this->doBuildTree($menu_name, $parameters);
+    // Check access for the current user to each item in the tree.
+    $this->checkAccess($data['tree'], $data['node_links']);
+    return $data['tree'];
+  }
+
+  /**
+   * Builds a menu tree.
+   *
+   * This function may be used build the data for a menu tree only, for example
+   * to further massage the data manually before further processing happens.
+   * menu_tree_check_access() needs to be invoked afterwards.
+   *
+   * @see static::buildTree()
+   */
+  protected function doBuildTree($menu_name, array $parameters = array()) {
+    $language_interface = $this->languageManager->getCurrentLanguage();
+
+    // Build the cache id; sort parents to prevent duplicate storage and remove
+    // default parameter values.
+    if (isset($parameters['expanded'])) {
+      sort($parameters['expanded']);
+    }
+    $tree_cid = 'links:' . $menu_name . ':tree-data:' . $language_interface->id . ':' . hash('sha256', serialize($parameters));
+
+    // If we do not have this tree in the static cache, check {cache_menu}.
+    if (!isset($this->menuTree[$tree_cid])) {
+      $cache = $this->cache->get($tree_cid);
+      if ($cache && isset($cache->data)) {
+        $this->menuFullTrees[$tree_cid] = $cache->data;
+      }
+    }
+
+    if (!isset($this->menuTree[$tree_cid])) {
+      $query = $this->queryFactory->get('menu_link');
+      for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) {
+        $query->sort('p' . $i, 'ASC');
+      }
+      $query->condition('menu_name', $menu_name);
+      if (!empty($parameters['expanded'])) {
+        $query->condition('plid', $parameters['expanded'], 'IN');
+      }
+      elseif (!empty($parameters['only_active_trail'])) {
+        $query->condition('mlid', $parameters['active_trail'], 'IN');
+      }
+      $min_depth = (isset($parameters['min_depth']) ? $parameters['min_depth'] : 1);
+      if ($min_depth != 1) {
+        $query->condition('depth', $min_depth, '>=');
+      }
+      if (isset($parameters['max_depth'])) {
+        $query->condition('depth', $parameters['max_depth'], '<=');
+      }
+      // Add custom query conditions, if any were passed.
+      if (isset($parameters['conditions'])) {
+        foreach ($parameters['conditions'] as $column => $value) {
+          $query->condition($column, $value);
+        }
+      }
+
+      // Build an ordered array of links using the query result object.
+      $links = array();
+      if ($result = $query->execute()) {
+        $links = $this->menuLinkStorage->loadMultiple($result);
+      }
+      $active_trail = (isset($parameters['active_trail']) ? $parameters['active_trail'] : array());
+      $data['tree'] = $this->buildTreeData($links, $active_trail, $min_depth);
+      $data['node_links'] = array();
+      $this->collectNodeLinks($data['tree'], $data['node_links']);
+
+      // Cache the data, if it is not already in the cache.
+      $this->cache->set($tree_cid, $data, Cache::PERMANENT, array('menu' => $menu_name));
+      $this->menuTree[$tree_cid] = $data;
+    }
+
+    return $this->menuTree[$tree_cid];
+  }
+
+  /**
+   * Collects node links from a given menu tree recursively.
+   *
+   * @param $tree
+   *   The menu tree you wish to collect node links from.
+   * @param $node_links
+   *   An array in which to store the collected node links.
+   */
+  public function collectNodeLinks(&$tree, &$node_links) {
+    foreach ($tree as $key => $v) {
+      if ($tree[$key]['link']['router_path'] == 'node/%') {
+        $nid = substr($tree[$key]['link']['link_path'], 5);
+        if (is_numeric($nid)) {
+          $node_links[$nid][$tree[$key]['link']['mlid']] = &$tree[$key]['link'];
+          $tree[$key]['link']['access'] = FALSE;
+        }
+      }
+      if ($tree[$key]['below']) {
+        $this->collectNodeLinks($tree[$key]['below'], $node_links);
+      }
+    }
+  }
+
+  /**
+   * Checks access and performs dynamic operations for each link in the tree.
+   *
+   * @param $tree
+   *   The menu tree you wish to operate on.
+   * @param $node_links
+   *   A collection of node link references generated from $tree by
+   *   menu_tree_collect_node_links().
+   */
+  public function checkAccess(&$tree, $node_links = array()) {
+    if ($node_links) {
+      $this->checkNodeLinksAccess($node_links);
+    }
+    $this->doCheckAccess($tree);
+  }
+
+  protected function checkNodeLinksAccess($node_links) {
+    $nids = array_keys($node_links);
+    $select = $this->database->select('node_field_data', 'n');
+    $select->addField('n', 'nid');
+    // @todo This should be actually filtering on the desired node status field
+    //   language and just fall back to the default language.
+    $select->condition('n.status', 1);
+
+    $select->condition('n.nid', $nids, 'IN');
+    $select->addTag('node_access');
+    $nids = $select->execute()->fetchCol();
+    foreach ($nids as $nid) {
+      foreach ($node_links[$nid] as $mlid => $link) {
+        $node_links[$nid][$mlid]['access'] = TRUE;
+      }
+    }
+    return $node_links;
+  }
+
+  /**
+   * Sorts the menu tree and recursively checks access for each item.
+   */
+  protected function doCheckAccess(&$tree) {
+    $new_tree = array();
+    foreach ($tree as $key => $v) {
+      $item = &$tree[$key]['link'];
+      $this->menuLinkTranslate();
+      if ($item['access'] || ($item['in_active_trail'] && strpos($item['href'], '%') !== FALSE)) {
+        if ($tree[$key]['below']) {
+          $this->doCheckAccess($tree[$key]['below']);
+        }
+        // The weights are made a uniform 5 digits by adding 50000 as an offset.
+        // After _menu_link_translate(), $item['title'] has the localized link title.
+        // Adding the mlid to the end of the index insures that it is unique.
+        $new_tree[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $tree[$key];
+      }
+    }
+    // Sort siblings in the tree based on the weights and localized titles.
+    ksort($new_tree);
+    $tree = $new_tree;
+  }
+
+  /**
+   * Sorts and returns the built data representing a menu tree.
+   *
+   * @param array $links
+   *   A flat array of menu links that are part of the menu. Each array element
+   *   is an associative array of information about the menu link, containing the
+   *   fields from the {menu_links} table, and optionally additional information
+   *   from the {menu_router} table, if the menu item appears in both tables.
+   *   This array must be ordered depth-first. See _menu_build_tree() for a sample
+   *   query.
+   * @param array $parents
+   *   An array of the menu link ID values that are in the path from the current
+   *   page to the root of the menu tree.
+   * @param int $depth
+   *   The minimum depth to include in the returned menu tree.
+   *
+   * @return array
+   *   An array of menu links in the form of a tree. Each item in the tree is an
+   *   associative array containing:
+   *   - link: The menu link item from $links, with additional element
+   *     'in_active_trail' (TRUE if the link ID was in $parents).
+   *   - below: An array containing the sub-tree of this item, where each element
+   *     is a tree item array with 'link' and 'below' elements. This array will be
+   *     empty if the menu item has no items in its sub-tree having a depth
+   *     greater than or equal to $depth.
+   */
+  public function buildTreeData(array $links, array $parents = array(), $depth = 1) {
+    // Reverse the array so we can use the more efficient array_pop() function.
+    $links = array_reverse($links);
+    return $this->doBuildTreeData($links, $parents, $depth);
+  }
+
+  /**
+   * Builds the data representing a menu tree.
+   *
+   * The function is a bit complex because the rendering of a link depends on
+   * the next menu link.
+   */
+  protected function doBuildTreeData(&$links, $parents, $depth) {
+    $tree = array();
+    while ($item = array_pop($links)) {
+      // We need to determine if we're on the path to root so we can later build
+      // the correct active trail.
+      $item['in_active_trail'] = in_array($item['mlid'], $parents);
+      // Add the current link to the tree.
+      $tree[$item['mlid']] = array(
+        'link' => $item,
+        'below' => array(),
+      );
+      // Look ahead to the next link, but leave it on the array so it's available
+      // to other recursive function calls if we return or build a sub-tree.
+      $next = end($links);
+      // Check whether the next link is the first in a new sub-tree.
+      if ($next && $next['depth'] > $depth) {
+        // Recursively call _menu_tree_data to build the sub-tree.
+        $tree[$item['mlid']]['below'] = $this->doBuildTreeData($links, $parents, $next['depth']);
+        // Fetch next link after filling the sub-tree.
+        $next = end($links);
+      }
+      // Determine if we should exit the loop and return.
+      if (!$next || $next['depth'] < $depth) {
+        break;
+      }
+    }
+    return $tree;
+  }
+
+  protected function menuLinkGetPreferred($menu_name, $active_path) {
+    return menu_link_get_preferred($active_path, $menu_name);
+  }
+
+  protected function menuLinkTranslate() {
+    return _menu_link_translate($item);
+  }
+
+}
diff --git a/core/modules/menu_link/menu_link.services.yml b/core/modules/menu_link/menu_link.services.yml
new file mode 100644
index 0000000..b225016
--- /dev/null
+++ b/core/modules/menu_link/menu_link.services.yml
@@ -0,0 +1,4 @@
+services:
+  menu_link.tree:
+    class: Drupal\menu_link\MenuTree
+    arguments: ['@database', '@cache.menu', '@request_stack', '@entity.manager', '@entity.query', '@state']
diff --git a/core/modules/menu_link/tests/Drupal/menu_link/Tests/MenuTreeTest.php b/core/modules/menu_link/tests/Drupal/menu_link/Tests/MenuTreeTest.php
new file mode 100644
index 0000000..72f2adc
--- /dev/null
+++ b/core/modules/menu_link/tests/Drupal/menu_link/Tests/MenuTreeTest.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_link\Tests\MenuTreeTest.
+ */
+
+namespace Drupal\menu_link\Tests;
+
+use Drupal\menu_link\MenuTree;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+class MenuTreeTest extends UnitTestCase {
+
+  /**
+   * The tested menu tree.
+   *
+   * @var \Drupal\menu_link\MenuTree
+   */
+  protected $menuTree;
+
+  protected $connection;
+  protected $cacheBackend;
+  protected $languageManager;
+  protected $requestStack;
+  protected $entityManager;
+  protected $entityQueryFactory;
+  protected $state;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
+    $this->requestStack = new RequestStack();
+    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+    $this->entityQueryFactory = $this->getMockBuilder('Drupal\Core\Entity\Query\QueryFactory')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->state = $this->getMock('Drupal\Core\KeyValueStore\StateInterface');
+
+    $this->menuTree = new MenuTree($this->connection, $this->cacheBackend, $this->languageManager, $this->requestStack, $this->entityManager, $this->entityQueryFactory, $this->state);
+  }
+
+  /**
+   * Tests active paths.
+   */
+  public function testActivePaths() {
+    $this->assertNull($this->menuTree->getPath('test_menu1'));
+
+    $this->menuTree->setPath('test_menu1', 'example_path1');
+    $this->assertEquals('example_path1', $this->menuTree->getPath('test_menu1'));
+    $this->assertNull($this->menuTree->getPath('test_menu2'));
+
+    $this->menuTree->setPath('test_menu2', 'example_path2');
+    $this->assertEquals('example_path1', $this->menuTree->getPath('test_menu1'));
+    $this->assertEquals('example_path2', $this->menuTree->getPath('test_menu2'));
+  }
+
+} 
