调用的函数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;
}
}