14

For example, "moduleone" defines the path 'admin/hello', which output 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" defines the path 'admin/hello', which output 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;
}

Both modules defined 'admin/hello' path for different result. With these two module enabled, how does Drupal select one than another? How does Drupal solve the conflict?

16
+100

The function invoking hook_menu() is menu_router_build(), called by menu_rebuild(). It contains the following code.

  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);

If there are two modules defining the same route, the last module in the array returned by module_implements() will override the value defined from the other modules.

The second parameter required by module_implements() is defined as:

$sort By default, modules are ordered by weight and filename, settings this option to TRUE, module list will be ordered by module name.

Since menu_router_build() doesn't pass the second parameter to menu_implements(), the function is using the default value for that parameter. This means the list of modules are ordered by their weight and filename; when two modules have the same weight, the first module that appears in the list is the one that alphabetically comes first.

Furthermore, any module implementing hook_module_implements_alter() can alter the order the hooks are invoked.

For this reason, you should not assume to know in which order the hooks are invoked.
If the purpose of the code is altering the route implemented by another module, for example because a route should be removed when a second module is installed and enabled, the code should use hook_menu_alter(). If you are trying to understand which module would "win" in the case of route conflicts, I would rather avoid such route conflict, and define a route that is not already defined from another module.

If then you are implementing hook_menu_alter(), and you want to be sure your module is executed last, to be the module that effectively overrides a route, you should implement hook_module_implements_alter() too.

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;
  }
}
  • so clear and helpful, thanks. also thanks for mentioning the usage hook_menu_alter() in this case. – gilzero Jan 17 '12 at 15:18
  • what if two modules define same path in hook_menu_alter()? same rules applied? – gilzero Jan 19 '12 at 12:07
  • hook_menu_alter() is not used to define new menus, but to alter the existing ones. If two modules alter the same menu, the alteration that survives is the one from the module that is executed for last. – kiamlaluno Jan 19 '12 at 23:41
4

Whichever module has a lower weight value in the system table will be called first, so the module with the higher weight value will 'win' in this case.

If the weights are the same for two (or more) modules I believe there is no specific ordering done other than the ordering that comes directly from the MySQL table (I could be wrong about that though).

As the return results from the invoke of hook_menu are just put into a single array of menu items there will never be a 'conflict' as such, the results from later calls to hook_menu will simply override those from the earlier calls.

  • 4
    According to Pro Drupal Development, modules with equal weight are called in alphabetical order by their system name. – mpdonadio Jan 17 '12 at 14:31
  • 2
    What reported by that book is correct: The list of modules implementing a hook is generally sorted by weight and alphabetically; it would not be sorted if module_implements() gets FALSE as second parameter, but functions like module_invoke_all() calls it with just a parameter. – kiamlaluno Jan 17 '12 at 14:40
  • My previous comment is not completely exact. What I report in my answer is correct. – kiamlaluno Jan 17 '12 at 15:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.