From bbef65e657212d3fc1daa2f812e66aa2bbbcff64 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 9 Jun 2012 16:43:04 +0200 Subject: [PATCH] [Routing] Add strict_parameters option to disable exceptions when a route generation fails due to an invalid parameter --- .../DependencyInjection/Configuration.php | 1 + .../FrameworkExtension.php | 5 ++- .../Resources/config/routing.xml | 2 + .../Generator/Dumper/PhpGeneratorDumper.php | 4 +- .../Routing/Generator/UrlGenerator.php | 40 ++++++++++++++++- src/Symfony/Component/Routing/Router.php | 43 ++++++++++++------- .../Tests/Generator/UrlGeneratorTest.php | 23 +++++++++- src/Symfony/Component/Routing/composer.json | 1 + 8 files changed, 96 insertions(+), 23 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index ece30cda49..92c84f0a5c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -155,6 +155,7 @@ class Configuration implements ConfigurationInterface ->scalarNode('type')->end() ->scalarNode('http_port')->defaultValue(80)->end() ->scalarNode('https_port')->defaultValue(443)->end() + ->scalarNode('strict_parameters')->defaultTrue()->end() ->end() ->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 8c397b6cbe..11d68401b6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -255,11 +255,12 @@ class FrameworkExtension extends Extension $container->setParameter('router.resource', $config['resource']); $router = $container->findDefinition('router.default'); + $argument = $router->getArgument(2); + $argument['strict_parameters'] = $config['strict_parameters']; if (isset($config['type'])) { - $argument = $router->getArgument(2); $argument['resource_type'] = $config['type']; - $router->replaceArgument(2, $argument); } + $router->replaceArgument(2, $argument); $container->setParameter('request_listener.http_port', $config['http_port']); $container->setParameter('request_listener.https_port', $config['https_port']); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index c5bb77bb3c..78aedcc0ca 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -50,6 +50,7 @@ + %router.resource% @@ -65,6 +66,7 @@ %router.options.matcher.cache_class% + diff --git a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php index 03a0903554..fad8ceaac0 100644 --- a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php +++ b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php @@ -49,6 +49,7 @@ class PhpGeneratorDumper extends GeneratorDumper use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Exception\RouteNotFoundException; +use Symfony\Component\HttpKernel\Log\LoggerInterface; /** * {$options['class']} @@ -63,9 +64,10 @@ class {$options['class']} extends {$options['base_class']} /** * Constructor. */ - public function __construct(RequestContext \$context) + public function __construct(RequestContext \$context, LoggerInterface \$logger = null) { \$this->context = \$context; + \$this->logger = \$logger; } {$this->generateGenerateMethod()} diff --git a/src/Symfony/Component/Routing/Generator/UrlGenerator.php b/src/Symfony/Component/Routing/Generator/UrlGenerator.php index 52ce63531e..68da4b7195 100644 --- a/src/Symfony/Component/Routing/Generator/UrlGenerator.php +++ b/src/Symfony/Component/Routing/Generator/UrlGenerator.php @@ -17,6 +17,7 @@ use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Exception\InvalidParameterException; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; +use Symfony\Component\HttpKernel\Log\LoggerInterface; /** * UrlGenerator generates a URL based on a set of routes. @@ -28,6 +29,8 @@ use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; class UrlGenerator implements UrlGeneratorInterface { protected $context; + protected $strictParameters = true; + protected $logger; protected $decodedChars = array( // %2F is not valid in a URL, so we don't encode it (which is fine as the requirements explicitly allowed it) '%2F' => '/', @@ -40,13 +43,15 @@ class UrlGenerator implements UrlGeneratorInterface * * @param RouteCollection $routes A RouteCollection instance * @param RequestContext $context The context + * @param LoggerInterface $logger A logger instance * * @api */ - public function __construct(RouteCollection $routes, RequestContext $context) + public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null) { $this->routes = $routes; $this->context = $context; + $this->logger = $logger; } /** @@ -71,6 +76,28 @@ class UrlGenerator implements UrlGeneratorInterface return $this->context; } + /** + * Enables or disables the exception on incorrect parameters. + * + * @param Boolean $enabled + * + * @api + */ + public function setStrictParameters($enabled) + { + $this->strictParameters = $enabled; + } + + /** + * Gets the strict check of incorrect parameters. + * + * @return Boolean + */ + public function getStrictParameters() + { + return $this->strictParameters; + } + /** * {@inheritDoc} * @@ -113,7 +140,16 @@ class UrlGenerator implements UrlGeneratorInterface if (!$isEmpty = in_array($tparams[$token[3]], array(null, '', false), true)) { // check requirement if ($tparams[$token[3]] && !preg_match('#^'.$token[2].'$#', $tparams[$token[3]])) { - throw new InvalidParameterException(sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $token[2], $tparams[$token[3]])); + $message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $token[2], $tparams[$token[3]]); + if ($this->strictParameters) { + throw new InvalidParameterException($message); + } + + if ($this->logger) { + $this->logger->err($message); + } + + return null; } } diff --git a/src/Symfony/Component/Routing/Router.php b/src/Symfony/Component/Routing/Router.php index bf4d61fc5b..80b5cea0ea 100644 --- a/src/Symfony/Component/Routing/Router.php +++ b/src/Symfony/Component/Routing/Router.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Routing; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\ConfigCache; +use Symfony\Component\HttpKernel\Log\LoggerInterface; /** * The Router class is an example of the integration of all pieces of the @@ -29,6 +30,7 @@ class Router implements RouterInterface protected $collection; protected $resource; protected $options; + protected $logger; /** * Constructor. @@ -37,11 +39,13 @@ class Router implements RouterInterface * @param mixed $resource The main resource to load * @param array $options An array of options * @param RequestContext $context The context + * @param LoggerInterface $logger A logger instance */ - public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null) + public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null, LoggerInterface $logger = null) { $this->loader = $loader; $this->resource = $resource; + $this->logger = $logger; $this->context = null === $context ? new RequestContext() : $context; $this->setOptions($options); } @@ -73,6 +77,7 @@ class Router implements RouterInterface 'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper', 'matcher_cache_class' => 'ProjectUrlMatcher', 'resource_type' => null, + 'strict_parameters' => true, ); // check option names and live merge, if errors are encountered Exception will be thrown @@ -225,24 +230,30 @@ class Router implements RouterInterface } if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) { - return $this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context); + $this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->logger); + } else { + $class = $this->options['generator_cache_class']; + $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']); + if (!$cache->isFresh($class)) { + $dumper = new $this->options['generator_dumper_class']($this->getRouteCollection()); + + $options = array( + 'class' => $class, + 'base_class' => $this->options['generator_base_class'], + ); + + $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources()); + } + + require_once $cache; + + $this->generator = new $class($this->context, $this->logger); } - $class = $this->options['generator_cache_class']; - $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']); - if (!$cache->isFresh($class)) { - $dumper = new $this->options['generator_dumper_class']($this->getRouteCollection()); - - $options = array( - 'class' => $class, - 'base_class' => $this->options['generator_base_class'], - ); - - $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources()); + if (false === $this->options['strict_parameters']) { + $this->generator->setStrictParameters(false); } - require_once $cache; - - return $this->generator = new $class($this->context); + return $this->generator; } } diff --git a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php index e4834e5511..f6a094e562 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php @@ -165,6 +165,25 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), true); } + public function testGenerateForRouteWithInvalidOptionalParameterNonStrict() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+'))); + $generator = $this->getGenerator($routes); + $generator->setStrictParameters(false); + $this->assertNull($generator->generate('test', array('foo' => 'bar'), true)); + } + + public function testGenerateForRouteWithInvalidOptionalParameterNonStrictWithLogger() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+'))); + $logger = $this->getMock('Symfony\Component\HttpKernel\Log\LoggerInterface'); + $logger->expects($this->once()) + ->method('err'); + $generator = $this->getGenerator($routes, array(), $logger); + $generator->setStrictParameters(false); + $this->assertNull($generator->generate('test', array('foo' => 'bar'), true)); + } + /** * @expectedException Symfony\Component\Routing\Exception\InvalidParameterException */ @@ -206,14 +225,14 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase $this->assertEquals('/app.php/foo', $this->getGenerator($routes)->generate('test', array('default' => 'foo'))); } - protected function getGenerator(RouteCollection $routes, array $parameters = array()) + protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null) { $context = new RequestContext('/app.php'); foreach ($parameters as $key => $value) { $method = 'set'.$key; $context->$method($value); } - $generator = new UrlGenerator($routes, $context); + $generator = new UrlGenerator($routes, $context, $logger); return $generator; } diff --git a/src/Symfony/Component/Routing/composer.json b/src/Symfony/Component/Routing/composer.json index 388a0c643d..44869a3695 100644 --- a/src/Symfony/Component/Routing/composer.json +++ b/src/Symfony/Component/Routing/composer.json @@ -21,6 +21,7 @@ "require-dev": { "symfony/config": "2.1.*", "symfony/yaml": "2.1.*", + "symfony/http-kernel": "2.1.*", "doctrine/common": ">=2.2,<2.4-dev" }, "suggest": {