diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index 0eaa6e844d..c15f6d2014 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -102,7 +102,7 @@ EOF; */ private function getProxyClassName(Definition $definition) { - return preg_replace('/^.*\\\\/', '', $definition->getClass()).'_'.substr(hash('sha256', spl_object_hash($definition).$this->salt), -7); + return preg_replace('/^.*\\\\/', '', $definition->getClass()).'_'.substr(hash('sha256', $definition->getClass().$this->salt), -7); } /** diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php index d3822ee777..d8cbde1017 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php @@ -29,7 +29,7 @@ class FormExtensionBootstrap4LayoutTest extends AbstractBootstrap4LayoutTest { use RuntimeLoaderProvider; /** - * @var FormRenderer; + * @var FormRenderer */ private $renderer; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index 03fb20c441..7b87fcf896 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -113,11 +113,11 @@ class JsonDescriptor extends Descriptor $service = $this->resolveServiceDefinition($builder, $serviceId); if ($service instanceof Alias) { - if ($showPrivate || $service->isPublic()) { + if ($showPrivate || ($service->isPublic() && !$service->isPrivate())) { $data['aliases'][$serviceId] = $this->getContainerAliasData($service); } } elseif ($service instanceof Definition) { - if (($showPrivate || $service->isPublic())) { + if (($showPrivate || ($service->isPublic() && !$service->isPrivate()))) { $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments); } } else { @@ -211,7 +211,7 @@ class JsonDescriptor extends Descriptor { $data = array( 'class' => (string) $definition->getClass(), - 'public' => $definition->isPublic(), + 'public' => $definition->isPublic() && !$definition->isPrivate(), 'synthetic' => $definition->isSynthetic(), 'lazy' => $definition->isLazy(), 'shared' => $definition->isShared(), @@ -265,7 +265,7 @@ class JsonDescriptor extends Descriptor { return array( 'service' => (string) $alias, - 'public' => $alias->isPublic(), + 'public' => $alias->isPublic() && !$alias->isPrivate(), ); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index 2efee2ed1d..8836cc234b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -139,11 +139,11 @@ class MarkdownDescriptor extends Descriptor $service = $this->resolveServiceDefinition($builder, $serviceId); if ($service instanceof Alias) { - if ($showPrivate || $service->isPublic()) { + if ($showPrivate || ($service->isPublic() && !$service->isPrivate())) { $services['aliases'][$serviceId] = $service; } } elseif ($service instanceof Definition) { - if (($showPrivate || $service->isPublic())) { + if (($showPrivate || ($service->isPublic() && !$service->isPrivate()))) { $services['definitions'][$serviceId] = $service; } } else { @@ -182,7 +182,7 @@ class MarkdownDescriptor extends Descriptor protected function describeContainerDefinition(Definition $definition, array $options = array()) { $output = '- Class: `'.$definition->getClass().'`' - ."\n".'- Public: '.($definition->isPublic() ? 'yes' : 'no') + ."\n".'- Public: '.($definition->isPublic() && !$definition->isPrivate() ? 'yes' : 'no') ."\n".'- Synthetic: '.($definition->isSynthetic() ? 'yes' : 'no') ."\n".'- Lazy: '.($definition->isLazy() ? 'yes' : 'no') ."\n".'- Shared: '.($definition->isShared() ? 'yes' : 'no') @@ -239,7 +239,7 @@ class MarkdownDescriptor extends Descriptor protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null) { $output = '- Service: `'.$alias.'`' - ."\n".'- Public: '.($alias->isPublic() ? 'yes' : 'no'); + ."\n".'- Public: '.($alias->isPublic() && !$alias->isPrivate() ? 'yes' : 'no'); if (!isset($options['id'])) { return $this->write($output); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 63513bfa43..e4d4d1ea20 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -194,7 +194,7 @@ class TextDescriptor extends Descriptor $definition = $this->resolveServiceDefinition($builder, $serviceId); if ($definition instanceof Definition) { // filter out private services unless shown explicitly - if (!$showPrivate && !$definition->isPublic()) { + if (!$showPrivate && (!$definition->isPublic() || $definition->isPrivate())) { unset($serviceIds[$key]); continue; } @@ -212,7 +212,7 @@ class TextDescriptor extends Descriptor } } } elseif ($definition instanceof Alias) { - if (!$showPrivate && !$definition->isPublic()) { + if (!$showPrivate && (!$definition->isPublic() || $definition->isPrivate())) { unset($serviceIds[$key]); continue; } @@ -302,7 +302,7 @@ class TextDescriptor extends Descriptor $tableRows[] = array('Calls', implode(', ', $callInformation)); } - $tableRows[] = array('Public', $definition->isPublic() ? 'yes' : 'no'); + $tableRows[] = array('Public', $definition->isPublic() && !$definition->isPrivate() ? 'yes' : 'no'); $tableRows[] = array('Synthetic', $definition->isSynthetic() ? 'yes' : 'no'); $tableRows[] = array('Lazy', $definition->isLazy() ? 'yes' : 'no'); $tableRows[] = array('Shared', $definition->isShared() ? 'yes' : 'no'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index 37af3802be..1e3148db9a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -283,7 +283,7 @@ class XmlDescriptor extends Descriptor foreach ($this->sortServiceIds($serviceIds) as $serviceId) { $service = $this->resolveServiceDefinition($builder, $serviceId); - if (($service instanceof Definition || $service instanceof Alias) && !($showPrivate || $service->isPublic())) { + if (($service instanceof Definition || $service instanceof Alias) && !($showPrivate || ($service->isPublic() && !$service->isPrivate()))) { continue; } @@ -322,7 +322,7 @@ class XmlDescriptor extends Descriptor } } - $serviceXML->setAttribute('public', $definition->isPublic() ? 'true' : 'false'); + $serviceXML->setAttribute('public', $definition->isPublic() && !$definition->isPrivate() ? 'true' : 'false'); $serviceXML->setAttribute('synthetic', $definition->isSynthetic() ? 'true' : 'false'); $serviceXML->setAttribute('lazy', $definition->isLazy() ? 'true' : 'false'); $serviceXML->setAttribute('shared', $definition->isShared() ? 'true' : 'false'); @@ -421,7 +421,7 @@ class XmlDescriptor extends Descriptor } $aliasXML->setAttribute('service', (string) $alias); - $aliasXML->setAttribute('public', $alias->isPublic() ? 'true' : 'false'); + $aliasXML->setAttribute('public', $alias->isPublic() && !$alias->isPrivate() ? 'true' : 'false'); return $dom; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json index e2ab628937..df76274db6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.json @@ -17,7 +17,7 @@ "%parameter%", { "class": "inline_service", - "public": true, + "public": false, "synthetic": false, "lazy": false, "shared": true, @@ -39,7 +39,7 @@ }, { "class": "inline_service", - "public": true, + "public": false, "synthetic": false, "lazy": false, "shared": true, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml index b016ae382a..59811b00d3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml @@ -6,7 +6,7 @@ %parameter% - + arg1 arg2 @@ -15,7 +15,7 @@ foo - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json index 5074d4fba6..2568b87dec 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.json @@ -15,7 +15,7 @@ "%parameter%", { "class": "inline_service", - "public": true, + "public": false, "synthetic": false, "lazy": false, "shared": true, @@ -37,7 +37,7 @@ }, { "class": "inline_service", - "public": true, + "public": false, "synthetic": false, "lazy": false, "shared": true, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml index 732f99f783..e250060d75 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml @@ -4,7 +4,7 @@ %parameter% - + arg1 arg2 @@ -13,7 +13,7 @@ foo - + diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 0a13ac1e66..f9c80d041b 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -140,7 +140,7 @@ class DebugClassLoader if ($this->isFinder && !isset($this->loaded[$class])) { $this->loaded[$class] = true; if ($file = $this->classLoader[0]->findFile($class) ?: false) { - $wasCached = \function_exists('opcache_is_script_cached') && opcache_is_script_cached($file); + $wasCached = \function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file); require $file; diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 047883a70c..00149b38e5 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -133,9 +133,20 @@ class ErrorHandler } if (!$replace && $prev) { restore_error_handler(); + $handlerIsRegistered = is_array($prev) && $handler === $prev[0]; + } else { + $handlerIsRegistered = true; } - if (is_array($prev = set_exception_handler(array($handler, 'handleException'))) && $prev[0] === $handler) { + if (is_array($prev = set_exception_handler(array($handler, 'handleException'))) && $prev[0] instanceof self) { restore_exception_handler(); + if (!$handlerIsRegistered) { + $handler = $prev[0]; + } elseif ($handler !== $prev[0] && $replace) { + set_exception_handler(array($handler, 'handleException')); + $p = $prev[0]->setExceptionHandler(null); + $handler->setExceptionHandler($p); + $prev[0]->setExceptionHandler($p); + } } else { $handler->setExceptionHandler($prev); } diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index 4b7bcc3cdd..0d57204481 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -35,7 +35,7 @@ class ErrorHandlerTest extends TestCase $newHandler = new ErrorHandler(); - $this->assertSame($newHandler, ErrorHandler::register($newHandler, false)); + $this->assertSame($handler, ErrorHandler::register($newHandler, false)); $h = set_error_handler('var_dump'); restore_error_handler(); $this->assertSame(array($handler, 'handleError'), $h); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index b7f1efdaca..f500ecccac 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -399,6 +399,8 @@ EOTXT if (!$proxyDumper->isProxyCandidate($definition)) { continue; } + // register class' reflector for resource tracking + $this->container->getReflectionClass($definition->getClass()); $proxyCode = "\n".$proxyDumper->getProxyCode($definition); if ($strip) { $proxyCode = "getForm(); $data = $event->getData(); + // Since the type always use mapper an empty array will not be + // considered as empty in Form::submit(), we need to evaluate + // empty data here so its value is submitted to sub forms if (null === $data) { $emptyData = $form->getConfig()->getEmptyData(); - - if (false === FormUtil::isEmpty($emptyData) && array() !== $emptyData) { - $data = is_callable($emptyData) ? call_user_func($emptyData, $form, $data) : $emptyData; - } + $data = $emptyData instanceof \Closure ? $emptyData($form, $data) : $emptyData; } // Convert the submitted data to a string, if scalar, before diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index 3873257829..36836378ae 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -27,10 +27,10 @@ class FileType extends AbstractType */ public function buildForm(FormBuilderInterface $builder, array $options) { + // Ensure that submitted data is always an uploaded file or an array of some $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) { $form = $event->getForm(); $requestHandler = $form->getConfig()->getRequestHandler(); - $data = null; if ($options['multiple']) { $data = array(); @@ -46,19 +46,16 @@ class FileType extends AbstractType } } - // submitted data for an input file (not required) without choosing any file - if (array(null) === $data || array() === $data) { + // Since the array is never considered empty in the view data format + // on submission, we need to evaluate the configured empty data here + if (array() === $data) { $emptyData = $form->getConfig()->getEmptyData(); - - $data = is_callable($emptyData) ? call_user_func($emptyData, $form, $data) : $emptyData; + $data = $emptyData instanceof \Closure ? $emptyData($form, $data) : $emptyData; } $event->setData($data); } elseif (!$requestHandler->isFileUpload($event->getData())) { - $emptyData = $form->getConfig()->getEmptyData(); - - $data = is_callable($emptyData) ? call_user_func($emptyData, $form, $data) : $emptyData; - $event->setData($data); + $event->setData(null); } }); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index 192d38f1a9..5ae3c53689 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -598,6 +598,20 @@ class ChoiceTypeTest extends BaseTypeTest $this->assertSame('test', $form->getData()); } + public function testSubmitSingleChoiceWithEmptyDataAndInitialData() + { + $form = $this->factory->create(static::TESTED_TYPE, 'initial', array( + 'multiple' => false, + 'expanded' => false, + 'choices' => array('initial', 'test'), + 'empty_data' => 'test', + )); + + $form->submit(null); + + $this->assertSame('test', $form->getData()); + } + public function testSubmitMultipleChoiceWithEmptyData() { $form = $this->factory->create(static::TESTED_TYPE, null, array( @@ -612,6 +626,34 @@ class ChoiceTypeTest extends BaseTypeTest $this->assertSame(array('test'), $form->getData()); } + public function testSubmitMultipleChoiceWithEmptyDataAndInitialEmptyArray() + { + $form = $this->factory->create(static::TESTED_TYPE, array(), array( + 'multiple' => true, + 'expanded' => false, + 'choices' => array('test'), + 'empty_data' => array('test'), + )); + + $form->submit(null); + + $this->assertSame(array('test'), $form->getData()); + } + + public function testSubmitMultipleChoiceWithEmptyDataAndInitialData() + { + $form = $this->factory->create(static::TESTED_TYPE, array('initial'), array( + 'multiple' => true, + 'expanded' => false, + 'choices' => array('initial', 'test'), + 'empty_data' => array('test'), + )); + + $form->submit(null); + + $this->assertSame(array('test'), $form->getData()); + } + public function testSubmitSingleChoiceExpandedWithEmptyData() { $form = $this->factory->create(static::TESTED_TYPE, null, array( @@ -626,6 +668,20 @@ class ChoiceTypeTest extends BaseTypeTest $this->assertSame('test', $form->getData()); } + public function testSubmitSingleChoiceExpandedWithEmptyDataAndInitialData() + { + $form = $this->factory->create(static::TESTED_TYPE, 'initial', array( + 'multiple' => false, + 'expanded' => true, + 'choices' => array('initial', 'test'), + 'empty_data' => 'test', + )); + + $form->submit(null); + + $this->assertSame('test', $form->getData()); + } + public function testSubmitMultipleChoiceExpandedWithEmptyData() { $form = $this->factory->create(static::TESTED_TYPE, null, array( @@ -640,6 +696,49 @@ class ChoiceTypeTest extends BaseTypeTest $this->assertSame(array('test'), $form->getData()); } + public function testSubmitMultipleChoiceExpandedWithEmptyDataAndInitialEmptyArray() + { + $form = $this->factory->create(static::TESTED_TYPE, array(), array( + 'multiple' => true, + 'expanded' => true, + 'choices' => array('test'), + 'empty_data' => array('test'), + )); + + $form->submit(null); + + $this->assertSame(array('test'), $form->getData()); + } + + public function testSubmitMultipleChoiceExpandedWithEmptyDataAndInitialData() + { + $form = $this->factory->create(static::TESTED_TYPE, array('init'), array( + 'multiple' => true, + 'expanded' => true, + 'choices' => array('init', 'test'), + 'empty_data' => array('test'), + )); + + $form->submit(null); + + $this->assertSame(array('test'), $form->getData()); + } + + /** + * @group legacy + */ + public function testLegacyNullChoices() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => null, + )); + $this->assertNull($form->getConfig()->getOption('choices')); + $this->assertFalse($form->getConfig()->getOption('multiple')); + $this->assertFalse($form->getConfig()->getOption('expanded')); + } + public function testSubmitMultipleNonExpanded() { $form = $this->factory->create(static::TESTED_TYPE, null, array( diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 7cd9c345ba..e003cd4095 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -629,7 +629,6 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl foreach ($this->bundles as $bundle) { if ($extension = $bundle->getContainerExtension()) { $container->registerExtension($extension); - $extensions[] = $extension->getAlias(); } if ($this->debug) { @@ -643,6 +642,10 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl $this->build($container); + foreach ($container->getExtensions() as $extension) { + $extensions[] = $extension->getAlias(); + } + // ensure these extensions are implicitly loaded $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions)); } @@ -681,7 +684,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl $dumper = new PhpDumper($container); if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper')) { - $dumper->setProxyDumper(new ProxyDumper(substr(hash('sha256', $cache->getPath()), 0, 7))); + $dumper->setProxyDumper(new ProxyDumper()); } $content = $dumper->dump(array( diff --git a/src/Symfony/Component/Intl/README.md b/src/Symfony/Component/Intl/README.md index 806c6275e8..9a9cb9b456 100644 --- a/src/Symfony/Component/Intl/README.md +++ b/src/Symfony/Component/Intl/README.md @@ -5,7 +5,7 @@ A PHP replacement layer for the C intl extension that also provides access to the localization data of the ICU library. The replacement layer is limited to the locale "en". If you want to use other -locales, you should [install the intl PHP extension] [0] instead. +locales, you should [install the intl PHP extension][0] instead. Resources --------- diff --git a/src/Symfony/Component/Lock/Tests/Store/AbstractStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/AbstractStoreTest.php index 630ba743cc..2ab030b200 100644 --- a/src/Symfony/Component/Lock/Tests/Store/AbstractStoreTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/AbstractStoreTest.php @@ -22,7 +22,7 @@ use Symfony\Component\Lock\StoreInterface; abstract class AbstractStoreTest extends TestCase { /** - * @return StoreInterface; + * @return StoreInterface */ abstract protected function getStore(); diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index acb9eddb34..e87a87fb49 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -102,7 +102,7 @@ EOF; \$pathinfo = rawurldecode(\$rawPathinfo); \$trimmedPathinfo = rtrim(\$pathinfo, '/'); \$context = \$this->context; - \$request = \$this->request; + \$request = \$this->request ?: \$this->createRequest(\$pathinfo); \$requestMethod = \$canonicalMethod = \$context->getMethod(); \$scheme = \$context->getScheme(); @@ -110,7 +110,6 @@ EOF; \$canonicalMethod = 'GET'; } - $code throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException(); @@ -361,7 +360,11 @@ EOF; if ($hasTrailingSlash) { $code .= <<context->getMethod(), array('HEAD', 'GET'))) { + goto $gotoname; + } else { return array_replace(\$ret, \$this->redirect(\$rawPathinfo.'/', '$name')); } @@ -391,7 +394,7 @@ EOF; } $code .= " }\n"; - if ($methods) { + if ($methods || $hasTrailingSlash) { $code .= " $gotoname:\n"; } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php index 839c7d1372..dd8bb98cf3 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php @@ -21,7 +21,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request; + $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); $scheme = $context->getScheme(); @@ -29,7 +29,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher $canonicalMethod = 'GET'; } - if ('/' === $pathinfo) { throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php index 5d7dca8e55..783072886c 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php @@ -21,7 +21,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request; + $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); $scheme = $context->getScheme(); @@ -29,7 +29,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/foo')) { // foo if (preg_match('#^/foo/(?Pbaz|symfony)$#s', $pathinfo, $matches)) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php index 49ce082a81..ac65f2957d 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php @@ -21,7 +21,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request; + $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); $scheme = $context->getScheme(); @@ -29,7 +29,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/foo')) { // foo if (preg_match('#^/foo/(?Pbaz|symfony)$#s', $pathinfo, $matches)) { @@ -83,24 +82,34 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // baz3 if ('/test/baz3' === $trimmedPathinfo) { $ret = array('_route' => 'baz3'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_baz3; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz3')); } return $ret; } + not_baz3: } // baz4 if (preg_match('#^/test/(?P[^/]++)/?$#s', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ()); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_baz4; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz4')); } return $ret; } + not_baz4: // baz5 if (preg_match('#^/test/(?P[^/]++)/$#s', $pathinfo, $matches)) { @@ -179,12 +188,17 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // hey if ('/multi/hey' === $trimmedPathinfo) { $ret = array('_route' => 'hey'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_hey; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'hey')); } return $ret; } + not_hey: // overridden2 if ('/multi/new' === $pathinfo) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php index ae54956312..04f0770e50 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php @@ -21,7 +21,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request; + $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); $scheme = $context->getScheme(); @@ -29,7 +29,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/rootprefix')) { // static if ('/rootprefix/test' === $pathinfo) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php index 50da489fb2..4fede6240c 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php @@ -21,7 +21,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request; + $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); $scheme = $context->getScheme(); @@ -29,7 +29,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher $canonicalMethod = 'GET'; } - // just_head if ('/just_head' === $pathinfo) { if ('HEAD' !== $requestMethod) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php index 51be5b0bc8..56204a8566 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php @@ -21,7 +21,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request; + $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); $scheme = $context->getScheme(); @@ -29,7 +29,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/a')) { // a_first if ('/a/11' === $pathinfo) { @@ -57,32 +56,47 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // a_fourth if ('/a/44' === $trimmedPathinfo) { $ret = array('_route' => 'a_fourth'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_a_fourth; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_fourth')); } return $ret; } + not_a_fourth: // a_fifth if ('/a/55' === $trimmedPathinfo) { $ret = array('_route' => 'a_fifth'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_a_fifth; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_fifth')); } return $ret; } + not_a_fifth: // a_sixth if ('/a/66' === $trimmedPathinfo) { $ret = array('_route' => 'a_sixth'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_a_sixth; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_sixth')); } return $ret; } + not_a_sixth: } @@ -95,32 +109,47 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // nested_a if ('/nested/group/a' === $trimmedPathinfo) { $ret = array('_route' => 'nested_a'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_nested_a; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_a')); } return $ret; } + not_nested_a: // nested_b if ('/nested/group/b' === $trimmedPathinfo) { $ret = array('_route' => 'nested_b'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_nested_b; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_b')); } return $ret; } + not_nested_b: // nested_c if ('/nested/group/c' === $trimmedPathinfo) { $ret = array('_route' => 'nested_c'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_nested_c; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_c')); } return $ret; } + not_nested_c: } @@ -128,32 +157,47 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // slashed_a if ('/slashed/group' === $trimmedPathinfo) { $ret = array('_route' => 'slashed_a'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_slashed_a; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_a')); } return $ret; } + not_slashed_a: // slashed_b if ('/slashed/group/b' === $trimmedPathinfo) { $ret = array('_route' => 'slashed_b'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_slashed_b; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_b')); } return $ret; } + not_slashed_b: // slashed_c if ('/slashed/group/c' === $trimmedPathinfo) { $ret = array('_route' => 'slashed_c'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_slashed_c; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_c')); } return $ret; } + not_slashed_c: } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php index 933525699c..eb437a1d97 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php @@ -21,7 +21,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request; + $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); $scheme = $context->getScheme(); @@ -29,7 +29,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/trailing/simple')) { // simple_trailing_slash_no_methods if ('/trailing/simple/no-methods/' === $pathinfo) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php index bceee6f3a3..29d8ec66a6 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php @@ -21,7 +21,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; - $request = $this->request; + $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); $scheme = $context->getScheme(); @@ -29,17 +29,21 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec $canonicalMethod = 'GET'; } - if (0 === strpos($pathinfo, '/trailing/simple')) { // simple_trailing_slash_no_methods if ('/trailing/simple/no-methods' === $trimmedPathinfo) { $ret = array('_route' => 'simple_trailing_slash_no_methods'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_simple_trailing_slash_no_methods; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'simple_trailing_slash_no_methods')); } return $ret; } + not_simple_trailing_slash_no_methods: // simple_trailing_slash_GET_method if ('/trailing/simple/get-method' === $trimmedPathinfo) { @@ -49,7 +53,11 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } $ret = array('_route' => 'simple_trailing_slash_GET_method'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_simple_trailing_slash_GET_method; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'simple_trailing_slash_GET_method')); } @@ -65,7 +73,11 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } $ret = array('_route' => 'simple_trailing_slash_HEAD_method'); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_simple_trailing_slash_HEAD_method; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'simple_trailing_slash_HEAD_method')); } @@ -90,12 +102,17 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // regex_trailing_slash_no_methods if (0 === strpos($pathinfo, '/trailing/regex/no-methods') && preg_match('#^/trailing/regex/no\\-methods/(?P[^/]++)/?$#s', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_no_methods')), array ()); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_regex_trailing_slash_no_methods; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'regex_trailing_slash_no_methods')); } return $ret; } + not_regex_trailing_slash_no_methods: // regex_trailing_slash_GET_method if (0 === strpos($pathinfo, '/trailing/regex/get-method') && preg_match('#^/trailing/regex/get\\-method/(?P[^/]++)/?$#s', $pathinfo, $matches)) { @@ -105,7 +122,11 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_GET_method')), array ()); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_regex_trailing_slash_GET_method; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'regex_trailing_slash_GET_method')); } @@ -121,7 +142,11 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_HEAD_method')), array ()); - if (substr($pathinfo, -1) !== '/') { + if ('/' === substr($pathinfo, -1)) { + // no-op + } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + goto not_regex_trailing_slash_HEAD_method; + } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'regex_trailing_slash_HEAD_method')); } diff --git a/src/Symfony/Component/Routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php new file mode 100644 index 0000000000..28f65aeeb5 --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Matcher; + +use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper; +use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface; +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; + +class DumpedRedirectableUrlMatcherTest extends RedirectableUrlMatcherTest +{ + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) + { + static $i = 0; + + $class = 'DumpedRedirectableUrlMatcher'.++$i; + $dumper = new PhpMatcherDumper($routes); + $dumpedRoutes = eval('?>'.$dumper->dump(array('class' => $class, 'base_class' => 'Symfony\Component\Routing\Tests\Matcher\TestDumpedRedirectableUrlMatcher'))); + + return $this->getMockBuilder($class) + ->setConstructorArgs(array($context ?: new RequestContext())) + ->setMethods(array('redirect')) + ->getMock(); + } +} + +class TestDumpedRedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface +{ + public function redirect($path, $route, $scheme = null) + { + return array(); + } +} diff --git a/src/Symfony/Component/Routing/Tests/Matcher/DumpedUrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/DumpedUrlMatcherTest.php new file mode 100644 index 0000000000..cc7eb8e2d7 --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Matcher/DumpedUrlMatcherTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Matcher; + +use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; + +class DumpedUrlMatcherTest extends UrlMatcherTest +{ + /** + * @expectedException \LogicException + * @expectedExceptionMessage The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface. + */ + public function testSchemeRequirement() + { + parent::testSchemeRequirement(); + } + + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) + { + static $i = 0; + + $class = 'DumpedUrlMatcher'.++$i; + $dumper = new PhpMatcherDumper($routes); + $dumpedRoutes = eval('?>'.$dumper->dump(array('class' => $class))); + + return new $class($context ?: new RequestContext()); + } +} diff --git a/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php index 0948e002ad..e49b784999 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php @@ -11,19 +11,18 @@ namespace Symfony\Component\Routing\Tests\Matcher; -use PHPUnit\Framework\TestCase; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RequestContext; -class RedirectableUrlMatcherTest extends TestCase +class RedirectableUrlMatcherTest extends UrlMatcherTest { public function testRedirectWhenNoSlash() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/')); - $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher = $this->getUrlMatcher($coll); $matcher->expects($this->once())->method('redirect')->will($this->returnValue(array())); $matcher->match('/foo'); } @@ -38,7 +37,7 @@ class RedirectableUrlMatcherTest extends TestCase $context = new RequestContext(); $context->setMethod('POST'); - $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, $context)); + $matcher = $this->getUrlMatcher($coll, $context); $matcher->match('/foo'); } @@ -47,7 +46,7 @@ class RedirectableUrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('FTP', 'HTTPS'))); - $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher = $this->getUrlMatcher($coll); $matcher ->expects($this->once()) ->method('redirect') @@ -62,7 +61,7 @@ class RedirectableUrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https', 'http'))); - $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher = $this->getUrlMatcher($coll); $matcher ->expects($this->never()) ->method('redirect'); @@ -74,7 +73,7 @@ class RedirectableUrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{bar}', array(), array(), array(), '', array('https'))); - $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher = $this->getUrlMatcher($coll); $matcher ->expects($this->once()) ->method('redirect') @@ -89,7 +88,7 @@ class RedirectableUrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{bar}/')); - $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher = $this->getUrlMatcher($coll); $matcher ->expects($this->once()) ->method('redirect') @@ -104,8 +103,22 @@ class RedirectableUrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo:bar/')); - $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher = $this->getUrlMatcher($coll); $matcher->expects($this->once())->method('redirect')->with('/foo%3Abar/')->willReturn(array()); $matcher->match('/foo%3Abar'); } + + public function testSchemeRequirement() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https'))); + $matcher = $this->getUrlMatcher($coll, new RequestContext()); + $matcher->expects($this->once())->method('redirect')->with('/foo', 'foo', 'https')->willReturn(array('_route' => 'foo')); + $this->assertSame(array('_route' => 'foo'), $matcher->match('/foo')); + } + + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) + { + return $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($routes, $context ?: new RequestContext())); + } } diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index 8545c2c29d..b86c54f5ff 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -26,7 +26,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo')); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $this->assertInternalType('array', $matcher->match('/foo')); } @@ -35,7 +35,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', array(), array(), array(), '', array(), array('post'))); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); try { $matcher->match('/foo'); @@ -50,7 +50,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', array(), array(), array(), '', array(), array('get'))); - $matcher = new UrlMatcher($coll, new RequestContext('', 'head')); + $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'head')); $this->assertInternalType('array', $matcher->match('/foo')); } @@ -60,7 +60,7 @@ class UrlMatcherTest extends TestCase $coll->add('foo1', new Route('/foo', array(), array(), array(), '', array(), array('post'))); $coll->add('foo2', new Route('/foo', array(), array(), array(), '', array(), array('put', 'delete'))); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); try { $matcher->match('/foo'); @@ -75,7 +75,7 @@ class UrlMatcherTest extends TestCase // test the patterns are matched and parameters are returned $collection = new RouteCollection(); $collection->add('foo', new Route('/foo/{bar}')); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); try { $matcher->match('/no-match'); $this->fail(); @@ -86,17 +86,17 @@ class UrlMatcherTest extends TestCase // test that defaults are merged $collection = new RouteCollection(); $collection->add('foo', new Route('/foo/{bar}', array('def' => 'test'))); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertEquals(array('_route' => 'foo', 'bar' => 'baz', 'def' => 'test'), $matcher->match('/foo/baz')); // test that route "method" is ignored if no method is given in the context $collection = new RouteCollection(); $collection->add('foo', new Route('/foo', array(), array(), array(), '', array(), array('get', 'head'))); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertInternalType('array', $matcher->match('/foo')); // route does not match with POST method context - $matcher = new UrlMatcher($collection, new RequestContext('', 'post')); + $matcher = $this->getUrlMatcher($collection, new RequestContext('', 'post')); try { $matcher->match('/foo'); $this->fail(); @@ -104,28 +104,28 @@ class UrlMatcherTest extends TestCase } // route does match with GET or HEAD method context - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertInternalType('array', $matcher->match('/foo')); - $matcher = new UrlMatcher($collection, new RequestContext('', 'head')); + $matcher = $this->getUrlMatcher($collection, new RequestContext('', 'head')); $this->assertInternalType('array', $matcher->match('/foo')); // route with an optional variable as the first segment $collection = new RouteCollection(); $collection->add('bar', new Route('/{bar}/foo', array('bar' => 'bar'), array('bar' => 'foo|bar'))); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertEquals(array('_route' => 'bar', 'bar' => 'bar'), $matcher->match('/bar/foo')); $this->assertEquals(array('_route' => 'bar', 'bar' => 'foo'), $matcher->match('/foo/foo')); $collection = new RouteCollection(); $collection->add('bar', new Route('/{bar}', array('bar' => 'bar'), array('bar' => 'foo|bar'))); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertEquals(array('_route' => 'bar', 'bar' => 'foo'), $matcher->match('/foo')); $this->assertEquals(array('_route' => 'bar', 'bar' => 'bar'), $matcher->match('/')); // route with only optional variables $collection = new RouteCollection(); $collection->add('bar', new Route('/{foo}/{bar}', array('foo' => 'foo', 'bar' => 'bar'), array())); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertEquals(array('_route' => 'bar', 'foo' => 'foo', 'bar' => 'bar'), $matcher->match('/')); $this->assertEquals(array('_route' => 'bar', 'foo' => 'a', 'bar' => 'bar'), $matcher->match('/a')); $this->assertEquals(array('_route' => 'bar', 'foo' => 'a', 'bar' => 'b'), $matcher->match('/a/b')); @@ -138,7 +138,7 @@ class UrlMatcherTest extends TestCase $collection->addPrefix('/b'); $collection->addPrefix('/a'); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertEquals(array('_route' => 'foo', 'foo' => 'foo'), $matcher->match('/a/b/foo')); } @@ -149,7 +149,7 @@ class UrlMatcherTest extends TestCase $collection->addPrefix('/b'); $collection->addPrefix('/{_locale}'); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertEquals(array('_locale' => 'fr', '_route' => 'foo', 'foo' => 'foo'), $matcher->match('/fr/b/foo')); } @@ -158,7 +158,7 @@ class UrlMatcherTest extends TestCase $collection = new RouteCollection(); $collection->add('$péß^a|', new Route('/bar')); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertEquals(array('_route' => '$péß^a|'), $matcher->match('/bar')); } @@ -168,7 +168,7 @@ class UrlMatcherTest extends TestCase $chars = '!"$%éà &\'()*+,./:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\[]^_`abcdefghijklmnopqrstuvwxyz{|}~-'; $collection->add('foo', new Route('/{foo}/bar', array(), array('foo' => '['.preg_quote($chars).']+'), array('utf8' => true))); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertEquals(array('_route' => 'foo', 'foo' => $chars), $matcher->match('/'.rawurlencode($chars).'/bar')); $this->assertEquals(array('_route' => 'foo', 'foo' => $chars), $matcher->match('/'.strtr($chars, array('%' => '%25')).'/bar')); } @@ -178,7 +178,7 @@ class UrlMatcherTest extends TestCase $collection = new RouteCollection(); $collection->add('foo', new Route('/{foo}/bar', array(), array('foo' => '.+'))); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertEquals(array('_route' => 'foo', 'foo' => "\n"), $matcher->match('/'.urlencode("\n").'/bar'), 'linefeed character is matched'); } @@ -192,7 +192,7 @@ class UrlMatcherTest extends TestCase $collection->addCollection($collection1); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); $this->assertEquals(array('_route' => 'foo'), $matcher->match('/foo1')); $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\Routing\Exception\ResourceNotFoundException'); @@ -205,12 +205,12 @@ class UrlMatcherTest extends TestCase $coll->add('foo', new Route('/foo/{foo}')); $coll->add('bar', new Route('/foo/bar/{foo}')); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $this->assertEquals(array('foo' => 'bar', '_route' => 'bar'), $matcher->match('/foo/bar/bar')); $collection = new RouteCollection(); $collection->add('foo', new Route('/{bar}')); - $matcher = new UrlMatcher($collection, new RequestContext()); + $matcher = $this->getUrlMatcher($collection); try { $matcher->match('/'); $this->fail(); @@ -223,7 +223,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('test', new Route('/{page}.{_format}', array('page' => 'index', '_format' => 'html'))); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $this->assertEquals(array('page' => 'my-page', '_format' => 'xml', '_route' => 'test'), $matcher->match('/my-page.xml')); } @@ -232,7 +232,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('test', new Route('/{foo}-{bar}-', array(), array('foo' => '.+', 'bar' => '.+'))); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $this->assertEquals(array('foo' => 'text1-text2-text3', 'bar' => 'text4', '_route' => 'test'), $matcher->match('/text1-text2-text3-text4-')); } @@ -241,7 +241,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('test', new Route('/{w}{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => 'y|Y'))); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); // 'w' eagerly matches as much as possible and the other variables match the remaining chars. // This also shows that the variables w-z must all exclude the separating char (the dot '.' in this case) by default requirement. // Otherwise they would also consume '.xml' and _format would never match as it's an optional variable. @@ -260,7 +260,7 @@ class UrlMatcherTest extends TestCase { $coll = new RouteCollection(); $coll->add('test', new Route('/get{what}', array('what' => 'All'))); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $this->assertEquals(array('what' => 'All', '_route' => 'test'), $matcher->match('/get')); $this->assertEquals(array('what' => 'Sites', '_route' => 'test'), $matcher->match('/getSites')); @@ -275,7 +275,7 @@ class UrlMatcherTest extends TestCase { $coll = new RouteCollection(); $coll->add('test', new Route('/get{what}Suffix')); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $this->assertEquals(array('what' => 'Sites', '_route' => 'test'), $matcher->match('/getSitesSuffix')); } @@ -284,7 +284,7 @@ class UrlMatcherTest extends TestCase { $coll = new RouteCollection(); $coll->add('test', new Route('/{page}.{_format}')); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $this->assertEquals(array('page' => 'index', '_format' => 'mobile.html', '_route' => 'test'), $matcher->match('/index.mobile.html')); } @@ -296,7 +296,7 @@ class UrlMatcherTest extends TestCase { $coll = new RouteCollection(); $coll->add('test', new Route('/{page}.{_format}')); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $matcher->match('/index.sl/ash'); } @@ -308,7 +308,7 @@ class UrlMatcherTest extends TestCase { $coll = new RouteCollection(); $coll->add('test', new Route('/{page}.{_format}', array(), array('_format' => 'html|xml'))); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $matcher->match('/do.t.html'); } @@ -320,7 +320,7 @@ class UrlMatcherTest extends TestCase { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https'))); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $matcher->match('/foo'); } @@ -333,7 +333,7 @@ class UrlMatcherTest extends TestCase $route = new Route('/foo'); $route->setCondition('context.getMethod() == "POST"'); $coll->add('foo', $route); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $matcher->match('/foo'); } @@ -343,7 +343,7 @@ class UrlMatcherTest extends TestCase $route = new Route('/foo/{bar}'); $route->setCondition('request.getBaseUrl() == "/sub/front.php" and request.getPathInfo() == "/foo/bar"'); $coll->add('foo', $route); - $matcher = new UrlMatcher($coll, new RequestContext('/sub/front.php')); + $matcher = $this->getUrlMatcher($coll, new RequestContext('/sub/front.php')); $this->assertEquals(array('bar' => 'bar', '_route' => 'foo'), $matcher->match('/foo/bar')); } @@ -352,7 +352,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{foo}')); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $this->assertEquals(array('foo' => 'bar%23', '_route' => 'foo'), $matcher->match('/foo/bar%2523')); } @@ -368,7 +368,7 @@ class UrlMatcherTest extends TestCase $coll->addCollection($subColl); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $this->assertEquals(array('_route' => 'bar'), $matcher->match('/new')); } @@ -377,7 +377,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{foo}', array(), array(), array(), '{locale}.example.com')); - $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); + $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); $this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar')); } @@ -388,10 +388,10 @@ class UrlMatcherTest extends TestCase $coll->add('bar', new Route('/bar/{foo}', array(), array(), array(), '{locale}.example.net')); $coll->setHost('{locale}.example.com'); - $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); + $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); $this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar')); - $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); + $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); $this->assertEquals(array('foo' => 'bar', '_route' => 'bar', 'locale' => 'en'), $matcher->match('/bar/bar')); } @@ -403,7 +403,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{foo}', array(), array(), array(), '{locale}.example.com')); - $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'example.com')); + $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'example.com')); $matcher->match('/foo/bar'); } @@ -415,7 +415,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/locale', array(), array('locale' => 'EN|FR|DE'))); - $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher = $this->getUrlMatcher($coll); $matcher->match('/en'); } @@ -424,7 +424,7 @@ class UrlMatcherTest extends TestCase $coll = new RouteCollection(); $coll->add('foo', new Route('/', array(), array('locale' => 'EN|FR|DE'), array(), '{locale}.example.com')); - $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); + $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); $this->assertEquals(array('_route' => 'foo', 'locale' => 'en'), $matcher->match('/')); } @@ -438,4 +438,33 @@ class UrlMatcherTest extends TestCase $matcher = new UrlMatcher($coll, new RequestContext()); $matcher->match('/'); } + + public function testNestedCollections() + { + $coll = new RouteCollection(); + + $subColl = new RouteCollection(); + $subColl->add('a', new Route('/a')); + $subColl->add('b', new Route('/b')); + $subColl->add('c', new Route('/c')); + $subColl->addPrefix('/p'); + $coll->addCollection($subColl); + + $coll->add('baz', new Route('/{baz}')); + + $subColl = new RouteCollection(); + $subColl->add('buz', new Route('/buz')); + $subColl->addPrefix('/prefix'); + $coll->addCollection($subColl); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'a'), $matcher->match('/p/a')); + $this->assertEquals(array('_route' => 'baz', 'baz' => 'p'), $matcher->match('/p')); + $this->assertEquals(array('_route' => 'buz'), $matcher->match('/prefix/buz')); + } + + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) + { + return new UrlMatcher($routes, $context ?: new RequestContext()); + } }