Add a basic framework for stackable partial matching.
parent
52fd27522b
commit
b0f90a1046
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Routing;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* This class filters routes based on their HTTP Method.
|
||||
*/
|
||||
class HttpMethodMatcher implements PartialMatcherInterface {
|
||||
|
||||
protected $routes;
|
||||
|
||||
public function __construct(RouteCollection $routes) {
|
||||
$this->routes = $routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a request against multiple routes.
|
||||
*
|
||||
* @param Request $request
|
||||
* A Request object against which to match.
|
||||
*
|
||||
* @return RouteCollection
|
||||
* A RouteCollection of matched routes.
|
||||
*/
|
||||
public function matchByRequest(Request $request) {
|
||||
|
||||
$method = $request->getMethod();
|
||||
|
||||
$collection = new RouteCollection();
|
||||
|
||||
foreach ($this->routes->all() as $name => $route) {
|
||||
$allowed_methods = $route->getRequirement('_method');
|
||||
if ($allowed_methods === NULL || in_array($method, explode('|', strtoupper($allowed_methods)))) {
|
||||
$collection->add($name, $route);
|
||||
}
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Routing;
|
||||
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
||||
|
||||
/**
|
||||
* A NestedMatcher allows for multiple-stage resolution of a route.
|
||||
*/
|
||||
interface NestedMatcherInterface extends UrlMatcherInterface {
|
||||
|
||||
/**
|
||||
* Adds a partial matcher to the matching plan.
|
||||
*
|
||||
* Partial matchers will be run in the order in which they are added.
|
||||
*
|
||||
* @param PartialMatcherInterface $matcher
|
||||
* A partial
|
||||
*
|
||||
* @return NestedMatcherInterface
|
||||
* The current matcher.
|
||||
*/
|
||||
public function addPartialMatcher(PartialMatcherInterface $matcher);
|
||||
|
||||
/**
|
||||
* Sets the final matcher for the matching plan.
|
||||
*
|
||||
* @param UrlMatcherInterface $final
|
||||
* The matcher that will be called last to ensure only a single route is
|
||||
* found.
|
||||
*
|
||||
* @return NestedMatcherInterface
|
||||
* The current matcher.
|
||||
*/
|
||||
public function setFinalMatcher(UrlMatcherInterface $final);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Routing;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* A PartialMatcher works like a UrlMatcher, but will return multiple candidate routes.
|
||||
*/
|
||||
interface PartialMatcherInterface {
|
||||
|
||||
/**
|
||||
* Matches a request against multiple routes.
|
||||
*
|
||||
* @param Request $request
|
||||
* A Request object against which to match.
|
||||
*
|
||||
* @return RouteCollection
|
||||
* A RouteCollection of matched routes.
|
||||
*/
|
||||
public function matchByRequest(Request $request);
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\Core\Routing\UrlMatcher.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Routing;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* UrlMatcher matches URL based on a set of routes.
|
||||
*/
|
||||
class UrlMatcher implements UrlMatcherInterface {
|
||||
|
||||
/**
|
||||
* The request context for this matcher.
|
||||
*
|
||||
* @var Symfony\Component\Routing\RequestContext
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* The request object for this matcher.
|
||||
*
|
||||
* @var Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
// We will not actually use this object, but it's needed to conform to
|
||||
// the interface.
|
||||
$this->context = new RequestContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the request context.
|
||||
*
|
||||
* This method is just to satisfy the interface, and is largely vestigial.
|
||||
* The request context object does not contain the information we need, so
|
||||
* we will use the original request object.
|
||||
*
|
||||
* @param Symfony\Component\Routing\RequestContext $context
|
||||
* The context.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function setContext(RequestContext $context) {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request context.
|
||||
*
|
||||
* This method is just to satisfy the interface, and is largely vestigial.
|
||||
* The request context object does not contain the information we need, so
|
||||
* we will use the original request object.
|
||||
*
|
||||
* @return Symfony\Component\Routing\RequestContext
|
||||
* The context.
|
||||
*/
|
||||
public function getContext() {
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the request object to use.
|
||||
*
|
||||
* This is used by the RouterListener to make additional request attributes
|
||||
* available.
|
||||
*
|
||||
* @param Symfony\Component\HttpFoundation\Request $request
|
||||
* The request object.
|
||||
*/
|
||||
public function setRequest(Request $request) {
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request object.
|
||||
*
|
||||
* @return Symfony\Component\HttpFoundation\Request $request
|
||||
* The request object.
|
||||
*/
|
||||
public function getRequest() {
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function match($pathinfo) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -64,12 +64,12 @@ class UrlMatcherDumper implements MatcherDumperInterface {
|
|||
*
|
||||
* Available options:
|
||||
*
|
||||
* * class: The class name
|
||||
* * route_set: The route grouping that is being dumped. All existing
|
||||
* routes with this route set will be deleted on dump.
|
||||
* * base_class: The base class name
|
||||
*
|
||||
* @param array $options An array of options
|
||||
*
|
||||
* @return string A PHP class representing the matcher class
|
||||
* @param $options array
|
||||
* $options An array of options
|
||||
*/
|
||||
public function dump(array $options = array()) {
|
||||
$options += array(
|
||||
|
@ -88,8 +88,6 @@ class UrlMatcherDumper implements MatcherDumperInterface {
|
|||
'route',
|
||||
));
|
||||
|
||||
|
||||
|
||||
foreach ($this->routes as $name => $route) {
|
||||
$compiled = $route->compile();
|
||||
$values = array(
|
||||
|
@ -115,13 +113,14 @@ class UrlMatcherDumper implements MatcherDumperInterface {
|
|||
$insert->execute();
|
||||
|
||||
// Transaction ends here.
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the routes to match.
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
* @return RouteCollection
|
||||
* A RouteCollection instance representing all routes currently in the
|
||||
* dumper.
|
||||
*/
|
||||
public function getRoutes() {
|
||||
return $this->routes;
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\system\Tests\Routing\PartialMatcherTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Routing;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
use Drupal\simpletest\UnitTestBase;
|
||||
use Drupal\Core\Routing\HttpMethodMatcher;
|
||||
|
||||
/**
|
||||
* Basic tests for the UrlMatcherDumper.
|
||||
*/
|
||||
class PartialMatcherTest extends UnitTestBase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Partial matcher HTTP Method tests',
|
||||
'description' => 'Confirm that the Http Method partial matcher is functioning properly.',
|
||||
'group' => 'Routing',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the HttpMethod matcher matches properly.
|
||||
*/
|
||||
function testFilterRoutes() {
|
||||
$collection = new RouteCollection();
|
||||
|
||||
$route = new Route('path/one');
|
||||
$route->setRequirement('_method', 'GET');
|
||||
$collection->add('route_a', $route);
|
||||
|
||||
$route = new Route('path/one');
|
||||
$route->setRequirement('_method', 'PUT');
|
||||
$collection->add('route_b', $route);
|
||||
|
||||
$route = new Route('path/two');
|
||||
$route->setRequirement('_method', 'GET');
|
||||
$collection->add('route_c', $route);
|
||||
|
||||
$route = new Route('path/three');
|
||||
$collection->add('route_d', $route);
|
||||
|
||||
$route = new Route('path/two');
|
||||
$route->setRequirement('_method', 'GET|HEAD');
|
||||
$collection->add('route_e', $route);
|
||||
|
||||
$matcher = new HttpMethodMatcher($collection, 'GET');
|
||||
|
||||
$routes = $matcher->matchByRequest(Request::create('path/one', 'GET'));
|
||||
|
||||
$this->assertEqual(count($routes->all()), 4, t('The correct number of routes was found.'));
|
||||
$this->assertNotNull($routes->get('route_a'), t('The first matching route was found.'));
|
||||
$this->assertNull($routes->get('route_b'), t('The non-matching route was not found.'));
|
||||
$this->assertNotNull($routes->get('route_c'), t('The second matching route was found.'));
|
||||
$this->assertNotNull($routes->get('route_d'), t('The all-matching route was found.'));
|
||||
$this->assertNotNull($routes->get('route_e'), t('The multi-matching route was found.'));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue