[development] The new menu system

Karoly Negyesi karoly at negyesi.net
Tue Sep 26 11:58:37 UTC 2006


Hi,

Note: this post is very long. But it worths a reading.

I dare to say that one of the biggest results of DrupalCon is that a new  
menu system is born.

It all began almost a year ago:

http://lists.drupal.org/archives/development/2005-10/msg00708.html

[quote]
There are three basic tasks to consider when talking about performance  
improvements:
- displaying the visible menu (in general, this means all accessible items  
since we may need them for DHTML solutions)
- displaying the breadcrumb trail
- finding the active callback
- checking access rights
[/quote]

OK that's four. I already have code for the last three in my sandbox and  
Adrian have outlined how the first will happen.

My focus was finding the active callback. I simply followed JonBob's  
directions: "We could simplify menu declaration by allowing some sort of  
wildcards in menu paths. For example, if a module could declare the path  
"node/%d/edit", then we could dispense with the logic that defines the  
function only if we are in a node path. The code would certainly be  
cleaner in this case."

I am using the percent sign as a wildcard. There is no %d or %s just %. So  
you define node/%/edit. Let's say someone navigates to node/12/edit/foo.  
Now, how do we find out that node/%/edit needs to be run? We generate all  
possible callbacks and check which one exists:

1111 node/12/edit/foo
1110 node/12/edit/%
1101 node/12/%/foo
1100 node/12/%/%
1011 node/%/edit/foo
1010 node/%/edit/%
1001 node/%/%/foo
1000 node/%/%/%
111 node/12/edit
110 node/12/%
101 node/%/edit
100 node/%/%
11  node/12
10  node/%
1   node

The binary numbers are generated naturally: where you see a %, that's a 0,  
where you see a specific part, that's a 1. This will nicely indicate the  
the order in which they need to be checked (this is my idea and I am very  
proud of it -- nothing else in here is my idea).

So, we have found node/%/edit. Can we access it? Instead of specifying  
'access' => node_access('update', arg(1)) now we do 'access' =>  
'node_access', 'access arguments' => array('update', 1) . So we use  
Adrian's favorite: callbacks. I map 1 to arg(1) on runtime for both access  
and callback arguments. (Side note: I know currently node_access gets a  
node and not a node id. Adding node_load won't stop us.)

Our life gets a bit complicated here, because we do not need to grab one  
menu item but three: one that defines the title, one that defines the  
(page) callback and one that defines access. Instead of running three  
queries against a huge table, I planned to retrieve all possible matches,  
and pick my three items. As a bonus, I will get the breadcrumb trail.  
Someone at the second menu meeting (Steven? Adrian?) pointed out that I  
could move this to the builder phase, and that's done. So we are back to  
retrieving one item which has title, callback and access inherited as  
needed.

At mentioned in the third meeting, Steven pointed out that my system is  
vulnerable to a simple DoS attack so now menu item definitions are limited  
to 6 parts. I can hardly imagine this being a real limitation, I can  
consider raising it to seven but definitely not more. We are shooting at  
what's useable not all possible cases.

Finally we need to display the navigation block. As said, I shoot at  
what's probable so instead of caching per user I will check every menu  
item's access on the fly. The probable case is that these are user_access  
calls. Maybe user_access needs to be changed to use a multi dimensional  
array if the repeated strpos calls are too slow. With that said, we need a  
mechanism to discover which menu items are possible. We will simply cache  
by parent id and unserialize that (credit goes to Adrian for this). Note  
that these structures are nowhere near the size of the current menu array.

As a bonus feature, we will have hook_menu_alter. To make that usable, we  
moved the path to the menu item key. (Silly me was doing it in the builder  
but Adrian pointed out that I should simply define the menu items this way)

Aside from Adrian and Steven, big kudos goes to Earl Milles  
(merlinofchaos).

What's done? On the menu level, I need to write the navigation block, all  
else is done. I also need to convert modules -- node and user already  
done. All this took only a dozen hours or so, beginning with the first  
evening/night of DrupalCon first day ending on the train to Charleroi  
converting user and three meetings at DrupalCon / BarCamp.

Regards

NK


More information about the development mailing list