Commit abb51387 authored by Fabien Potencier's avatar Fabien Potencier

feature #971 [Routing] Replace the UrlMatcher by a RequestMatcher (GromNaN)

This PR was merged into the 2.0.x-dev branch.

Discussion
----------

[Routing] Replace the UrlMatcher by a RequestMatcher

Commits
-------

857023d1 [Routing] Replace the UrlMatcher by a RequestMatcher
parents 5775fdb0 857023d1
...@@ -13,30 +13,72 @@ namespace Silex\Provider\Locale; ...@@ -13,30 +13,72 @@ namespace Silex\Provider\Locale;
use Pimple\Container; use Pimple\Container;
use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\EventListener\LocaleListener as BaseLocaleListener; use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\RequestContextAwareInterface; use Symfony\Component\Routing\RequestContext;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/** /**
* Initializes the locale based on the current request. * Initializes the locale based on the current request.
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/ */
class LocaleListener extends BaseLocaleListener class LocaleListener implements EventSubscriberInterface
{ {
protected $app; private $app;
private $defaultLocale;
private $requestStack;
private $requestContext;
public function __construct(Container $app, RequestContextAwareInterface $router = null, RequestStack $requestStack = null) public function __construct(Container $app, $defaultLocale = 'en', RequestStack $requestStack, RequestContext $requestContext = null)
{ {
parent::__construct($app['locale'], $router, $requestStack);
$this->app = $app; $this->app = $app;
$this->defaultLocale = $defaultLocale;
$this->requestStack = $requestStack;
$this->requestContext = $requestContext;
} }
public function onKernelRequest(GetResponseEvent $event) public function onKernelRequest(GetResponseEvent $event)
{ {
parent::onKernelRequest($event); $request = $event->getRequest();
$request->setDefaultLocale($this->defaultLocale);
$this->setLocale($request);
$this->setRouterContext($request);
$this->app['locale'] = $event->getRequest()->getLocale(); $this->app['locale'] = $request->getLocale();
}
public function onKernelFinishRequest(FinishRequestEvent $event)
{
if (null !== $parentRequest = $this->requestStack->getParentRequest()) {
$this->setRouterContext($parentRequest);
}
}
private function setLocale(Request $request)
{
if ($locale = $request->attributes->get('_locale')) {
$request->setLocale($locale);
}
}
private function setRouterContext(Request $request)
{
if (null !== $this->requestContext) {
$this->requestContext->setParameter('_locale', $request->getLocale());
}
}
public static function getSubscribedEvents()
{
return array(
// must be registered after the Router to have access to the _locale
KernelEvents::REQUEST => array(array('onKernelRequest', 16)),
KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)),
);
} }
} }
...@@ -15,7 +15,6 @@ use Pimple\Container; ...@@ -15,7 +15,6 @@ use Pimple\Container;
use Pimple\ServiceProviderInterface; use Pimple\ServiceProviderInterface;
use Silex\Api\EventListenerProviderInterface; use Silex\Api\EventListenerProviderInterface;
use Silex\Provider\Locale\LocaleListener; use Silex\Provider\Locale\LocaleListener;
use Silex\Provider\Routing\LazyUrlMatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/** /**
...@@ -28,14 +27,7 @@ class LocaleServiceProvider implements ServiceProviderInterface, EventListenerPr ...@@ -28,14 +27,7 @@ class LocaleServiceProvider implements ServiceProviderInterface, EventListenerPr
public function register(Container $app) public function register(Container $app)
{ {
$app['locale.listener'] = function ($app) { $app['locale.listener'] = function ($app) {
$urlMatcher = null; return new LocaleListener($app, $app['locale'], $app['request_stack'], isset($app['request_context']) ? $app['request_context'] : null);
if (isset($app['url_matcher'])) {
$urlMatcher = new LazyUrlMatcher(function () use ($app) {
return $app['url_matcher'];
});
}
return new LocaleListener($app, $urlMatcher, $app['request_stack']);
}; };
$app['locale'] = 'en'; $app['locale'] = 'en';
......
...@@ -11,15 +11,16 @@ ...@@ -11,15 +11,16 @@
namespace Silex\Provider\Routing; namespace Silex\Provider\Routing;
use Symfony\Component\Routing\RequestContext as SymfonyRequestContext; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Matcher\UrlMatcherInterface; use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
/** /**
* Implements a lazy UrlMatcher. * Implements a lazy UrlMatcher.
* *
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/ */
class LazyUrlMatcher implements UrlMatcherInterface class LazyRequestMatcher implements RequestMatcherInterface
{ {
private $factory; private $factory;
...@@ -29,41 +30,25 @@ class LazyUrlMatcher implements UrlMatcherInterface ...@@ -29,41 +30,25 @@ class LazyUrlMatcher implements UrlMatcherInterface
} }
/** /**
* Returns the corresponding UrlMatcherInterface instance. * Returns the corresponding RequestMatcherInterface instance.
* *
* @return UrlMatcherInterface * @return UrlMatcherInterface
*/ */
public function getUrlMatcher() public function getRequestMatcher()
{ {
$urlMatcher = call_user_func($this->factory); $matcher = call_user_func($this->factory);
if (!$urlMatcher instanceof UrlMatcherInterface) { if (!$matcher instanceof RequestMatcherInterface) {
throw new \LogicException("Factory supplied to LazyUrlMatcher must return implementation of UrlMatcherInterface."); throw new \LogicException("Factory supplied to LazyRequestMatcher must return implementation of Symfony\Component\Routing\RequestMatcherInterface.");
} }
return $urlMatcher; return $matcher;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function match($pathinfo) public function matchRequest(Request $request)
{ {
return $this->getUrlMatcher()->match($pathinfo); return $this->getRequestMatcher()->matchRequest($request);
}
/**
* {@inheritdoc}
*/
public function setContext(SymfonyRequestContext $context)
{
$this->getUrlMatcher()->setContext($context);
}
/**
* {@inheritdoc}
*/
public function getContext()
{
return $this->getUrlMatcher()->getContext();
} }
} }
...@@ -15,7 +15,7 @@ use Pimple\Container; ...@@ -15,7 +15,7 @@ use Pimple\Container;
use Pimple\ServiceProviderInterface; use Pimple\ServiceProviderInterface;
use Silex\Api\EventListenerProviderInterface; use Silex\Api\EventListenerProviderInterface;
use Silex\Provider\Routing\RedirectableUrlMatcher; use Silex\Provider\Routing\RedirectableUrlMatcher;
use Silex\Provider\Routing\LazyUrlMatcher; use Silex\Provider\Routing\LazyRequestMatcher;
use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RequestContext;
use Symfony\Component\HttpKernel\EventListener\RouterListener; use Symfony\Component\HttpKernel\EventListener\RouterListener;
...@@ -34,7 +34,7 @@ class RoutingServiceProvider implements ServiceProviderInterface, EventListenerP ...@@ -34,7 +34,7 @@ class RoutingServiceProvider implements ServiceProviderInterface, EventListenerP
return new UrlGenerator($app['routes'], $app['request_context']); return new UrlGenerator($app['routes'], $app['request_context']);
}; };
$app['url_matcher'] = function () use ($app) { $app['request_matcher'] = function () use ($app) {
return new RedirectableUrlMatcher($app['routes'], $app['request_context']); return new RedirectableUrlMatcher($app['routes'], $app['request_context']);
}; };
...@@ -48,8 +48,8 @@ class RoutingServiceProvider implements ServiceProviderInterface, EventListenerP ...@@ -48,8 +48,8 @@ class RoutingServiceProvider implements ServiceProviderInterface, EventListenerP
}; };
$app['routing.listener'] = function () use ($app) { $app['routing.listener'] = function () use ($app) {
$urlMatcher = new LazyUrlMatcher(function () use ($app) { $urlMatcher = new LazyRequestMatcher(function () use ($app) {
return $app['url_matcher']; return $app['request_matcher'];
}); });
return new RouterListener($urlMatcher, $app['request_context'], $app['logger'], $app['request_stack']); return new RouterListener($urlMatcher, $app['request_context'], $app['logger'], $app['request_stack']);
......
...@@ -315,7 +315,7 @@ class SecurityServiceProvider implements ServiceProviderInterface, EventListener ...@@ -315,7 +315,7 @@ class SecurityServiceProvider implements ServiceProviderInterface, EventListener
}; };
$app['security.http_utils'] = function ($app) { $app['security.http_utils'] = function ($app) {
return new HttpUtils(isset($app['url_generator']) ? $app['url_generator'] : null, $app['url_matcher']); return new HttpUtils(isset($app['url_generator']) ? $app['url_generator'] : null, $app['request_matcher']);
}; };
$app['security.last_error'] = $app->protect(function (Request $request) { $app['security.last_error'] = $app->protect(function (Request $request) {
......
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Silex\Tests;
use Symfony\Component\HttpFoundation\Request;
use Silex\Provider\Routing\LazyRequestMatcher;
/**
* LazyRequestMatcher test case.
*
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
class LazyRequestMatcherTest extends \PHPUnit_Framework_TestCase
{
/**
* @covers Silex\LazyRequestMatcher::getResquestMatcher
*/
public function testUserMatcherIsCreatedLazily()
{
$callCounter = 0;
$requestMatcher = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface');
$matcher = new LazyRequestMatcher(function () use ($requestMatcher, &$callCounter) {
$callCounter++;
return $requestMatcher;
});
$this->assertEquals(0, $callCounter);
$request = Request::create('path');
$matcher->matchRequest($request);
$this->assertEquals(1, $callCounter);
}
/**
* @expectedException LogicException
* @expectedExceptionMessage Factory supplied to LazyRequestMatcher must return implementation of Symfony\Component\Routing\RequestMatcherInterface.
*/
public function testThatCanInjectResquestMatcherOnly()
{
$matcher = new LazyRequestMatcher(function () {
return 'someMatcher';
});
$request = Request::create('path');
$matcher->matchRequest($request);
}
/**
* @covers Silex\LazyRequestMatcher::matchRequest
*/
public function testMatchIsProxy()
{
$request = Request::create('path');
$matcher = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface');
$matcher->expects($this->once())
->method('matchRequest')
->with($request)
->will($this->returnValue('matcherReturnValue'));
$matcher = new LazyRequestMatcher(function () use ($matcher) {
return $matcher;
});
$result = $matcher->matchRequest($request);
$this->assertEquals('matcherReturnValue', $result);
}
}
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Silex\Tests;
use Silex\Provider\Routing\LazyUrlMatcher;
/**
* LazyUrlMatcher test case.
*
* @author Leszek Prabucki <leszek.prabucki@gmail.com>
*/
class LazyUrlMatcherTest extends \PHPUnit_Framework_TestCase
{
/**
* @covers Silex\LazyUrlMatcher::getUrlMatcher
*/
public function testUserMatcherIsCreatedLazily()
{
$callCounter = 0;
$urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
$matcher = new LazyUrlMatcher(function () use ($urlMatcher, &$callCounter) {
$callCounter++;
return $urlMatcher;
});
$this->assertEquals(0, $callCounter);
$matcher->match('path');
$this->assertEquals(1, $callCounter);
}
/**
* @expectedException LogicException
* @expectedExceptionMessage Factory supplied to LazyUrlMatcher must return implementation of UrlMatcherInterface.
*/
public function testThatCanInjectUrlMatcherOnly()
{
$matcher = new LazyUrlMatcher(function () {
return 'someMatcher';
});
$matcher->match('path');
}
/**
* @covers Silex\LazyUrlMatcher::match
*/
public function testMatchIsProxy()
{
$urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
$urlMatcher->expects($this->once())
->method('match')
->with('path')
->will($this->returnValue('matcherReturnValue'));
$matcher = new LazyUrlMatcher(function () use ($urlMatcher) {
return $urlMatcher;
});
$result = $matcher->match('path');
$this->assertEquals('matcherReturnValue', $result);
}
/**
* @covers Silex\LazyUrlMatcher::setContext
*/
public function testSetContextIsProxy()
{
$context = $this->getMock('Symfony\Component\Routing\RequestContext');
$urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
$urlMatcher->expects($this->once())
->method('setContext')
->with($context);
$matcher = new LazyUrlMatcher(function () use ($urlMatcher) {
return $urlMatcher;
});
$matcher->setContext($context);
}
/**
* @covers Silex\LazyUrlMatcher::getContext
*/
public function testGetContextIsProxy()
{
$context = $this->getMock('Symfony\Component\Routing\RequestContext');
$urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
$urlMatcher->expects($this->once())
->method('getContext')
->will($this->returnValue($context));
$matcher = new LazyUrlMatcher(function () use ($urlMatcher) {
return $urlMatcher;
});
$resultContext = $matcher->getContext();
$this->assertSame($resultContext, $context);
}
}
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