Commit 3ef99f90 authored by neclimdul's avatar neclimdul

Convert to our own UrlMatcher

This is a transitional implementation but by using the UrlMatcherInterface
we can embed our matching logic directly into Symfony's routing logic.
parent 5ae2b932
......@@ -5,7 +5,7 @@
use Symfony\Component\Routing;
use Symfony\Component\HttpKernel;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Routing\Matcher\UrlMatcher as SymfonyUrlMatcher;
use Drupal\Core\UrlMatcher;
/**
......@@ -20,17 +20,13 @@
* the result to the caller (FALSE).
*/
function router_execute_active_handler($request) {
// Do some hand waving to setup the routing.
$routes = router_get_routes($request);
try {
// Resolve a routing context(path, etc) using the routes object to a
// Set a /routing/ context to translate
// @todo probably write our own route object and UrlMatcher to handle
// converting Drupal's db driven backend.
// Set a routing context to translate.
$context = new Routing\RequestContext();
$context->fromRequest($request);
$matcher = new SymfonyUrlMatcher($routes, $context);
$matcher = new UrlMatcher($context);
// Push path paramaters into attributes.
$request->attributes->add($matcher->match($request->getPathInfo()));
......@@ -53,84 +49,3 @@ function router_execute_active_handler($request) {
return $response;
}
/**
* Get a RouteCollection for resolving a request.
*
* Ok, so... we need a routing collection that's not this "dumb". Symfony's just
* is just a trivial implementation. It probably means we need our own
* DrupalRouteCollection which would actually wrap this logic, our menu router
* table, translating between it, and caching.
*/
function router_get_routes($request) {
// Rebuild if we know it's needed, or if the menu masks are missing which
// occurs rarely, likely due to a race condition of multiple rebuilds.
if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) {
menu_rebuild();
}
$routes = new Routing\RouteCollection();
$foo = db_query('SELECT path, page_callback, page_arguments FROM {menu_router}');
while ($router_item = $foo->fetchAssoc()) {
$route = array(
'path' => $router_item['path'],
'_controller' => $router_item['page_callback'],
);
if (0 !== strpos($route['path'], '/')) {
$route['path'] = '/' . $route['path'];
}
// Place argument defaults on the route.
foreach (unserialize($router_item['page_arguments']) as $k => $v) {
$route[$k] = $v;
}
$routes->add(hash('sha256', $route['path']), new Routing\Route($route['path'], $route));
}
return $routes;
// Since there is no limit to the length of $path, use a hash to keep it
// short yet unique.
$cid = 'menu_item:' . hash('sha256', $path);
if ($cached = cache('menu')->get($cid)) {
$router_item = $cached->data;
}
else {
$parts = array_slice($original_map, 0, MENU_MAX_PARTS);
$ancestors = menu_get_ancestors($parts);
$router_item = db_query_range('SELECT * FROM {menu_router} WHERE path IN (:ancestors) ORDER BY fit DESC', 0, 1, array(':ancestors' => $ancestors))->fetchAssoc();
cache('menu')->set($cid, $router_item);
}
$routes = new Routing\RouteCollection();
foreach (module_list() as $module) {
$items = module_invoke($module, 'menu');
foreach ($items as $path => $item) {
// Drupal doesn't prefix but if someone did we wouldn't want to double up.
if (0 !== strpos($path, '/')) {
$path = '/' . $path;
}
// Set base route array.
$route = array(
// A page callback could be a router. I'm not sure if the controller
// should actually be a thin layer on top or work like this yet.
'_controller' => $item['page callback'],
);
// Place argument defaults on the route.
foreach ($item['page arguments'] as $k => $v) {
$route[$k] = $v;
}
// @todo put other "menu" information somewhere.
$routes->add(hash('sha256', $path), new Routing\Route($path, $route));
}
}
return $routes;
}
<?php
namespace Drupal\Core;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Matcher\UrlMatcher as SymfonyUrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* UrlMatcher matches URL based on a set of routes.
*/
class UrlMatcher extends SymfonyUrlMatcher {
protected $context;
/**
* Constructor.
*
* @param RouteCollection $routes A RouteCollection instance
* @param RequestContext $context The context
*/
public function __construct(RequestContext $context) {
$this->context = $context;
}
/**
* {@inheritDoc}
*
* @api
*/
public function match($pathinfo) {
$this->allow = array();
// Symfony uses a prefixing / but we don't yet.
$dpathinfo = ltrim($pathinfo, '/');
// Do our fancy frontpage logic.
if (empty($dpathinfo)) {
$dpathinfo = variable_get('site_frontpage', 'user');
}
if ($router_item = $this->matchDrupalItem($dpathinfo)) {
$routes = new RouteCollection();
$routes->add(hash('sha256', $router_item['path']), $this->convertDrupalItem($router_item));
if ($ret = $this->matchCollection($pathinfo, $routes)) {
return $ret;
}
}
throw 0 < count($this->allow)
? new MethodNotAllowedException(array_unique(array_map('strtoupper', $this->allow)))
: new ResourceNotFoundException();
}
/**
* Get a drupal menu item.
*
* @param string $path
* The path being looked up by
*/
protected function matchDrupalItem($path) {
// For now we can just proxy our procedural method. At some point this will
// become more complicated because we'll need to get back candidates for a
// path and them resolve them based on things like method and scheme which
// we currently can't do.
return menu_get_item($path);
}
protected function convertDrupalItem($router_item) {
$route = array(
'_controller' => $router_item['page_callback']
);
// Place argument defaults on the route.
foreach ($router_item['page_arguments'] as $k => $v) {
$route[$k] = $v;
}
return new Route($router_item['path'], $route);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment