如果两个模块都在“ hook_menu”中定义了相同的菜单路径,那么Drupal将选择哪个?


14

例如,“ moduleone”定义路径“ admin / hello”,该路径输出print_moduleone_stuff()

/**
 * Implements hook_menu()
 */
function moduleone_menu() {
  $items['admin/hello'] = array(
    'title' => 'Module One Hello World',
    'page callback' => print_moduleone_stuff,
  );
  return $items;
}

“ moduletwo”定义路径“ admin / hello”,其输出 print_moduletwo_stuff()

/**
 * Implements hook_menu()
 */
function moduletwo_menu() {
  $items['admin/hello'] = array(
    'title' => 'Module Two Hello World',
    'page callback' => print_moduletwo_stuff,
  );
  return $items;
}

两个模块都为不同的结果定义了“ admin / hello”路径。启用这两个模块后,Drupal如何选择一个?Drupal如何解决冲突?

Answers:


16

调用的函数hook_menu()menu_router_build(),由menu_rebuild()调用。它包含以下代码。

  foreach (module_implements('menu') as $module) {
    $router_items = call_user_func($module . '_menu');
    if (isset($router_items) && is_array($router_items)) {
      foreach (array_keys($router_items) as $path) {
        $router_items[$path]['module'] = $module;
      }
      $callbacks = array_merge($callbacks, $router_items);
    }
  }
  // Alter the menu as defined in modules, keys are like user/%user.
  drupal_alter('menu', $callbacks);

如果有两个模块定义相同的路由,则返回的数组中的最后一个模块module_implements()将覆盖其他模块定义的值。

所需的第二个参数module_implements()定义为:

$sort 默认情况下,模块按权重和文件名排序,将此选项设置为 TRUE,模块列表将按模块名称排序。

由于menu_router_build()没有将第二个参数传递给menu_implements(),因此该函数正在使用该参数的默认值。这意味着模块列表按其权重和文件名排序。当两个模块的权重相同时,列表中出现的第一个模块是按字母顺序排列的第一个模块。

此外,任何实现 hook_module_implements_alter()都可以更改挂钩的调用顺序。

出于这个原因,您不应假定知道挂钩的调用顺序。
如果代码的目的是更改由另一个模块实现的路由,例如,由于在安装和启用第二个模块时应删除路由,则代码应使用hook_menu_alter()。如果您试图了解在发生路由冲突的情况下哪个模块将“获胜”,我宁愿避免此类路由冲突,并定义一个尚未从其他模块定义的路由。

如果要实现hook_menu_alter(),则要确保模块最后执行,作为有效覆盖路由的模块,也应该实现hook_module_implements_alter()

function mymodule_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'menu_alter') {
    // Move mymodule_menu_alter() to the end of the list. module_implements()
    // iterates through $implementations with a foreach loop which PHP iterates
    // in the order that the items were added, so to move an item to the end of
    // the array, we remove it and then add it.
    $group = $implementations['mymodule'];
    unset($implementations['mymodule']);
    $implementations['mymodule'] = $group;
  }
}

非常清楚和乐于助人,谢谢。也感谢您在这种情况下提到了hook_menu_alter()的用法。
gilzero 2012年

如果两个模块在hook_menu_alter()中定义相同的路径怎么办?应用相同的规则?
gilzero

hook_menu_alter()不是用来定义新菜单,而是用来更改现有菜单。如果两个模块更改了同一菜单,则剩下的更改是最后执行的模块中的更改。
kiamlaluno

4

表中weight值较低的模块system将首先被调用,因此具有较高值的​​模块将被调用weight在这种情况下,值将“获胜”。

如果两个(或多个)模块的权重相同,那么我相信除了直接来自MySQL表的排序外,没有其他特定的排序(尽管我可能是错的)。

由于调用的返回结果hook_menu仅放在菜单项的单个数组中,因此永远不会发生“冲突”,以后调用的结果hook_menu将仅覆盖先前调用的结果。


4
根据Pro Drupal Development的介绍,具有相同权重的模块按其系统名称按字母顺序调用。
mpdonadio

2
那本书报告的内容是正确的:实现钩子的模块列表通常按权重和字母顺序排序;如果作为第二个参数module_implements()获取FALSE,它将不会被排序,但是像module_invoke_all()仅使用一个参数调用它这样的功能。
kiamlaluno

我之前的评论并不完全准确。我在回答中报告的是正确的。
kiamlaluno
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.