This is step 1 in the WSCCI/Symfony shift in our core routing logic. See this post for background. I will not repeat it here for brevity. (Rare for me, I know.)
This issue puts a Symfony2 HttpKernel-based routing model on top of our current bootstrap, effectively replacing menu_execute_active_handler(). It is not fully developed yet. There are still a lot of @todos in the code. The goal of this patch is to get the foundation in place so that we can build on top of it, hopefully in parallel.
Please read this blog series on the Symfony2 components before reviewing this patch. I know it's long (although it's actually a quick read), but reading that will vastly help understand the moving parts in this patch and save everyone time explaining things that are already documented.
Given that, what this patch does is as follows:
- Creates a Drupal kernel, called DrupalKernel, that extends the Symfony HttpKernel implementation. That lets us do our own setup, but leverage all of the existing workflow from Symfony itself.
Creates a Drupal-specific UrlMatcher class, extending Symfony's. This class is responsible for determining the correct Route (what Drupal 7 calls a router item) for a given request. At this point it is just a fairly trivial wrapper around the existing menu lookup system. This is a temporary measure; follow-up work will add a new, more robust routing component that can live along side this one for now, and then we can remove the old one entirely once everything is ported over.
- Registers a number of subscribers to the kernel. Subscribers are essentially a convenient way to bundle Listeners, which are more or less the Symfony equivalent of hooks. HttpKernel, which we are using here, has numerous event points to control various parts of the request process and allow others to hook into it. See the code for all of the nitty gritty
- drupal_page_footer() is now a kernel "terminate" event. This needs to get cleaned up later.
- We do not actually route on the path of the request. Drupal 7 manipulated the request path in-place, stored it into $_GET['q'], and then pretended that was what the browser asked for. Instead, we now have a request attribute, system_path, that we populate in the kernel "request" event. Currently we do four things to it, all ported from the legacy handling, from different listeners: urldcode() the path, handle language prefixing on the path, replace an empty request with the front page value, and url alias dereferencing. Although it wasn't the original intent, it means that we have essentially replaced hook_url_inbound_alter() entirely with "you can do that in the kernel request event if you want", and then ported all of our existing logic to that. As a side effect, it means that it will likely be possible to move url alias functionality out of core to a module if we want. I don't know if we want to do that, but the fact that we will be able to (after we refactor url generation, too) is really cool. :-)
- Any controller that is a function we assume to be a legacy controller and wrap in an anonymous function. That means we're not actually using any of Symfony's magic controller argument handling yet, but it means we needed to change almost nothing in the current routing system. That's a good trade off. Once the new routing system is in place we will leverage all of that fun new stuff.
- Returning MENU_NOT_FOUND or MENU_ACCESS_DENIED no longer works. The new correct way to handle those is to throw an exception, which the new ExceptionController will turn into the appropriate page response.
- The database system no longer throws PDOExceptions. Because of Symfony's FlattenException class, we cannot pass extra arbitrary public properties forward on the exception object to carry the query information. Instead, we ported the "the query that broke was" logic into the database layer directly, and took advantage of PHP 5.3's nested exception capability.
Note: Please *do not post new patches here*. The code is being worked on in the kernel branch of the WSCCI sandbox. New patches will be rolled from that. If you want to help writing code, contact Crell.
Follow up issues are being tagged kernel-followup. The following is a list that was started prior to that issue tag. Make sure these items are posted as issues and tagged appropriately.
- Integrate the kernel and its components with the dependency injection container.
- Replace current_path(), request_path(), and so forth with direct access to the request object, somehow. Or better, refactor enough code so that we don't need utility functions or the direct request object at all.
- Develop the new routing system to replace our current one, as discussed.
- Either extend or replace hook_menu() so that we can declare multiple router items at the same path for different mime types and HTTP methods. Since the router system and menu links system have no business being as intertwined as they are now, replacing hook_menu() entirely is a viable approach we will consider. (Possibly put it in CMI?)
- Replace the trivial access listener with something more flexible, powerful, and non-trivial. This should be considered as part of the previous two items.
- Revise the Ajax system to be more self-documenting and intuitive, and fit better with this new listener model.
- Eliminate our various "screw with the global request data" bootstrap routines, since we will not be screwing with it anymore.
- Lots of other things, but we'll get there. :-)
User interface changes
There shouldn't be any of note. This is all at the API level.
1) Delivery callbacks are gone in favor of kernel View events.
2) *Most* things are emulated fairly well at the moment, so most modules do not need to change. The big API changes will come in follow-ups. However, some more esoteric edge cases (returning NULL from a page handler, anywhere that we print and then call drupal_exit(), etc.) cannot be emulated so are being altered to simply return a response object.