hook_menu(): More than you ever needed to know

Posted By Brad Czerniak on Thursday, December 5, 2013 - 15:45

hook_menu is the most-used Drupal core hook; it's well-documented, and many tutorials are available.

This isn't documentation or a tutorial. What I wanted to know was: how much are each of the plentiful [Drupal 7] API options used? So I set about to collect data, using core implementations of hook_menu(), as well as those from the contrib modules tracked by DrupalContrib.

tl;dr

Things are very much as you might expect. Options that seem one-off or potentially-underused are. Coding practices appear to consistently mirror what you might predict if you guessed beforehand.

Items declared

Perhaps it's ease-of-mind, or perhaps a holdover from a previous implementation; however it is,

  1.   $items = array();

is not unfamiliar to people who peruse hook_menu implementations.

But how common is it?

In the 58 core functions tested, 12 include a declaration. For 288 contrib functions, 131 do.

Implements hook_menu().

In core, only batch_test_menu() diverges. In the contrib space, 18% get it wrong, in predictable ways:

  • Implement hook_menu().
  • Implementation of hook_menu().
  • Implements hook_menu
  • Implements hook_menu()
  • Implements hook_menu() to set up the URLs (menu entries) for the file examples.
  • Implement of hook_menu().
  • Implementation of hook_menu_()
  • Delegated implementation of hook_menu().
  • Implement hook_menu.


Path Arguments

The 371 functions (some modified) I ran through the test script yielded 1864 menu items. Of those, the maximum number of path arguments (strings separated by slashes) was 9 (3 items, by 2 modules). The minimum was obviously 1, while the average was almost exactly 4.

Implementations made good use of wildcards, with a minimum of 0 used, a maximum of 5, and an average of 0.47.

Admin Paths

A full 55% of tested menu items were used for paths starting with 'admin/'.

Titles

'title' is the only required option for an $item. Despite this, a whopping 11.5% of items use an empty string. Average titles are 12.1 characters long, while the longest at 67 is "Page that displays different themes without using a theme callback." from menu_test.

3.8% of items declare a title callback. Regardless of declaring a callback (as t() is used by default), 4.8% declare title arguments (with og_ui being the clear argument-passing winner with 6 items each passing 3 or 4 arguments apiece). Wildcard (from the path) arguments are slightly favored over strings or other arguments.

34.5% of items have descriptions. Of those, the average description length is 42.2 characters. The shortest, at 5 characters, is "todo.".

Page Callbacks

The vast majority (90.5%) of items declare a page callback. Since hook_menu() inherits callbacks from parent items, the remaining items must be using this affordance.

Among callbacks, the most common is drupal_get_form, which 40.3% of items use. system_admin_menu_block_page() is the distant second, with 1.7% usage.

62.2% of items pass an average of 1.6 arguments to the page callback, with 6 arguments being the outlying maximum number of arguments. 44% are wildcards, while the rest are strings (such as the form ID).

Delivery and Access

A pitiful 1.5% of items declare a delivery callback. Most of those are ajax_deliver().

1.4% of items pass a simple TRUE value as the access callback. 23.4% declare a function, with the most-frequent being user_access() (which is the default).

75.3% pass at least one access argument. Of those, 80% were strings (or non-integers). Widcards were only passed 20% of the time.

Theme

A theme callback was only defined 2% of the time. 24/38 of those were ajax_base_page_theme(). Only 21% of items that used a theme callback passed a theme argument. Of those, only 1 passed a wildcard.

Files and Paths

64% of items declared a .inc file where the menu stuff resides. Only 2.7% included a file path, though. Of those, all but 2 used drupal_get_path(), with 4 of them appending '/includes'.

Type

  • None declared 32.6%
  • MENU_CALLBACK 23.9%
  • MENU_LOCAL_TASK 21.2%
  • MENU_DEFAULT_LOCAL_TASK 11.5%
  • MENU_LOCAL_ACTION 5%
  • MENU_NORMAL_ITEM 3.4%
  • MENU_VISIBLE_IN_BREADCRUMB 1.2%
  • MENU_SUGGESTED_ITEM 0.8%
  • Other 0.3%

You can see that 3.4% of items have MENU_NORMAL_ITEM. However, omitting a bitmask flag sets that as the default, so the overall number of MENU_NORMAL_ITEMs is actually 36%.

Other

0.8% of items declared any options.

1.2% declared a position, with left edging right by a margin of 13-to-10.

I left some underscores out of tab_parent and tab_root, so I don't have any data on those for this post.

Context is relatively popular by that standard, with 5.3% of items using one.

2.2% of items use load arguments.

Item weight is declared by 32% of items. The weights range from -100 to 200, with the average weight being 3.36. The most common weight is -10, followed by 0.

Only 7/1864 items set 'expanded' to TRUE. None of these are for items with menu_name, which number 34/1864. The most common menu_name is 'devel', with 18 items.

Conclusion

The most-used hook has tons of options, some of which only get used 1-or-2 out of 100 times. Whether it's worthwhile to tweak the API (or the documentation) to suit these realities is debatable. All I know is that it was a lot of fun to collect this data and share it with you. Thanks for reading!