chx's overview of menu system innards
This is a longish description of menu structures which are IMO way too big and rarely needed to stuff into a code comment of Drupal. At original creation, it's only chx's understanding of menu system innards.
For easier reference, let $menu = menu_get_menu();
Menu items can be defined at two places: in hook_menu and on the admin/menu interface. The latter items are stored in the database. However, there is the problem of modules changing from time to time and this changes the defined menu items. However, menu items defined on the UI must remain stable, hold their place in the menu tree, so we save some menu information to the menu table to 'pin it down'. There is another problem here, though -- hook_menu defines a ton more information than menu table store. Unless you want to store the PHP code there is no other choice. So, we should store information from the modules into a table and maintain this relationship.
menu IDs are negative for items defined in hook_menu and positive for those stored in the database. There is a special 0 menu ID which is the root element, it's children are the menus.
$menu['items'] will contain an associative array, the keys of which are menu IDs. The values are themselves associative arrays, with the following key-value pairs defined:
- pid - the menu ID of the parent of the menu item or 0 for menus.
- path - The Drupal path to the menu item.
- title - The displayed title of the menu or menu item. It will already have been translated by the locale system.
- weight - the weight of the menu or menu item.
- access - whether the current item is accessible to the current user.
- type - typical values are 22 (MENU_NORMAL_ITEM), 4 (MENU_CALLBACK). See the defines in the beginning of menu.inc
- children - A linear list of the menu ID's of this item's children. While theoretically this is redundant information as the pid will contain the same information, it's a huge performance benefit to save these.
$menu['visible'] is already documented, it's a subset of $menu['items'] actually.
$menu['path index'] is an associative array, the keys are Drupal paths and the values are menu IDs. $menu['callbacks'] is again an associative array, the keys are Drupal paths and the values are an (surprise!) associative array which define the callback. This callback array always contains a 'callback' => 'name_of_callback_function' pair and optionally a 'callback arguments' => array(arg1, arg2, arg3).
Now that we are familiar with the menu structure, let's see what happens when we save a menu item (menu_edit_item_save): it's saved into the database, without much consideration about our structure. When you move an item it's possible that you put it into under an item which is not yet in the database and then we need to fix the situation.
That's what menu_rebuild does: first it collects all items not yet in the database into the queue. Then it walks the loop and if an element has a valid parent and is not yet in the menu tree then saves it and for new items we fix all its children mid to the new mid. Regardless of the item needed fixing or not, the item is dropped from the queue. As long as there are no recursive menu items, and there can't be, that's guaranteed by _menu_build, the queue will empty after a few loop.
