diff --git a/core/composer.json b/core/composer.json index 87e60b4..26030b0 100644 --- a/core/composer.json +++ b/core/composer.json @@ -14,7 +14,8 @@ "twig/twig": "1.*@stable", "doctrine/common": "2.3.*@stable", "guzzle/http": "*", - "kriswallsmith/assetic": "1.1.*@alpha" + "kriswallsmith/assetic": "1.1.*@alpha", + "symfony-cmf/routing": "1.0.*@dev" }, "minimum-stability": "dev" } diff --git a/core/composer.lock b/core/composer.lock index 1894ab3..d8e8608 100644 --- a/core/composer.lock +++ b/core/composer.lock @@ -1,5 +1,5 @@ { - "hash": "5d17aee0bd24c24563c2c864600fc5bd", + "hash": "27b5fb7194e0d492c69d372d8ba17b2b", "packages": [ { "name": "doctrine/common", @@ -310,6 +310,56 @@ ] }, { + "name": "symfony-cmf/routing", + "version": "dev-master", + "target-dir": "Symfony/Cmf/Component/Routing", + "source": { + "type": "git", + "url": "https://github.com/symfony-cmf/Routing", + "reference": "1.0.0-alpha3" + }, + "dist": { + "type": "zip", + "url": "https://github.com/symfony-cmf/Routing/archive/1.0.0-alpha3.zip", + "reference": "1.0.0-alpha3", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "symfony/routing": ">=2.1,<2.3-dev", + "symfony/http-kernel": ">=2.1,<2.3-dev" + }, + "time": "2012-12-16 17:52:57", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "source", + "autoload": { + "psr-0": { + "Symfony\\Cmf\\Component\\Routing": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony CMF Community", + "homepage": "https://github.com/symfony-cmf/Routing/contributors" + } + ], + "description": "Extends the Symfony2 routing component for dynamic routes and chaining several routers", + "homepage": "http://cmf.symfony.com", + "keywords": [ + "database", + "routing" + ] + }, + { "name": "symfony/class-loader", "version": "dev-master", "target-dir": "Symfony/Component/ClassLoader", @@ -848,6 +898,7 @@ "stability-flags": { "twig/twig": 0, "doctrine/common": 0, - "kriswallsmith/assetic": 15 + "kriswallsmith/assetic": 15, + "symfony-cmf/routing": 20 } } diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php index 8300fc3..02c8432 100644 --- a/core/lib/Drupal/Core/CoreBundle.php +++ b/core/lib/Drupal/Core/CoreBundle.php @@ -10,7 +10,7 @@ use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass; use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass; use Drupal\Core\DependencyInjection\Compiler\RegisterMatchersPass; -use Drupal\Core\DependencyInjection\Compiler\RegisterNestedMatchersPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass; use Drupal\Core\DependencyInjection\Compiler\RegisterSerializationClassesPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -136,12 +136,32 @@ public function build(ContainerBuilder $container) { ->addArgument(new Reference('lock')) ->addArgument(new Reference('dispatcher')); + $container->register('router.request_context', 'Symfony\Component\Routing\RequestContext') + ->addMethodCall('fromRequest', array(new Reference('request'))); + $container->register('router.route_provider', 'Drupal\Core\Routing\RouteProvider') + ->addArgument(new Reference('database')); + $container->register('router.matcher.final_matcher', 'Drupal\Core\Routing\UrlMatcher'); + $container->register('router.matcher', 'Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher') + ->addArgument(new Reference('router.route_provider')) + ->addMethodCall('setFinalMatcher', array(new Reference('router.matcher.final_matcher'))); + $container->register('router.generator', 'Symfony\Cmf\Component\Routing\ProviderBasedGenerator') + ->addArgument(new Reference('router.route_provider')); + $container->register('router.dynamic', 'Symfony\Cmf\Component\Routing\DynamicRouter') + ->addArgument(new Reference('router.request_context')) + ->addArgument(new Reference('router.matcher')) + ->addArgument(new Reference('router.generator')); + + $container->register('legacy_generator', 'Drupal\Core\Routing\NullGenerator'); + $container->register('legacy_url_matcher', 'Drupal\Core\LegacyUrlMatcher'); + $container->register('legacy_router', 'Symfony\Cmf\Component\Routing\DynamicRouter') + ->addArgument(new Reference('router.request_context')) + ->addArgument(new Reference('legacy_url_matcher')) + ->addArgument(new Reference('legacy_generator')); - $container->register('matcher', 'Drupal\Core\Routing\ChainMatcher'); - $container->register('legacy_url_matcher', 'Drupal\Core\LegacyUrlMatcher') - ->addTag('chained_matcher'); - $container->register('nested_matcher', 'Drupal\Core\Routing\NestedMatcher') - ->addTag('chained_matcher', array('priority' => 5)); + $container->register('router', 'Symfony\Cmf\Component\Routing\ChainRouter') + ->addMethodCall('setContext', array(new Reference('router.request_context'))) + ->addMethodCall('add', array(new Reference('legacy_router'))) + ->addMethodCall('add', array(new Reference('router.dynamic'))); $container ->register('cache.path', 'Drupal\Core\Cache\CacheBackendInterface') @@ -166,26 +186,15 @@ public function build(ContainerBuilder $container) { $container->register('password', 'Drupal\Core\Password\PhpassHashedPassword') ->addArgument(16); - // The following services are tagged as 'nested_matcher' services and are - // processed in the RegisterNestedMatchersPass compiler pass. Each one - // needs to be set on the matcher using a different method, so we use a - // tag attribute, 'method', which can be retrieved and passed to the - // addMethodCall() method that gets called on the matcher service in the - // compiler pass. - $container->register('path_matcher', 'Drupal\Core\Routing\PathMatcher') - ->addArgument(new Reference('database')) - ->addTag('nested_matcher', array('method' => 'setInitialMatcher')); - $container->register('http_method_matcher', 'Drupal\Core\Routing\HttpMethodMatcher') - ->addTag('nested_matcher', array('method' => 'addPartialMatcher')); + // The following services are tagged as 'route_filter' services and are + // processed in the RegisterRouteFiltersPass compiler pass. $container->register('mime_type_matcher', 'Drupal\Core\Routing\MimeTypeMatcher') - ->addTag('nested_matcher', array('method' => 'addPartialMatcher')); - $container->register('first_entry_final_matcher', 'Drupal\Core\Routing\FirstEntryFinalMatcher') - ->addTag('nested_matcher', array('method' => 'setFinalMatcher')); + ->addTag('route_filter'); $container->register('router_processor_subscriber', 'Drupal\Core\EventSubscriber\RouteProcessorSubscriber') ->addTag('event_subscriber'); $container->register('router_listener', 'Symfony\Component\HttpKernel\EventListener\RouterListener') - ->addArgument(new Reference('matcher')) + ->addArgument(new Reference('router')) ->addTag('event_subscriber'); $container->register('content_negotiation', 'Drupal\Core\ContentNegotiation'); $container->register('view_subscriber', 'Drupal\Core\EventSubscriber\ViewSubscriber') @@ -243,7 +252,7 @@ public function build(ContainerBuilder $container) { ->addArgument(new Reference('database')); $container->addCompilerPass(new RegisterMatchersPass()); - $container->addCompilerPass(new RegisterNestedMatchersPass()); + $container->addCompilerPass(new RegisterRouteFiltersPass()); // Add a compiler pass for registering event subscribers. $container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING); // Add a compiler pass for adding Normalizers and Encoders to Serializer. diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterNestedMatchersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterRouteFiltersPass.php similarity index 65% rename from core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterNestedMatchersPass.php rename to core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterRouteFiltersPass.php index b245952..be21180 100644 --- a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterNestedMatchersPass.php +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterRouteFiltersPass.php @@ -14,7 +14,7 @@ /** * Adds services tagged 'nested_matcher' to the tagged_matcher service. */ -class RegisterNestedMatchersPass implements CompilerPassInterface { +class RegisterRouteFiltersPass implements CompilerPassInterface { /** * Adds services tagged 'nested_matcher' to the tagged_matcher service. @@ -23,13 +23,12 @@ class RegisterNestedMatchersPass implements CompilerPassInterface { * The container to process. */ public function process(ContainerBuilder $container) { - if (!$container->hasDefinition('nested_matcher')) { + if (!$container->hasDefinition('router.matcher')) { return; } - $nested = $container->getDefinition('nested_matcher'); - foreach ($container->findTaggedServiceIds('nested_matcher') as $id => $attributes) { - $method = $attributes[0]['method']; - $nested->addMethodCall($method, array(new Reference($id))); + $nested = $container->getDefinition('router.matcher'); + foreach ($container->findTaggedServiceIds('router_filter') as $id => $attributes) { + $nested->addMethodCall('addRouteFilter', array(new Reference($id))); } } } diff --git a/core/lib/Drupal/Core/Routing/ChainMatcher.php b/core/lib/Drupal/Core/Routing/ChainMatcher.php deleted file mode 100644 index 23410bf..0000000 --- a/core/lib/Drupal/Core/Routing/ChainMatcher.php +++ /dev/null @@ -1,165 +0,0 @@ -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. - */ - 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; - } - - /** - * Matches a request against all queued matchers. - * - * @param Request $request The request to match - * - * @return array An array of parameters - * - * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException - * If no matching resource could be found - * @throws \Symfony\Component\Routing\Exception\MethodNotAllowedException - * If a matching resource was found but the request method is not allowed - */ - public function matchRequest(Request $request) { - $methodNotAllowed = null; - - foreach ($this->all() as $matcher) { - try { - return $matcher->matchRequest($request); - } catch (ResourceNotFoundException $e) { - // Needs special care - } catch (MethodNotAllowedException $e) { - $methodNotAllowed = $e; - } - } - - throw $methodNotAllowed ?: new ResourceNotFoundException("None of the matchers in the chain matched this request."); - } - - /** - * Adds a Matcher to the index. - * - * @param MatcherInterface $matcher - * The matcher to add. - * @param int $priority - * (optional) The priority of the matcher. Higher number matchers will be checked - * first. Default to 0. - */ - public function add(RequestMatcherInterface $matcher, $priority = 0) { - if (empty($this->matchers[$priority])) { - $this->matchers[$priority] = array(); - } - - $this->matchers[$priority][] = $matcher; - $this->sortedMatchers = array(); - } - - /** - * Sorts the matchers and flattens them. - * - * @return array - * An array of RequestMatcherInterface objects. - */ - public function all() { - if (empty($this->sortedMatchers)) { - $this->sortedMatchers = $this->sortMatchers(); - } - - return $this->sortedMatchers; - } - - /** - * Sort matchers by priority. - * - * The highest priority number is the highest priority (reverse sorting). - * - * @return \Symfony\Component\Routing\RequestMatcherInterface[] - * An array of Matcher objects in the order they should be used. - */ - protected function sortMatchers() { - $sortedMatchers = array(); - krsort($this->matchers); - - foreach ($this->matchers as $matchers) { - $sortedMatchers = array_merge($sortedMatchers, $matchers); - } - - return $sortedMatchers; - } - -} diff --git a/core/lib/Drupal/Core/Routing/CompiledRoute.php b/core/lib/Drupal/Core/Routing/CompiledRoute.php index c5cdde8..4ebd119 100644 --- a/core/lib/Drupal/Core/Routing/CompiledRoute.php +++ b/core/lib/Drupal/Core/Routing/CompiledRoute.php @@ -8,11 +8,12 @@ namespace Drupal\Core\Routing; use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\CompiledRoute as SymfonyCompiledRoute; /** - * Description of CompiledRoute + * A compiled route contains derived information from a route object. */ -class CompiledRoute { +class CompiledRoute extends SymfonyCompiledRoute { /** * The fitness of this route. @@ -43,14 +44,12 @@ class CompiledRoute { protected $route; /** - * The regular expression to match placeholders out of this path. + * Constructs a new compiled route object. * - * @var string - */ - protected $regex; - - /** - * Constructs a new CompiledRoute object. + * This is a ridiculously long set of constructor parameters, but as this + * object is little more than a collection of values it's not a serious + * problem. The parent Symfony class does the same, as well, making it + * difficult to override differently. * * @param \Symfony\Component\Routing\Route $route * A original Route instance. @@ -60,15 +59,30 @@ class CompiledRoute { * The pattern outline for this route. * @param int $num_parts * The number of parts in the path. + * @param string $staticPrefix + * The static prefix of the compiled route * @param string $regex - * The regular expression to match placeholders out of this path. + * The regular expression to use to match this route + * @param array $tokens + * An array of tokens to use to generate URL for this route + * @param array $pathVariables + * An array of path variables + * @param string|null $hostnameRegex + * Hostname regex + * @param array $hostnameTokens + * Hostname tokens + * @param array $hostnameVariables + * An array of hostname variables + * @param array $variables + * An array of variables (variables defined in the path and in the hostname patterns) */ - public function __construct(Route $route, $fit, $pattern_outline, $num_parts, $regex) { + public function __construct(Route $route, $fit, $pattern_outline, $num_parts, $staticPrefix, $regex, array $tokens, array $pathVariables, $hostnameRegex = null, array $hostnameTokens = array(), array $hostnameVariables = array(), array $variables = array()) { + parent::__construct($staticPrefix, $regex, $tokens, $pathVariables); + $this->route = $route; $this->fit = $fit; $this->patternOutline = $pattern_outline; $this->numParts = $num_parts; - $this->regex = $regex; } /** @@ -110,16 +124,6 @@ public function getPatternOutline() { } /** - * Returns the placeholder regex. - * - * @return string - * The regex to locate placeholders in this pattern. - */ - public function getRegex() { - return $this->regex; - } - - /** * Returns the Route instance. * * @return Route diff --git a/core/lib/Drupal/Core/Routing/FinalMatcherInterface.php b/core/lib/Drupal/Core/Routing/FinalMatcherInterface.php deleted file mode 100644 index 8b85c21..0000000 --- a/core/lib/Drupal/Core/Routing/FinalMatcherInterface.php +++ /dev/null @@ -1,39 +0,0 @@ -routes = $collection; - - return $this; - } - - /** - * Implements Drupal\Core\Routing\FinalMatcherInterface::matchRequest(). - */ - public function matchRequest(Request $request) { - // Return whatever the first route in the collection is. - foreach ($this->routes as $name => $route) { - $path = '/' . $request->attributes->get('system_path'); - - $route->setOption('compiler_class', '\Drupal\Core\Routing\RouteCompiler'); - $compiled = $route->compile(); - - preg_match($compiled->getRegex(), $path, $matches); - - $route->setOption('_name', $name); - return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $route)); - } - } - - /** - * Get merged default parameters. - * - * @param array $params - * The parameters. - * @param array $defaults - * The defaults. - * - * @return array - * Merged default parameters. - */ - protected function mergeDefaults($params, $defaults) { - $parameters = $defaults; - foreach ($params as $key => $value) { - if (!is_int($key)) { - $parameters[$key] = $value; - } - } - - return $parameters; - } - -} diff --git a/core/lib/Drupal/Core/Routing/HttpMethodMatcher.php b/core/lib/Drupal/Core/Routing/HttpMethodMatcher.php deleted file mode 100644 index 5064353..0000000 --- a/core/lib/Drupal/Core/Routing/HttpMethodMatcher.php +++ /dev/null @@ -1,58 +0,0 @@ -getMethod(); - - $collection = new RouteCollection(); - - foreach ($this->routes->all() as $name => $route) { - // _method could be a |-delimited list of allowed methods, or null. If - // null, we accept any method. - $allowed_methods = array_filter(explode('|', strtoupper($route->getRequirement('_method')))); - if (empty($allowed_methods) || in_array($method, $allowed_methods)) { - $collection->add($name, $route); - } - else { - // Build a list of methods that would have matched. Note that we only - // need to do this if a route doesn't match, because if even one route - // passes then we'll never throw the exception that needs this array. - $possible_methods += $allowed_methods; - } - } - - if (!count($collection->all())) { - throw new MethodNotAllowedException(array_unique($possible_methods)); - } - - return $collection; - } - -} - diff --git a/core/lib/Drupal/Core/Routing/InitialMatcherInterface.php b/core/lib/Drupal/Core/Routing/InitialMatcherInterface.php deleted file mode 100644 index 53bc8e7..0000000 --- a/core/lib/Drupal/Core/Routing/InitialMatcherInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -getAcceptableContentTypes(); $acceptable_formats = array_map(array($request, 'getFormat'), $acceptable_mime_types); - $collection = new RouteCollection(); + $filtered_collection = new RouteCollection(); - foreach ($this->routes->all() as $name => $route) { + foreach ($collection as $name => $route) { // _format could be a |-delimited list of supported formats. $supported_formats = array_filter(explode('|', $route->getRequirement('_format'))); // The route partially matches if it doesn't care about format, if it // explicitly allows any format, or if one of its allowed formats is // in the request's list of acceptable formats. if (empty($supported_formats) || in_array('*/*', $acceptable_mime_types) || array_intersect($acceptable_formats, $supported_formats)) { - $collection->add($name, $route); + $filtered_collection->add($name, $route); } } - if (!count($collection)) { + if (!count($filtered_collection)) { throw new UnsupportedMediaTypeHttpException(); } - return $collection; + return $filtered_collection; } } diff --git a/core/lib/Drupal/Core/Routing/NestedMatcher.php b/core/lib/Drupal/Core/Routing/NestedMatcher.php deleted file mode 100644 index ff53715..0000000 --- a/core/lib/Drupal/Core/Routing/NestedMatcher.php +++ /dev/null @@ -1,199 +0,0 @@ -matchers[$priority])) { - $this->matchers[$priority] = array(); - } - - $this->matchers[$priority][] = $matcher; - $this->sortedMatchers = array(); - } - - /** - * Sets the final matcher for the matching plan. - * - * @param \Drupal\Core\Routing\FinalMatcherInterface $final - * The matcher that will be called last to ensure only a single route is - * found. - * - * @return \Drupal\Core\Routing\NestedMatcherInterface - * The current matcher. - */ - public function setFinalMatcher(FinalMatcherInterface $final) { - $this->finalMatcher = $final; - - return $this; - } - - /** - * Sets the first matcher for the matching plan. - * - * Partial matchers will be run in the order in which they are added. - * - * @param \Drupal\Core\Routing\InitialMatcherInterface $matcher - * An initial matcher. It is responsible for its own configuration and - * initial route collection - * - * @return \Drupal\Core\Routing\NestedMatcherInterface - * The current matcher. - */ - public function setInitialMatcher(InitialMatcherInterface $initial) { - $this->initialMatcher = $initial; - - return $this; - } - - /** - * Tries to match a request with a set of routes. - * - * If the matcher can not find information, it must throw one of the - * exceptions documented below. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The request to match. - * - * @return array - * An array of parameters. - * - * @throws ResourceNotFoundException - * If no matching resource could be found. - * @throws MethodNotAllowedException - * If a matching resource was found but the request method is not allowed. - */ - public function matchRequest(Request $request) { - $collection = $this->initialMatcher->matchRequestPartial($request); - - foreach ($this->getPartialMatchers() as $matcher) { - if ($collection) { - $matcher->setCollection($collection); - } - $collection = $matcher->matchRequestPartial($request); - } - - $attributes = $this->finalMatcher->setCollection($collection)->matchRequest($request); - - return $attributes; - } - - /** - * Sorts the matchers and flattens them. - * - * @return array - * An array of RequestMatcherInterface objects. - */ - public function getPartialMatchers() { - if (empty($this->sortedMatchers)) { - $this->sortedMatchers = $this->sortMatchers(); - } - - return $this->sortedMatchers; - } - - /** - * Sort matchers by priority. - * - * The highest priority number is the highest priority (reverse sorting). - * - * @return \Symfony\Component\Routing\RequestMatcherInterface[] - * An array of Matcher objects in the order they should be used. - */ - protected function sortMatchers() { - $sortedMatchers = array(); - krsort($this->matchers); - - foreach ($this->matchers as $matchers) { - $sortedMatchers = array_merge($sortedMatchers, $matchers); - } - - return $sortedMatchers; - } - - /** - * Sets the request context. - * - * This method is unused. It is here only to satisfy the interface. - * - * @param \Symfony\Component\Routing\RequestContext $context - * The context - */ - public function setContext(RequestContext $context) { - $this->context = $context; - } - - /** - * Gets the request context. - * - * This method is unused. It is here only to satisfy the interface. - * - * @return \Symfony\Component\Routing\RequestContext - * The context - */ - public function getContext() { - return $this->context; - } - -} diff --git a/core/lib/Drupal/Core/Routing/NestedMatcherInterface.php b/core/lib/Drupal/Core/Routing/NestedMatcherInterface.php deleted file mode 100644 index 6ae0995..0000000 --- a/core/lib/Drupal/Core/Routing/NestedMatcherInterface.php +++ /dev/null @@ -1,58 +0,0 @@ -routes = $collection; - - return $this; - } - -} diff --git a/core/lib/Drupal/Core/Routing/PartialMatcherInterface.php b/core/lib/Drupal/Core/Routing/PartialMatcherInterface.php deleted file mode 100644 index 0d180c6..0000000 --- a/core/lib/Drupal/Core/Routing/PartialMatcherInterface.php +++ /dev/null @@ -1,39 +0,0 @@ -symfonyCompiler = new SymfonyRouteCompiler(); + } + + /** * Compiles the current route instance. * + * Because so much of the parent class is private, we need to call the parent + * class's compile() method and then dissect its return value to build our + * new compiled object. If upstream gets refactored so we can subclass more + * easily then this may not be necessary. + * * @param \Symfony\Component\Routing\Route $route * A Route instance. * @@ -36,142 +60,29 @@ class RouteCompiler implements RouteCompilerInterface { */ public function compile(Route $route) { - $stripped_path = $this->getPathWithoutDefaults($route); + $symfony_compiled = parent::compile($route); + // The Drupal-specific compiled information. + $stripped_path = $this->getPathWithoutDefaults($route); $fit = $this->getFit($stripped_path); - $pattern_outline = $this->getPatternOutline($stripped_path); - $num_parts = count(explode('/', trim($pattern_outline, '/'))); - $regex = $this->getRegex($route, $route->getPattern()); - - return new CompiledRoute($route, $fit, $pattern_outline, $num_parts, $regex); - } - - /** - * Generates a regular expression that will match this pattern. - * - * This regex can be used in preg_match() to extract values inside {}. - * - * This algorithm was lifted directly from Symfony's RouteCompiler class. - * It is not factored out nicely there, so we cannot simply subclass it. - * @todo Refactor Symfony's RouteCompiler so that it's useful to subclass. - * - * @param \Symfony\Component\Routing\Route $route - * The route object. - * @param string $pattern - * The pattern for which we want a matching regex. - * - * @return string - * A regular expression that will match a path against this route. - * - * @throws \LogicException - */ - public function getRegex(Route $route, $pattern) { - $len = strlen($pattern); - $tokens = array(); - $variables = array(); - $pos = 0; - preg_match_all('#.\{(\w+)\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); - foreach ($matches as $match) { - if ($text = substr($pattern, $pos, $match[0][1] - $pos)) { - $tokens[] = array('text', $text); - } - - $pos = $match[0][1] + strlen($match[0][0]); - $var = $match[1][0]; - - if ($req = $route->getRequirement($var)) { - $regexp = $req; - } - else { - // Use the character preceding the variable as a separator - $separators = array($match[0][0][0]); - - if ($pos !== $len) { - // Use the character following the variable as the separator when available - $separators[] = $pattern[$pos]; - } - $regexp = sprintf('[^%s]+', preg_quote(implode('', array_unique($separators)), self::REGEX_DELIMITER)); - } - - $tokens[] = array('variable', $match[0][0][0], $regexp, $var); - - if (in_array($var, $variables)) { - throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $route->getPattern(), $var)); - } - - $variables[] = $var; - } - - if ($pos < $len) { - $tokens[] = array('text', substr($pattern, $pos)); - } - - // find the first optional token - $first_optional = INF; - for ($i = count($tokens) - 1; $i >= 0; $i--) { - $token = $tokens[$i]; - if ('variable' === $token[0] && $route->hasDefault($token[3])) { - $first_optional = $i; - } else { - break; - } - } - - // compute the matching regexp - $regexp = ''; - for ($i = 0, $nbToken = count($tokens); $i < $nbToken; $i++) { - $regexp .= $this->computeRegexp($tokens, $i, $first_optional); - } - - return self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s'; - } - - /** - * Computes the regexp used to match a specific token. It can be static text or a subpattern. - * - * @param array $tokens - * The route tokens - * @param integer $index - * The index of the current token - * @param integer $first_optional - * The index of the first optional token - * - * @return string - * The regexp pattern for a single token - */ - private function computeRegexp(array $tokens, $index, $first_optional) { - $token = $tokens[$index]; - if ('text' === $token[0]) { - // Text tokens - return preg_quote($token[1], self::REGEX_DELIMITER); - } - else { - // Variable tokens - if (0 === $index && 0 === $first_optional) { - // When the only token is an optional variable token, the separator is - // required. - return sprintf('%s(?<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); - } - else { - $regexp = sprintf('%s(?<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); - if ($index >= $first_optional) { - // Enclose each optional token in a subpattern to make it optional. - // "?:" means it is non-capturing, i.e. the portion of the subject - // string that matched the optional subpattern is not passed back. - $regexp = "(?:$regexp"; - $nbTokens = count($tokens); - if ($nbTokens - 1 == $index) { - // Close the optional subpatterns. - $regexp .= str_repeat(")?", $nbTokens - $first_optional - (0 === $first_optional ? 1 : 0)); - } - } - - return $regexp; - } - } + return new CompiledRoute( + $route, + $fit, + $pattern_outline, + $num_parts, + // These are the Symfony compiled parts. + $symfony_compiled->getStaticPrefix(), + $symfony_compiled->getRegex(), + $symfony_compiled->getTokens(), + $symfony_compiled->getPathVariables(), + $symfony_compiled->getHostnameRegex(), + $symfony_compiled->getHostnameTokens(), + $symfony_compiled->getHostnameVariables(), + $symfony_compiled->getVariables() + ); } /** diff --git a/core/lib/Drupal/Core/Routing/PathMatcher.php b/core/lib/Drupal/Core/Routing/RouteProvider.php similarity index 54% rename from core/lib/Drupal/Core/Routing/PathMatcher.php rename to core/lib/Drupal/Core/Routing/RouteProvider.php index 9b5bd5e..f2aea92 100644 --- a/core/lib/Drupal/Core/Routing/PathMatcher.php +++ b/core/lib/Drupal/Core/Routing/RouteProvider.php @@ -2,21 +2,22 @@ /** * @file - * Definition of Drupal\Core\Routing\PathMatcher. + * Contains Drupal\Core\Routing\RouteProvider. */ namespace Drupal\Core\Routing; +use Symfony\Cmf\Component\Routing\RouteProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Exception\ResourceNotFoundException; -use Drupal\Core\Database\Connection; +use \Drupal\Core\Database\Connection; /** - * Initial matcher to match a route against a built database, by path. + * A Route Provider front-end for all Drupal-stored routes. */ -class PathMatcher implements InitialMatcherInterface { +class RouteProvider implements RouteProviderInterface { /** * The database connection from which to read route information. @@ -46,15 +47,29 @@ public function __construct(Connection $connection, $table = 'router') { } /** - * Matches a request against multiple routes. + * Finds routes that may potentially match the request. * - * @param \Symfony\Component\HttpFoundation\Request $request - * A Request object against which to match. + * This may return a mixed list of class instances, but all routes returned + * must extend the core symfony route. The classes may also implement + * RouteObjectInterface to link to a content document. * - * @return \Symfony\Component\Routing\RouteCollection - * A RouteCollection of matched routes. + * This method may not throw an exception based on implementation specific + * restrictions on the url. That case is considered a not found - returning + * an empty array. Exceptions are only used to abort the whole request in + * case something is seriously broken, like the storage backend being down. + * + * Note that implementations may not implement an optimal matching + * algorithm, simply a reasonable first pass. That allows for potentially + * very large route sets to be filtered down to likely candidates, which + * may then be filtered in memory more completely. + * + * @param Request $request A request against which to match. + * + * @return \Symfony\Component\Routing\RouteCollection with all urls that + * could potentially match $request. Empty collection if nothing can + * match. */ - public function matchRequestPartial(Request $request) { + public function getRouteCollectionForRequest(Request $request) { // The 'system_path' has language prefix stripped and path alias resolved, // whereas getPathInfo() returns the requested path. In Drupal, the request @@ -97,6 +112,57 @@ public function matchRequestPartial(Request $request) { } /** + * Find the route using the provided route name (and parameters) + * + * @param string $name the route name to fetch + * @param array $parameters the parameters as they are passed to the + * UrlGeneratorInterface::generate call + * + * @return \Symfony\Component\Routing\Route + * + * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException if + * there is no route with that name in this repository + */ + public function getRouteByName($name, $parameters = array()) { + $routes = $this->getRoutesByNames(array($name), $parameters); + if (empty($routes)) { + throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name)); + } + + return reset($routes); + } + + /** + * Find many routes by their names using the provided list of names + * + * Note that this method may not throw an exception if some of the routes + * are not found. It will just return the list of those routes it found. + * + * This method exists in order to allow performance optimizations. The + * simple implementation could be to just repeatedly call + * $this->getRouteByName() + * + * @param array $names the list of names to retrieve + * @param array $parameters the parameters as they are passed to the + * UrlGeneratorInterface::generate call. (Only one array, not one for + * each entry in $names. + * + * @return \Symfony\Component\Routing\Route[] iterable thing with the keys + * the names of the $names argument. + */ + public function getRoutesByNames($names, $parameters = array()) { + $result = $this->connection->query('SELECT name, route FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name IN :names', array(':names' => $names)); + $routes = $result->fetchAllKeyed(); + + $return = array(); + foreach ($routes as $name => $route) { + $return[$name] = unserialize($route); + } + + return $return; + } + + /** * Returns an array of path pattern outlines that could match the path parts. * * @param array $parts @@ -145,4 +211,5 @@ public function getCandidateOutlines(array $parts) { } return $ancestors; } + } diff --git a/core/lib/Drupal/Core/Routing/UrlMatcher.php b/core/lib/Drupal/Core/Routing/UrlMatcher.php new file mode 100644 index 0000000..1e302bd --- /dev/null +++ b/core/lib/Drupal/Core/Routing/UrlMatcher.php @@ -0,0 +1,25 @@ +routes = $collection; + $context = new RequestContext(); + $context->fromRequest($request); + $this->setContext($context); + return $this->match('/' . $request->attributes->get('system_path')); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/ChainMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/ChainMatcherTest.php deleted file mode 100644 index c6b28cc..0000000 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/ChainMatcherTest.php +++ /dev/null @@ -1,112 +0,0 @@ - 'Chain matcher tests', - 'description' => 'Confirm that the chain matcher is working correctly.', - 'group' => 'Routing', - ); - } - - /** - * Confirms that the expected exception is thrown. - */ - public function testMethodNotAllowed() { - - $chain = new ChainMatcher(); - - $method_not_allowed = new MockMatcher(function(Request $request) { - throw new MethodNotAllowedException(array('POST')); - }); - - try { - $chain->add($method_not_allowed); - $chain->matchRequest(Request::create('my/path')); - } - catch (MethodNotAllowedException $e) { - $this->pass('Correct exception thrown.'); - } - catch (Exception $e) { - $this->fail('Incorrect exception thrown: ' . get_class($e)); - } - } - - /** - * Confirms that the expected exception is thrown. - */ - public function testRequestNotFound() { - - $chain = new ChainMatcher(); - - $resource_not_found = new MockMatcher(function(Request $request) { - throw new ResourceNotFoundException(); - }); - - try { - $chain->add($resource_not_found); - $chain->matchRequest(Request::create('my/path')); - } - catch (ResourceNotFoundException $e) { - $this->pass('Correct exception thrown.'); - } - catch (Exception $e) { - $this->fail('Incorrect exception thrown: ' . get_class($e)); - } - } - - /** - * Confirms that the expected exception is thrown. - */ - public function testRequestFound() { - - $chain = new ChainMatcher(); - - $method_not_allowed = new MockMatcher(function(Request $request) { - throw new MethodNotAllowedException(array('POST')); - }); - - $resource_not_found = new MockMatcher(function(Request $request) { - throw new ResourceNotFoundException(); - }); - - $found_data = new MockMatcher(function(Request $request) { - return array('_controller' => 'foo'); - }); - - try { - $chain->add($method_not_allowed); - $chain->add($resource_not_found); - $chain->add($found_data); - $request = Request::create('my/path'); - $attributes = $chain->matchRequest($request); - $this->assertEqual($attributes['_controller'], 'foo', 'Correct attributes returned.'); - } - catch (Exception $e) { - $this->fail('Exception thrown when a match should have been successful: ' . get_class($e)); - } - } - -} diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php deleted file mode 100644 index c44a492..0000000 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php +++ /dev/null @@ -1,112 +0,0 @@ - 'FirstEntryFinalMatcher tests', - 'description' => 'Confirm that the FirstEntryFinalMatcher is working properly.', - 'group' => 'Routing', - ); - } - - function __construct($test_id = NULL) { - parent::__construct($test_id); - - $this->fixtures = new RoutingFixtures(); - } - - /** - * Confirms the final matcher returns correct attributes for static paths. - */ - public function testFinalMatcherStatic() { - - $collection = new RouteCollection(); - $collection->add('route_a', new Route('/path/one', array( - '_controller' => 'foo', - ))); - - $request = Request::create('/path/one', 'GET'); - - $matcher = new FirstEntryFinalMatcher(); - $matcher->setCollection($collection); - $attributes = $matcher->matchRequest($request); - - $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.'); - $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.'); - } - - /** - * Confirms the final matcher returns correct attributes for pattern paths. - */ - public function testFinalMatcherPattern() { - - $collection = new RouteCollection(); - $collection->add('route_a', new Route('/path/one/{value}', array( - '_controller' => 'foo', - ))); - - $request = Request::create('/path/one/narf', 'GET'); - $request->attributes->set('system_path', 'path/one/narf'); - - $matcher = new FirstEntryFinalMatcher(); - $matcher->setCollection($collection); - $attributes = $matcher->matchRequest($request); - - $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.'); - $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.'); - $this->assertEqual($attributes['value'], 'narf', 'Required placeholder value found.'); - } - - /** - * Confirms the final matcher returns correct attributes with default values. - */ - public function testFinalMatcherPatternDefalts() { - - $collection = new RouteCollection(); - $collection->add('route_a', new Route('/path/one/{value}', array( - '_controller' => 'foo', - 'value' => 'poink' - ))); - - $request = Request::create('/path/one', 'GET'); - $request->attributes->set('system_path', 'path/one'); - - $matcher = new FirstEntryFinalMatcher(); - $matcher->setCollection($collection); - $attributes = $matcher->matchRequest($request); - - $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.'); - $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.'); - $this->assertEqual($attributes['value'], 'poink', 'Optional placeholder value used default.'); - } -} diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php deleted file mode 100644 index 8055743..0000000 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php +++ /dev/null @@ -1,105 +0,0 @@ - 'Partial matcher HTTP Method tests', - 'description' => 'Confirm that the Http Method partial matcher is functioning properly.', - 'group' => 'Routing', - ); - } - - function __construct($test_id = NULL) { - parent::__construct($test_id); - - $this->fixtures = new RoutingFixtures(); - } - - /** - * Confirms that the HttpMethod matcher matches properly. - */ - public function testFilterRoutes() { - - $matcher = new HttpMethodMatcher(); - $matcher->setCollection($this->fixtures->sampleRouteCollection()); - - $routes = $matcher->matchRequestPartial(Request::create('path/one', 'GET')); - - $this->assertEqual(count($routes->all()), 4, 'The correct number of routes was found.'); - $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.'); - $this->assertNull($routes->get('route_b'), 'The non-matching route was not found.'); - $this->assertNotNull($routes->get('route_c'), 'The second matching route was found.'); - $this->assertNotNull($routes->get('route_d'), 'The all-matching route was found.'); - $this->assertNotNull($routes->get('route_e'), 'The multi-matching route was found.'); - } - - /** - * Confirms we can nest multiple partial matchers. - */ - public function testNestedMatcher() { - - $matcher = new NestedMatcher(); - - $matcher->setInitialMatcher(new MockPathMatcher($this->fixtures->sampleRouteCollection())); - $matcher->addPartialMatcher(new HttpMethodMatcher()); - $matcher->setFinalMatcher(new FirstEntryFinalMatcher()); - - $request = Request::create('/path/one', 'GET'); - - $attributes = $matcher->matchRequest($request); - - $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.'); - } - - /** - * Confirms that the HttpMethod matcher throws an exception for no-route. - */ - public function testNoRouteFound() { - $matcher = new HttpMethodMatcher(); - - // Remove the sample route that would match any method. - $routes = $this->fixtures->sampleRouteCollection(); - $routes->remove('route_d'); - - $matcher->setCollection($routes); - - try { - $routes = $matcher->matchRequestPartial(Request::create('path/one', 'DELETE')); - $this->fail(t('No exception was thrown.')); - } - catch (Exception $e) { - $this->assertTrue($e instanceof MethodNotAllowedException, 'The correct exception was thrown.'); - } - - } -} diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MimeTypeMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MimeTypeMatcherTest.php index 05f8d42..07bb859 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/MimeTypeMatcherTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/MimeTypeMatcherTest.php @@ -47,52 +47,34 @@ function __construct($test_id = NULL) { public function testFilterRoutes() { $matcher = new MimeTypeMatcher(); - $matcher->setCollection($this->fixtures->sampleRouteCollection()); + $collection = $this->fixtures->sampleRouteCollection(); // Tests basic JSON request. $request = Request::create('path/two', 'GET'); $request->headers->set('Accept', 'application/json, text/xml;q=0.9'); - $routes = $matcher->matchRequestPartial($request); - $this->assertEqual(count($routes->all()), 4, 'The correct number of routes was found.'); + $routes = $matcher->filter($collection, $request); + $this->assertEqual(count($routes), 4, 'The correct number of routes was found.'); $this->assertNotNull($routes->get('route_c'), 'The json route was found.'); $this->assertNull($routes->get('route_e'), 'The html route was not found.'); // Tests JSON request with alternative JSON MIME type Accept header. $request = Request::create('path/two', 'GET'); $request->headers->set('Accept', 'application/x-json, text/xml;q=0.9'); - $routes = $matcher->matchRequestPartial($request); - $this->assertEqual(count($routes->all()), 4, 'The correct number of routes was found.'); + $routes = $matcher->filter($collection, $request); + $this->assertEqual(count($routes), 4, 'The correct number of routes was found.'); $this->assertNotNull($routes->get('route_c'), 'The json route was found.'); $this->assertNull($routes->get('route_e'), 'The html route was not found.'); // Tests basic HTML request. $request = Request::create('path/two', 'GET'); $request->headers->set('Accept', 'text/html, text/xml;q=0.9'); - $routes = $matcher->matchRequestPartial($request); - $this->assertEqual(count($routes->all()), 4, 'The correct number of routes was found.'); + $routes = $matcher->filter($collection, $request); + $this->assertEqual(count($routes), 4, 'The correct number of routes was found.'); $this->assertNull($routes->get('route_c'), 'The json route was not found.'); $this->assertNotNull($routes->get('route_e'), 'The html route was found.'); } /** - * Confirms we can nest multiple partial matchers. - */ - public function testNestedMatcher() { - - $matcher = new NestedMatcher(); - - $matcher->setInitialMatcher(new MockPathMatcher($this->fixtures->sampleRouteCollection())); - $matcher->addPartialMatcher(new MimeTypeMatcher()); - $matcher->setFinalMatcher(new FirstEntryFinalMatcher()); - - $request = Request::create('/path/two', 'GET'); - $request->headers->set('Accept', 'text/html, text/xml;q=0.9'); - - $attributes = $matcher->matchRequest($request); - $this->assertEqual($attributes['_route']->getOption('_name'), 'route_e', 'The correct matching route was found.'); - } - - /** * Confirms that the MimeTypeMatcher matcher throws an exception for no-route. */ public function testNoRouteFound() { @@ -105,12 +87,10 @@ public function testNoRouteFound() { $routes->remove('route_c'); $routes->remove('route_d'); - $matcher->setCollection($routes); - try { $request = Request::create('path/two', 'GET'); $request->headers->set('Accept', 'application/json, text/xml;q=0.9'); - $routes = $matcher->matchRequestPartial($request); + $routes = $matcher->filter($routes, $request); $this->fail(t('No exception was thrown.')); } catch (UnsupportedMediaTypeHttpException $e) { diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MockPathMatcher.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MockPathMatcher.php deleted file mode 100644 index 1592cbf..0000000 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/MockPathMatcher.php +++ /dev/null @@ -1,59 +0,0 @@ -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 matchRequestPartial(Request $request) { - // For now for testing we'll just do a straight string match. - - $path = $request->getPathInfo(); - - $return = new RouteCollection(); - - foreach ($this->routes as $name => $route) { - if ($route->getPattern() == $path) { - $return->add($name, $route); - } - } - - return $return; - } - - -} diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php deleted file mode 100644 index de29538..0000000 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php +++ /dev/null @@ -1,65 +0,0 @@ - 'NestedMatcher tests', - 'description' => 'Confirm that the NestedMatcher system is working properly.', - 'group' => 'Routing', - ); - } - - function __construct($test_id = NULL) { - parent::__construct($test_id); - - $this->fixtures = new RoutingFixtures(); - } - - /** - * Confirms we can nest multiple partial matchers. - */ - public function testNestedMatcher() { - - $matcher = new NestedMatcher(); - - $matcher->setInitialMatcher(new MockPathMatcher($this->fixtures->sampleRouteCollection())); - $matcher->addPartialMatcher(new HttpMethodMatcher(), 1); - $matcher->setFinalMatcher(new FirstEntryFinalMatcher()); - - $request = Request::create('/path/one', 'GET'); - - $attributes = $matcher->matchRequest($request); - - $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.'); - } -} diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php similarity index 81% rename from core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php rename to core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php index 6769107..9ebd2ed 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\system\Tests\Routing\PartialMatcherTest. + * Definition of Drupal\system\Tests\Routing\RouteProviderTest. */ namespace Drupal\system\Tests\Routing; @@ -13,16 +13,16 @@ use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Drupal\simpletest\UnitTestBase; -use Drupal\Core\Routing\PathMatcher; +use Drupal\Core\Routing\RouteProvider; use Drupal\Core\Database\Database; use Drupal\Core\Routing\MatcherDumper; use Exception; /** - * Basic tests for the UrlMatcherDumper. + * Basic tests for the RouteProvider. */ -class PathMatcherTest extends UnitTestBase { +class RouteProviderTest extends UnitTestBase { /** * A collection of shared fixture data for tests. @@ -33,8 +33,8 @@ class PathMatcherTest extends UnitTestBase { public static function getInfo() { return array( - 'name' => 'Path matcher tests', - 'description' => 'Confirm that the path matching library is working correctly.', + 'name' => 'Route Provider tests', + 'description' => 'Confirm that the default route provider is working correctly.', 'group' => 'Routing', ); } @@ -57,11 +57,11 @@ public function tearDown() { public function testCandidateOutlines() { $connection = Database::getConnection(); - $matcher = new PathMatcher($connection); + $provider = new RouteProvider($connection); $parts = array('node', '5', 'edit'); - $candidates = $matcher->getCandidateOutlines($parts); + $candidates = $provider->getCandidateOutlines($parts); $candidates = array_flip($candidates); @@ -77,7 +77,7 @@ public function testCandidateOutlines() { */ function testExactPathMatch() { $connection = Database::getConnection(); - $matcher = new PathMatcher($connection, 'test_routes'); + $provider = new RouteProvider($connection, 'test_routes'); $this->fixtures->createTables($connection); @@ -89,7 +89,7 @@ function testExactPathMatch() { $request = Request::create($path, 'GET'); - $routes = $matcher->matchRequestPartial($request); + $routes = $provider->getRouteCollectionForRequest($request); foreach ($routes as $route) { $this->assertEqual($route->getPattern(), $path, 'Found path has correct pattern'); @@ -101,7 +101,7 @@ function testExactPathMatch() { */ function testOutlinePathMatch() { $connection = Database::getConnection(); - $matcher = new PathMatcher($connection, 'test_routes'); + $provider = new RouteProvider($connection, 'test_routes'); $this->fixtures->createTables($connection); @@ -113,14 +113,14 @@ function testOutlinePathMatch() { $request = Request::create($path, 'GET'); - $routes = $matcher->matchRequestPartial($request); + $routes = $provider->getRouteCollectionForRequest($request); // All of the matching paths have the correct pattern. foreach ($routes as $route) { $this->assertEqual($route->compile()->getPatternOutline(), '/path/%/one', 'Found path has correct pattern'); } - $this->assertEqual(count($routes->all()), 2, 'The correct number of routes was found.'); + $this->assertEqual(count($routes), 2, 'The correct number of routes was found.'); $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.'); $this->assertNotNull($routes->get('route_b'), 'The second matching route was not found.'); } @@ -130,7 +130,7 @@ function testOutlinePathMatch() { */ function testOutlinePathMatchTrailingSlash() { $connection = Database::getConnection(); - $matcher = new PathMatcher($connection, 'test_routes'); + $provider = new RouteProvider($connection, 'test_routes'); $this->fixtures->createTables($connection); @@ -142,14 +142,14 @@ function testOutlinePathMatchTrailingSlash() { $request = Request::create($path, 'GET'); - $routes = $matcher->matchRequestPartial($request); + $routes = $provider->getRouteCollectionForRequest($request); // All of the matching paths have the correct pattern. foreach ($routes as $route) { $this->assertEqual($route->compile()->getPatternOutline(), '/path/%/one', 'Found path has correct pattern'); } - $this->assertEqual(count($routes->all()), 2, 'The correct number of routes was found.'); + $this->assertEqual(count($routes), 2, 'The correct number of routes was found.'); $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.'); $this->assertNotNull($routes->get('route_b'), 'The second matching route was not found.'); } @@ -159,7 +159,7 @@ function testOutlinePathMatchTrailingSlash() { */ function testOutlinePathMatchDefaults() { $connection = Database::getConnection(); - $matcher = new PathMatcher($connection, 'test_routes'); + $provider = new RouteProvider($connection, 'test_routes'); $this->fixtures->createTables($connection); @@ -177,15 +177,14 @@ function testOutlinePathMatchDefaults() { $request = Request::create($path, 'GET'); try { - $routes = $matcher->matchRequestPartial($request); + $routes = $provider->getRouteCollectionForRequest($request); // All of the matching paths have the correct pattern. foreach ($routes as $route) { - $compiled = $route->compile(); $this->assertEqual($route->compile()->getPatternOutline(), '/some/path', 'Found path has correct pattern'); } - $this->assertEqual(count($routes->all()), 1, 'The correct number of routes was found.'); + $this->assertEqual(count($routes), 1, 'The correct number of routes was found.'); $this->assertNotNull($routes->get('poink'), 'The first matching route was found.'); } catch (ResourceNotFoundException $e) { @@ -198,7 +197,7 @@ function testOutlinePathMatchDefaults() { */ function testOutlinePathMatchDefaultsCollision() { $connection = Database::getConnection(); - $matcher = new PathMatcher($connection, 'test_routes'); + $provider = new RouteProvider($connection, 'test_routes'); $this->fixtures->createTables($connection); @@ -217,15 +216,14 @@ function testOutlinePathMatchDefaultsCollision() { $request = Request::create($path, 'GET'); try { - $routes = $matcher->matchRequestPartial($request); + $routes = $provider->getRouteCollectionForRequest($request); // All of the matching paths have the correct pattern. foreach ($routes as $route) { - $compiled = $route->compile(); $this->assertEqual($route->compile()->getPatternOutline(), '/some/path', 'Found path has correct pattern'); } - $this->assertEqual(count($routes->all()), 1, 'The correct number of routes was found.'); + $this->assertEqual(count($routes), 1, 'The correct number of routes was found.'); $this->assertNotNull($routes->get('poink'), 'The first matching route was found.'); } catch (ResourceNotFoundException $e) { @@ -238,7 +236,7 @@ function testOutlinePathMatchDefaultsCollision() { */ function testOutlinePathMatchDefaultsCollision2() { $connection = Database::getConnection(); - $matcher = new PathMatcher($connection, 'test_routes'); + $provider = new RouteProvider($connection, 'test_routes'); $this->fixtures->createTables($connection); @@ -257,14 +255,14 @@ function testOutlinePathMatchDefaultsCollision2() { $request = Request::create($path, 'GET'); try { - $routes = $matcher->matchRequestPartial($request); + $routes = $provider->getRouteCollectionForRequest($request); // All of the matching paths have the correct pattern. foreach ($routes as $route) { $this->assertEqual($route->compile()->getPatternOutline(), '/some/path/here', 'Found path has correct pattern'); } - $this->assertEqual(count($routes->all()), 1, 'The correct number of routes was found.'); + $this->assertEqual(count($routes), 1, 'The correct number of routes was found.'); $this->assertNotNull($routes->get('narf'), 'The first matching route was found.'); } catch (ResourceNotFoundException $e) { @@ -277,7 +275,7 @@ function testOutlinePathMatchDefaultsCollision2() { */ function testOutlinePathNoMatch() { $connection = Database::getConnection(); - $matcher = new PathMatcher($connection, 'test_routes'); + $provider = new RouteProvider($connection, 'test_routes'); $this->fixtures->createTables($connection); @@ -290,13 +288,12 @@ function testOutlinePathNoMatch() { $request = Request::create($path, 'GET'); try { - $routes = $matcher->matchRequestPartial($request); + $routes = $provider->getRouteCollectionForRequest($request); $this->fail(t('No exception was thrown.')); } catch (Exception $e) { $this->assertTrue($e instanceof ResourceNotFoundException, 'The correct exception was thrown.'); } - } /** @@ -304,7 +301,7 @@ function testOutlinePathNoMatch() { */ function testSystemPathMatch() { $connection = Database::getConnection(); - $matcher = new PathMatcher($connection, 'test_routes'); + $provider = new RouteProvider($connection, 'test_routes'); $this->fixtures->createTables($connection); @@ -315,7 +312,7 @@ function testSystemPathMatch() { $request = Request::create('/path/one', 'GET'); $request->attributes->set('system_path', 'path/two'); - $routes = $matcher->matchRequestPartial($request); + $routes = $provider->getRouteCollectionForRequest($request); foreach ($routes as $route) { $this->assertEqual($route->getPattern(), '/path/two', 'Found path has correct pattern'); diff --git a/core/vendor/autoload.php b/core/vendor/autoload.php index daa7ee7..7bc9123 100644 --- a/core/vendor/autoload.php +++ b/core/vendor/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer' . '/autoload_real.php'; -return ComposerAutoloaderInit295209ab8f7c3b45c210d28fa6db3592::getLoader(); +return ComposerAutoloaderInit23a41d5f637bb8e297fc063ef4ab931a::getLoader(); diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php index 9072401..ecb4b19 100644 --- a/core/vendor/composer/autoload_namespaces.php +++ b/core/vendor/composer/autoload_namespaces.php @@ -16,6 +16,7 @@ 'Symfony\\Component\\EventDispatcher\\' => $vendorDir . '/symfony/event-dispatcher/', 'Symfony\\Component\\DependencyInjection\\' => $vendorDir . '/symfony/dependency-injection/', 'Symfony\\Component\\ClassLoader\\' => $vendorDir . '/symfony/class-loader/', + 'Symfony\\Cmf\\Component\\Routing' => $vendorDir . '/symfony-cmf/routing/', 'Guzzle\\Stream' => $vendorDir . '/guzzle/stream/', 'Guzzle\\Parser' => $vendorDir . '/guzzle/parser/', 'Guzzle\\Http' => $vendorDir . '/guzzle/http/', diff --git a/core/vendor/composer/autoload_real.php b/core/vendor/composer/autoload_real.php index dbe463c..9634e0b 100644 --- a/core/vendor/composer/autoload_real.php +++ b/core/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php generated by Composer -class ComposerAutoloaderInit295209ab8f7c3b45c210d28fa6db3592 +class ComposerAutoloaderInit23a41d5f637bb8e297fc063ef4ab931a { private static $loader; @@ -19,9 +19,9 @@ public static function getLoader() return static::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit295209ab8f7c3b45c210d28fa6db3592', 'loadClassLoader')); + spl_autoload_register(array('ComposerAutoloaderInit23a41d5f637bb8e297fc063ef4ab931a', 'loadClassLoader')); static::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit295209ab8f7c3b45c210d28fa6db3592', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit23a41d5f637bb8e297fc063ef4ab931a', 'loadClassLoader')); $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json index aaa926b..9dd2912 100644 --- a/core/vendor/composer/installed.json +++ b/core/vendor/composer/installed.json @@ -852,5 +852,56 @@ "client", "Guzzle" ] + }, + { + "name": "symfony-cmf/routing", + "version": "dev-master", + "version_normalized": "9999999-dev", + "target-dir": "Symfony/Cmf/Component/Routing", + "source": { + "type": "git", + "url": "https://github.com/symfony-cmf/Routing", + "reference": "1.0.0-alpha3" + }, + "dist": { + "type": "zip", + "url": "https://github.com/symfony-cmf/Routing/archive/1.0.0-alpha3.zip", + "reference": "1.0.0-alpha3", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "symfony/routing": ">=2.1,<2.3-dev", + "symfony/http-kernel": ">=2.1,<2.3-dev" + }, + "time": "2012-12-16 17:52:57", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "source", + "autoload": { + "psr-0": { + "Symfony\\Cmf\\Component\\Routing": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony CMF Community", + "homepage": "https://github.com/symfony-cmf/Routing/contributors" + } + ], + "description": "Extends the Symfony2 routing component for dynamic routes and chaining several routers", + "homepage": "http://cmf.symfony.com", + "keywords": [ + "database", + "routing" + ] } ] diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/.travis.yml b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/.travis.yml new file mode 100644 index 0000000..8bae46c --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/.travis.yml @@ -0,0 +1,15 @@ +language: php + +env: + - SYMFONY_VERSION=2.1.* + - SYMFONY_VERSION=dev-master + +before_script: + - composer require symfony/routing:${SYMFONY_VERSION} + - composer install --dev + +script: phpunit --coverage-text + +notifications: + irc: "irc.freenode.org#symfony-cmf" + email: "symfony-cmf-devs@googlegroups.com" diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainRouter.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainRouter.php new file mode 100644 index 0000000..6160fc3 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainRouter.php @@ -0,0 +1,269 @@ + + * @author Magnus Nordlander + */ +class ChainRouter implements RouterInterface, RequestMatcherInterface, WarmableInterface +{ + /** + * @var \Symfony\Component\Routing\RequestContext + */ + private $context; + + /** + * @var Symfony\Component\Routing\RouterInterface[] + */ + private $routers = array(); + + /** + * @var \Symfony\Component\Routing\RouterInterface[] Array of routers, sorted by priority + */ + private $sortedRouters; + + /** + * @var \Symfony\Component\Routing\RouteCollection + */ + private $routeCollection; + + /** + * @var null|\Symfony\Component\HttpKernel\Log\LoggerInterface + */ + protected $logger; + + /** + * @param LoggerInterface $logger + */ + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + /** + * @return RequestContext + */ + public function getContext() + { + return $this->context; + } + + /** + * Add a Router to the index + * + * @param RouterInterface $router The router instance + * @param integer $priority The priority + */ + public function add(RouterInterface $router, $priority = 0) + { + if (empty($this->routers[$priority])) { + $this->routers[$priority] = array(); + } + + $this->routers[$priority][] = $router; + $this->sortedRouters = array(); + } + + /** + * Sorts the routers and flattens them. + * + * @return array + */ + public function all() + { + if (empty($this->sortedRouters)) { + $this->sortedRouters = $this->sortRouters(); + + // setContext() is done here instead of in add() to avoid fatal errors when clearing and warming up caches + // See https://github.com/symfony-cmf/Routing/pull/18 + $context = $this->getContext(); + if (null !== $context) { + foreach ($this->sortedRouters as $router) { + if ($router instanceof RequestContextAwareInterface) { + $router->setContext($context); + } + } + } + } + + return $this->sortedRouters; + } + + /** + * Sort routers by priority. + * The highest priority number is the highest priority (reverse sorting) + * + * @return RouterInterface[] + */ + protected function sortRouters() + { + $sortedRouters = array(); + krsort($this->routers); + + foreach ($this->routers as $routers) { + $sortedRouters = array_merge($sortedRouters, $routers); + } + + return $sortedRouters; + } + + /** + * {@inheritdoc} + * + * Loops through all routes and tries to match the passed url. + * + * Note: You should use matchRequest if you can. + */ + public function match($url) + { + $methodNotAllowed = null; + + /** @var $router ChainedRouterInterface */ + foreach ($this->all() as $router) { + try { + return $router->match($url); + } catch (ResourceNotFoundException $e) { + if ($this->logger) { + $this->logger->info('Router '.get_class($router).' was not able to match, message "'.$e->getMessage().'"'); + } + // Needs special care + } catch (MethodNotAllowedException $e) { + if ($this->logger) { + $this->logger->info('Router '.get_class($router).' throws MethodNotAllowedException with message "'.$e->getMessage().'"'); + } + $methodNotAllowed = $e; + } + } + + throw $methodNotAllowed ?: new ResourceNotFoundException("None of the routers in the chain matched '$url'"); + } + + /** + * {@inheritdoc} + * + * Loops through all routes and tries to match the passed request. + */ + public function matchRequest(Request $request) + { + $methodNotAllowed = null; + + foreach ($this->all() as $router) { + try { + // the request/url match logic is the same as in Symfony/Component/HttpKernel/EventListener/RouterListener.php + // matching requests is more powerful than matching URLs only, so try that first + if ($router instanceof RequestMatcherInterface) { + return $router->matchRequest($request); + } + return $router->match($request->getPathInfo()); + } catch (ResourceNotFoundException $e) { + if ($this->logger) { + $this->logger->info('Router '.get_class($router).' was not able to match, message "'.$e->getMessage().'"'); + } + // Needs special care + } catch (MethodNotAllowedException $e) { + if ($this->logger) { + $this->logger->info('Router '.get_class($router).' throws MethodNotAllowedException with message "'.$e->getMessage().'"'); + } + $methodNotAllowed = $e; + } + } + + throw $methodNotAllowed ?: new ResourceNotFoundException("None of the routers in the chain matched this request"); + } + + /** + * {@inheritdoc} + * + * Loops through all registered routers and returns a router if one is found. + * It will always return the first route generated. + */ + public function generate($name, $parameters = array(), $absolute = false) + { + /** @var $router ChainedRouterInterface */ + foreach ($this->all() as $router) { + + // if $name and $router does not implement ChainedRouterInterface and $name is not a string, continue + // if $name and $router does not implement ChainedRouterInterface and $name is string but does not match a default Symfony2 route name, continue + if ($name && !$router instanceof ChainedRouterInterface) { + if (!is_string($name) || !preg_match('/^[a-z0-9A-Z_.]+$/', $name)) { + continue; + } + } + + // If $router implements ChainedRouterInterface but doesn't support this route name, continue + if ($router instanceof ChainedRouterInterface && !$router->supports($name)) { + continue; + } + + try { + return $router->generate($name, $parameters, $absolute); + } catch (RouteNotFoundException $e) { + if ($this->logger) { + $this->logger->info($e->getMessage()); + } + } + } + + throw new RouteNotFoundException(sprintf('None of the chained routers were able to generate route "%s".', $name)); + } + + /** + * {@inheritdoc} + */ + public function setContext(RequestContext $context) + { + foreach ($this->all() as $router) { + if ($router instanceof RequestContextAwareInterface) { + $router->setContext($context); + } + } + + $this->context = $context; + } + + /** + * {@inheritdoc} + * + * check for each contained router if it can warmup + */ + public function warmUp($cacheDir) + { + foreach ($this->all() as $router) { + if ($router instanceof WarmableInterface) { + $router->warmUp($cacheDir); + } + } + } + + /** + * {@inheritdoc} + */ + public function getRouteCollection() + { + if (!$this->routeCollection instanceof RouteCollection) { + $this->routeCollection = new RouteCollection(); + foreach ($this->all() as $router) { + $this->routeCollection->addCollection($router->getRouteCollection()); + } + } + + return $this->routeCollection; + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainedRouterInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainedRouterInterface.php new file mode 100644 index 0000000..a4247aa --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainedRouterInterface.php @@ -0,0 +1,25 @@ +contentRepository = $contentRepository; + } + + /** + * {@inheritDoc} + * + * @param string $name ignored + * @param array $parameters must either contain the field 'route' with a + * RouteObjectInterface or the field 'content' with the document + * instance to get the route for (implementing RouteAwareInterface) + * + * @throws RouteNotFoundException If there is no such route in the database + */ + public function generate($name, $parameters = array(), $absolute = false) + { + if ($name instanceof SymfonyRoute) { + $route = $this->getBestLocaleRoute($name, $parameters); + } elseif (is_string($name) && $name) { + $route = $this->getRouteByName($name, $parameters); + } else { + $route = $this->getRouteByContent($name, $parameters); + } + + if (! $route instanceof SymfonyRoute) { + $hint = is_object($route) ? get_class($route) : gettype($route); + throw new RouteNotFoundException('Route of this document is not an instance of Symfony\Component\Routing\Route but: '.$hint); + } + + return parent::generate($route, $parameters, $absolute); + } + + /** + * Get the route by a string name + * + * @param string $route + * @param array $parameters + * + * @return SymfonyRoute + * + * @throws RouteNotFoundException if there is no route found for the provided name + */ + protected function getRouteByName($name, array $parameters) + { + $route = $this->provider->getRouteByName($name, $parameters); + if (empty($route)) { + throw new RouteNotFoundException('No route found for name: ' . $name); + } + + return $this->getBestLocaleRoute($route, $parameters); + } + + /** + * Determine if there is a route with matching locale associated with the + * given route via associated content. + * + * @param SymfonyRoute $route + * @param array $parameters + * + * @return SymfonyRoute either the passed route or an alternative with better locale + */ + protected function getBestLocaleRoute(SymfonyRoute $route, $parameters) + { + if (! $route instanceof RouteObjectInterface) { + // this route has no content, we can't get the alternatives + return $route; + } + $locale = $this->getLocale($parameters); + if (! $this->checkLocaleRequirement($route, $locale)) { + $content = $route->getRouteContent(); + if ($content instanceof RouteAwareInterface) { + $routes = $content->getRoutes(); + $contentRoute = $this->getRouteByLocale($routes, $locale); + if ($contentRoute) { + return $contentRoute; + } + } + } + + return $route; + } + + /** + * Get the route based on the content field in parameters + * + * Called in generate when there is no route given in the parameters. + * + * If there is more than one route for the content, tries to find the + * first one that matches the _locale (provided in $parameters or otherwise + * defaulting to the request locale). + * + * If none is found, falls back to just return the first route. + * + * @param mixed $name + * @param array $parameters which should contain a content field containing a RouteAwareInterface object + * + * @return SymfonyRoute the route instance + * + * @throws RouteNotFoundException if there is no content field in the + * parameters or its not possible to build a route from that object + */ + protected function getRouteByContent($name, &$parameters) + { + if ($name instanceof RouteAwareInterface) { + $content = $name; + } elseif (isset($parameters['content_id']) && null !== $this->contentRepository) { + $content = $this->contentRepository->findById($parameters['content_id']); + } elseif (isset($parameters['content'])) { + $content = $parameters['content']; + } + + unset($parameters['content'], $parameters['content_id']); + + if (empty($content)) { + throw new RouteNotFoundException('Neither the route name, nor a parameter "content" or "content_id" could be resolved to an content instance'); + } + + if (!$content instanceof RouteAwareInterface) { + $hint = is_object($content) ? get_class($content) : gettype($content); + throw new RouteNotFoundException('The content does not implement RouteAwareInterface: ' . $hint); + } + + $routes = $content->getRoutes(); + if (empty($routes)) { + $hint = method_exists($content, 'getPath') ? $content->getPath() : get_class($content); + throw new RouteNotFoundException('Document has no route: ' . $hint); + } + + $route = $this->getRouteByLocale($routes, $this->getLocale($parameters)); + if ($route) { + return $route; + } + + // if none matched, continue and randomly return the first one + return reset($routes); + } + + /** + * @param RouteCollection $routes + * @param string $locale + * + * @return bool|SymfonyRoute false if no route requirement matches the provided locale + */ + protected function getRouteByLocale($routes, $locale) + { + foreach ($routes as $route) { + if (! $route instanceof SymfonyRoute) { + continue; + } + + if ($this->checkLocaleRequirement($route, $locale)) { + return $route; + } + } + + return false; + } + + /** + * @param SymfonyRoute $route + * @param string $locale + * + * @return bool TRUE if there is either no _locale, no _locale requirement or if the two match + */ + private function checkLocaleRequirement(SymfonyRoute $route, $locale) + { + return empty($locale) + || !$route->getRequirement('_locale') + || preg_match('/'.$route->getRequirement('_locale').'/', $locale) + ; + } + + /** + * Determine the locale to be used with this request + * + * @param array $parameters the parameters determined by the route + * + * @return string|null the locale following of the parameters or any other + * information the router has available. + */ + protected function getLocale($parameters) + { + if (isset($parameters['_locale'])) { + return $parameters['_locale']; + } + + return null; + } + + /** + * We additionally support empty name and data in parameters and RouteAware content + */ + public function supports($name) + { + return ! $name || parent::supports($name) || $name instanceof RouteAwareInterface; + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ContentRepositoryInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ContentRepositoryInterface.php new file mode 100644 index 0000000..415cd7d --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ContentRepositoryInterface.php @@ -0,0 +1,27 @@ +context = $context; + if (! $matcher instanceof RequestMatcherInterface && ! $matcher instanceof UrlMatcherInterface) { + throw new \InvalidArgumentException('Invalid $matcher'); + } + $this->matcher = $matcher; + $this->generator = $generator; + $this->uriFilterRegexp = $uriFilterRegexp; + + $this->generator->setContext($context); + } + + /** + * Not implemented. + */ + public function getRouteCollection() + { + return new RouteCollection(); + } + + /** + * @return RequestMatcherInterface|UrlMatcherInterface + */ + public function getMatcher() + { + // we may not set the context in DynamicRouter::setContext as this would lead to symfony cache warmup problems + // a request matcher does not need the request context separately as it can get it from the request. + if ($this->matcher instanceof RequestContextAwareInterface) { + $this->matcher->setContext($this->getContext()); + } + + return $this->matcher; + } + + /** + * @return UrlGeneratorInterface + */ + public function getGenerator() + { + $this->generator->setContext($this->getContext()); + + return $this->generator; + } + + /** + * Generates a URL from the given parameters. + * + * If the generator is not able to generate the url, it must throw the RouteNotFoundException + * as documented below. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param Boolean $absolute Whether to generate an absolute URL + * + * @return string The generated URL + * + * @throws RouteNotFoundException if route doesn't exist + * + * @api + */ + public function generate($name, $parameters = array(), $absolute = false) + { + return $this->getGenerator()->generate($name, $parameters, $absolute); + } + + /** + * Support any string as route name + * + * {@inheritDoc} + */ + public function supports($name) + { + // TODO: check $this->generator instanceof VersatileGeneratorInterface + return $this->generator->supports($name); + } + + /** + * Tries to match a URL path with a set of routes. + * + * If the matcher can not find information, it must throw one of the + * exceptions documented below. + * + * @param string $pathinfo The path info to be parsed (raw format, i.e. not urldecoded) + * + * @return array An array of parameters + * + * @throws ResourceNotFoundException If the resource could not be found + * @throws MethodNotAllowedException If the resource was found but the request method is not allowed + * + * @api + */ + public function match($pathinfo) + { + if (! empty($this->uriFilterRegexp) && ! preg_match($this->uriFilterRegexp, $pathinfo)) { + throw new ResourceNotFoundException("$pathinfo does not match the '{$this->uriFilterRegexp}' pattern"); + } + + $matcher = $this->getMatcher(); + if (! $matcher instanceof UrlMatcherInterface) { + throw new \InvalidArgumentException('Wrong matcher type, you need to call matchRequest'); + } + + $defaults = $matcher->match($pathinfo); + + return $this->applyRouteEnhancers($defaults, Request::create($pathinfo)); + } + + /** + * Tries to match a request with a set of routes and returns the array of + * information for that route. + * + * If the matcher can not find information, it must throw one of the + * exceptions documented below. + * + * @param Request $request The request to match + * + * @return array An array of parameters + * + * @throws ResourceNotFoundException If no matching resource could be found + * @throws MethodNotAllowedException If a matching resource was found but the request method is not allowed + */ + public function matchRequest(Request $request) + { + if (! empty($this->uriFilterRegexp) && ! preg_match($this->uriFilterRegexp, $request->getPathInfo())) { + throw new ResourceNotFoundException("{$request->getPathInfo()} does not match the '{$this->uriFilterRegexp}' pattern"); + } + + $matcher = $this->getMatcher(); + if ($matcher instanceof UrlMatcherInterface) { + return $this->match($request->getPathInfo()); + } + + $defaults = $matcher->matchRequest($request); + + return $this->applyRouteEnhancers($defaults, $request); + } + + /** + * Apply the route enhancers to the defaults, according to priorities + * + * @param array $defaults + * @param Request $request + * @return array + */ + protected function applyRouteEnhancers($defaults, Request $request) + { + foreach ($this->getRouteEnhancers() as $enhancer) { + $defaults = $enhancer->enhance($defaults, $request); + } + return $defaults; + } + + /** + * Add route enhancers to the router to let them generate information on + * matched routes. + * + * The order of the enhancers is determined by the priority, the higher the + * value, the earlier the enhancer is run. + * + * @param RouteEnhancerInterface $enhancer + * @param int $priority + */ + public function addRouteEnhancer(RouteEnhancerInterface $enhancer, $priority = 0) + { + if (empty($this->enhancers[$priority])) { + $this->enhancers[$priority] = array(); + } + + $this->enhancers[$priority][] = $enhancer; + $this->sortedEnhancers = array(); + + return $this; + } + + /** + * Sorts the enhancers and flattens them. + * + * @return RouteEnhancerInterface[] the enhancers ordered by priority + */ + public function getRouteEnhancers() + { + if (empty($this->sortedEnhancers)) { + $this->sortedEnhancers = $this->sortRouteEnhancers(); + } + + return $this->sortedEnhancers; + } + + /** + * Sort enhancers by priority. + * + * The highest priority number is the highest priority (reverse sorting). + * + * @return RouteEnhancerInterface[] the sorted enhancers + */ + protected function sortRouteEnhancers() + { + $sortedEnhancers = array(); + krsort($this->enhancers); + + foreach ($this->enhancers as $enhancers) { + $sortedEnhancers = array_merge($sortedEnhancers, $enhancers); + } + + return $sortedEnhancers; + } + + /** + * Sets the request context. + * + * @param RequestContext $context The context + * + * @api + */ + public function setContext(RequestContext $context) + { + $this->context = $context; + } + + /** + * Gets the request context. + * + * @return RequestContext The context + * + * @api + */ + public function getContext() + { + return $this->context; + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldByClassEnhancer.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldByClassEnhancer.php new file mode 100644 index 0000000..23bc714 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldByClassEnhancer.php @@ -0,0 +1,78 @@ +source = $source; + $this->target = $target; + $this->map = $map; + } + + /** + * If the source field is instance of one of the entries in the map, + * target is set to the value of that map entry. + * + * {@inheritDoc} + */ + public function enhance(array $defaults, Request $request) + { + if (isset($defaults[$this->target])) { + // no need to do anything + return $defaults; + } + + if (! isset($defaults[$this->source])) { + return $defaults; + } + + // we need to loop over the array and do instanceof in case the content + // class extends the specified class + // i.e. phpcr-odm generates proxy class for the content. + foreach ($this->map as $class => $value) { + if ($defaults[$this->source] instanceof $class) { + // found a matching entry in the map + $defaults[$this->target] = $value; + + return $defaults; + } + } + + return $defaults; + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldMapEnhancer.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldMapEnhancer.php new file mode 100644 index 0000000..53f6c0e --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldMapEnhancer.php @@ -0,0 +1,61 @@ +source = $source; + $this->target = $target; + $this->hashmap = $hashmap; + } + + /** + * If the target field is not set but the source field is, map the field + * + * {@inheritDoc} + */ + public function enhance(array $defaults, Request $request) + { + if (isset($defaults[$this->target])) { + return $defaults; + } + if (! isset($defaults[$this->source])) { + return $defaults; + } + if (! isset($this->hashmap[$defaults[$this->source]])) { + return $defaults; + } + + $defaults[$this->target] = $this->hashmap[$defaults[$this->source]]; + + return $defaults; + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldPresenceEnhancer.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldPresenceEnhancer.php new file mode 100644 index 0000000..9eb3a2a --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldPresenceEnhancer.php @@ -0,0 +1,60 @@ +source = $source; + $this->target = $target; + $this->value = $value; + } + + /** + * {@inheritDoc} + */ + public function enhance(array $defaults, Request $request) + { + if (isset($defaults[$this->target])) { + // no need to do anything + return $defaults; + } + + if (! isset($defaults[$this->source])) { + return $defaults; + } + + $defaults[$this->target] = $this->value; + + return $defaults; + } + +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteContentEnhancer.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteContentEnhancer.php new file mode 100644 index 0000000..39e54d8 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteContentEnhancer.php @@ -0,0 +1,70 @@ +routefield = $routefield; + $this->target = $target; + } + + /** + * If the route has a non-null content and if that content class is in the + * injected map, returns that controller. + * + * {@inheritDoc} + */ + public function enhance(array $defaults, Request $request) + { + if (isset($defaults[$this->target])) { + // no need to do anything + return $defaults; + } + + if (! isset($defaults[$this->routefield]) + || ! $defaults[$this->routefield] instanceof RouteObjectInterface + ) { + // we can't determine the content + return $defaults; + } + $route = $defaults[$this->routefield]; + + $content = $route->getRouteContent(); + if (! $content) { + // we have no content + return $defaults; + } + $defaults[$this->target] = $content; + + return $defaults; + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteEnhancerInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteEnhancerInterface.php new file mode 100644 index 0000000..5932c71 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteEnhancerInterface.php @@ -0,0 +1,27 @@ +matcherClass = $matcherClass; + } + + /** + * {@inheritdoc} + */ + public function finalMatch(RouteCollection $collection, Request $request) + { + $context = new RequestContext(); + $context->fromRequest($request); + $matcher = $this->getMatcher($collection, $context); + $attributes = $matcher->match($request->getPathInfo()); + + // cleanup route attributes + if (! isset($attributes['_route']) || ! $attributes['_route'] instanceof Route) { + $name = $attributes['_route']; + $route = $collection->get($attributes['_route']); + $attributes['_route'] = $route; + + if ($route instanceof RouteObjectInterface && is_string($route->getRouteKey())) { + $name = $route->getRouteKey(); + } + + if (empty($attributes['_route_name']) && is_string($name)) { + $attributes['_route_name'] = $name; + } + } + + return $attributes; + } + + /** + * @param RouteCollection $collection the route collection to match + * @param RequestContext $context the context to match in + * + * @return \Symfony\Component\Routing\Matcher\UrlMatcherInterface + */ + protected function getMatcher(RouteCollection $collection, RequestContext $context) + { + return new $this->matcherClass($collection, $context); + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/FinalMatcherInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/FinalMatcherInterface.php new file mode 100644 index 0000000..b48859c --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/FinalMatcherInterface.php @@ -0,0 +1,29 @@ +routeProvider = $provider; + } + + /** + * Sets the route provider for the matching plan. + * + * @param RouteProviderInterface $provider A route provider. It is responsible for its own configuration. + * + * @return NestedMatcher this object to have a fluent interface + */ + public function setRouteProvider(RouteProviderInterface $provider) + { + $this->routeProvider = $provider; + + return $this; + } + + /** + * Adds a partial matcher to the matching plan. + * + * Partial matchers will be run in the order in which they are added. + * + * @param RouteFilterInterface $filter + * @param int $priority (optional) The priority of the filter. Higher number filters will be used first. Default to 0. + * + * @return NestedMatcher this object to have a fluent interface + */ + public function addRouteFilter(RouteFilterInterface $filter, $priority = 0) + { + if (empty($this->filters[$priority])) { + $this->filters[$priority] = array(); + } + + $this->filters[$priority][] = $filter; + $this->sortedFilters = array(); + + return $this; + } + + /** + * Sets the final matcher for the matching plan. + * + * @param FinalMatcherInterface $final The final matcher that will have to pick the route that will be used. + * + * @return NestedMatcher this object to have a fluent interface + */ + public function setFinalMatcher(FinalMatcherInterface $final) + { + $this->finalMatcher = $final; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function matchRequest(Request $request) + { + $collection = $this->routeProvider->getRouteCollectionForRequest($request); + if (!count($collection)) { + throw new ResourceNotFoundException(); + } + + // Route Filters are expected to throw an exception themselves if they + // end up filtering the list down to 0. + foreach ($this->getRouteFilters() as $filter) { + $collection = $filter->filter($collection, $request); + } + + $attributes = $this->finalMatcher->finalMatch($collection, $request); + + return $attributes; + } + + /** + * Sorts the filters and flattens them. + * + * @return RouteFilterInterface[] the filters ordered by priority + */ + public function getRouteFilters() + { + if (empty($this->sortedFilters)) { + $this->sortedFilters = $this->sortFilters(); + } + + return $this->sortedFilters; + } + + /** + * Sort filters by priority. + * + * The highest priority number is the highest priority (reverse sorting). + * + * @return RouteFilterInterface[] the sorted filters + */ + protected function sortFilters() + { + $sortedFilters = array(); + krsort($this->filters); + + foreach ($this->filters as $filters) { + $sortedFilters = array_merge($sortedFilters, $filters); + } + + return $sortedFilters; + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/RouteFilterInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/RouteFilterInterface.php new file mode 100644 index 0000000..7fe4ca9 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/RouteFilterInterface.php @@ -0,0 +1,36 @@ +routes = $collection; + $context = new RequestContext(); + $context->fromRequest($request); + $this->setContext($context); + return $this->match($request->getPathInfo()); + } + + /** + * {@inheritdoc} + */ + protected function getAttributes(Route $route, $name, array $attributes) + { + if ($route instanceof RouteObjectInterface && is_string($route->getRouteKey())) { + $name = $route->getRouteKey(); + } + $attributes['_route_name'] = $name; + $attributes['_route'] = $route; + return $this->mergeDefaults($attributes, $route->getDefaults()); + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ProviderBasedGenerator.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ProviderBasedGenerator.php new file mode 100644 index 0000000..1c353f8 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ProviderBasedGenerator.php @@ -0,0 +1,67 @@ +provider = $provider; + $this->logger = $logger; + } + + /** + * {@inheritDoc} + */ + public function generate($name, $parameters = array(), $absolute = false) + { + if ($name instanceof SymfonyRoute) { + $route = $name; + } elseif (null === $route = $this->provider->getRouteByName($name, $parameters)) { + throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name)); + } + + // the Route has a cache of its own and is not recompiled as long as it does not get modified + $compiledRoute = $route->compile(); + + // handle symfony 2.1 and 2.2 + // getHostnameTokens exists only since 2.2 + $hostnameTokens = null; + if (method_exists($compiledRoute, 'getHostnameTokens')) { + $hostnameTokens = $compiledRoute->getHostnameTokens(); + } + + return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $absolute, $hostnameTokens); + } + + /** + * Support a route object and any string as route name + * + * {@inheritDoc} + */ + public function supports($name) + { + return is_string($name) || $name instanceof SymfonyRoute; + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/README.md b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/README.md new file mode 100644 index 0000000..607fb72 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/README.md @@ -0,0 +1,7 @@ +# Symfony CMF Routing Component [![Build Status](https://secure.travis-ci.org/symfony-cmf/Routing.png)](http://travis-ci.org/symfony-cmf/Routing) + +This library extends the Symfony2 Routing component. Even though it has Symfony +in its name, it does not need the full Symfony2 framework and can be used in +standalone projects. + +http://symfony.com/doc/master/cmf/components/routing.html diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RedirectRouteInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RedirectRouteInterface.php new file mode 100644 index 0000000..c4ac783 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RedirectRouteInterface.php @@ -0,0 +1,76 @@ + + */ +interface RedirectRouteInterface extends RouteObjectInterface +{ + /** + * Get the absolute uri to redirect to external domains. + * + * If this is non-empty, the other methods won't be used. + * + * @return string target absolute uri + */ + public function getUri(); + + /** + * Get the target route document this route redirects to. + * + * If non-null, it is added as route into the parameters, which will lead + * to have the generate call issued by the RedirectController to have + * the target route in the parameters. + * + * @return RouteObjectInterface the route this redirection points to + */ + public function getRouteTarget(); + + /** + * Get the name of the target route for working with the symfony standard + * router. + * + * @return string target route name + */ + public function getRouteName(); + + /** + * Whether this should be a permanent or temporary redirect + * + * @return boolean + */ + public function isPermanent(); + + /** + * Get the parameters for the target route router::generate() + * + * Note that for the DynamicRouter, you return the target route + * document as field 'route' of the hashmap. + * + * @return array Information to build the route + */ + public function getParameters(); +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteAwareInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteAwareInterface.php new file mode 100644 index 0000000..59983c6 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteAwareInterface.php @@ -0,0 +1,22 @@ +routeContent + * to have referrer resolution work. + * + * @return object the document or entity this route entry points to + */ + public function getRouteContent(); + + /** + * Get the route key. + * + * This key will be used as route name instead of the symfony core compatible + * route name and can contain any characters. + * + * Return null if you want to use the default key. + * + * @return string the route name + */ + public function getRouteKey(); +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteProviderInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteProviderInterface.php new file mode 100644 index 0000000..de9b646 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteProviderInterface.php @@ -0,0 +1,73 @@ +getRouteByName() + * + * @param array $names the list of names to retrieve + * @param array $parameters the parameters as they are passed to the + * UrlGeneratorInterface::generate call. (Only one array, not one for + * each entry in $names. + * + * @return \Symfony\Component\Routing\Route[] iterable thing with the keys + * the names of the $names argument. + */ + public function getRoutesByNames($names, $parameters = array()); +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Test/CmfUnitTestCase.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Test/CmfUnitTestCase.php new file mode 100644 index 0000000..7898238 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Test/CmfUnitTestCase.php @@ -0,0 +1,16 @@ +getMockBuilder($class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldByClassEnhancerTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldByClassEnhancerTest.php new file mode 100644 index 0000000..d7eb0a8 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldByClassEnhancerTest.php @@ -0,0 +1,62 @@ +document = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Enhancer\\RouteObject'); + + $mapping = array('Symfony\\Cmf\\Component\\Routing\\Tests\\Enhancer\\RouteObject' + => 'symfony_cmf_content.controller:indexAction'); + + $this->mapper = new FieldByClassEnhancer('_content', '_controller', $mapping); + + $this->request = Request::create('/test'); + } + + public function testClassFoundInMapping() + { + // this is the mock, thus a child class to make sure we properly check with instanceof + $defaults = array('_content' => $this->document); + $expected = array( + '_content' => $this->document, + '_controller' => 'symfony_cmf_content.controller:indexAction', + ); + $this->assertEquals($expected, $this->mapper->enhance($defaults, $this->request)); + } + + public function testFieldAlreadyThere() + { + $defaults = array( + '_content' => $this->document, + '_controller' => 'custom.controller:indexAction', + ); + $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request)); + } + + public function testClassNotFoundInMapping() + { + $defaults = array('_content' => $this); + $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request)); + } + + public function testNoClass() + { + $defaults = array('foo' => 'bar'); + $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request)); + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldMapEnhancerTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldMapEnhancerTest.php new file mode 100644 index 0000000..982467a --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldMapEnhancerTest.php @@ -0,0 +1,61 @@ +request = Request::create('/test'); + $mapping = array('static_pages' => 'symfony_cmf_content.controller:indexAction'); + + $this->enhancer = new FieldMapEnhancer('type', '_controller', $mapping); + } + + public function testFieldFoundInMapping() + { + $defaults = array('type' => 'static_pages'); + $expected = array( + 'type' => 'static_pages', + '_controller' => 'symfony_cmf_content.controller:indexAction', + ); + $this->assertEquals($expected, $this->enhancer->enhance($defaults, $this->request)); + } + + public function testFieldAlreadyThere() + { + $defaults = array( + 'type' => 'static_pages', + '_controller' => 'custom.controller:indexAction', + ); + $this->assertEquals($defaults, $this->enhancer->enhance($defaults, $this->request)); + } + + + public function testNoType() + { + $defaults = array(); + $this->assertEquals(array(), $this->enhancer->enhance($defaults, $this->request)); + } + + public function testNotFoundInMapping() + { + $defaults = array('type' => 'unknown_route'); + $this->assertEquals($defaults, $this->enhancer->enhance($defaults, $this->request)); + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldPresenceEnhancerTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldPresenceEnhancerTest.php new file mode 100644 index 0000000..78c3e10 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldPresenceEnhancerTest.php @@ -0,0 +1,51 @@ +mapper = new FieldPresenceEnhancer('_template', '_controller', 'symfony_cmf_content.controller:indexAction'); + + $this->request = Request::create('/test'); + } + + public function testHasTemplate() + { + $defaults = array('_template' => 'Bundle:Topic:template.html.twig'); + $expected = array( + '_template' => 'Bundle:Topic:template.html.twig', + '_controller' => 'symfony_cmf_content.controller:indexAction', + ); + $this->assertEquals($expected, $this->mapper->enhance($defaults, $this->request)); + } + + public function testFieldAlreadyThere() + { + $defaults = array( + '_template' => 'Bundle:Topic:template.html.twig', + '_controller' => 'custom.controller:indexAction', + ); + $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request)); + } + + public function testHasNoTemplate() + { + $defaults = array('foo' => 'bar'); + $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request)); + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteContentEnhancerTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteContentEnhancerTest.php new file mode 100644 index 0000000..df9008a --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteContentEnhancerTest.php @@ -0,0 +1,78 @@ +document = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Enhancer\\RouteObject', + array('getRouteContent', 'getRouteDefaults', 'getUrl')); + + $this->mapper = new RouteContentEnhancer('_route', '_content'); + + $this->request = Request::create('/test'); + } + + public function testContent() + { + $targetDocument = new TargetDocument(); + $this->document->expects($this->once()) + ->method('getRouteContent') + ->will($this->returnValue($targetDocument)); + + $defaults = array('_route' => $this->document); + $expected = array('_route' => $this->document, '_content' => $targetDocument); + + $this->assertEquals($expected, $this->mapper->enhance($defaults, $this->request)); + } + + + public function testFieldAlreadyThere() + { + $this->document->expects($this->never()) + ->method('getRouteContent') + ; + + $defaults = array('_route' => $this->document, '_content' => 'foo'); + + $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request)); + } + + public function testNoContent() + { + $this->document->expects($this->once()) + ->method('getRouteContent') + ->will($this->returnValue(null)); + + $defaults = array('_route' => $this->document); + $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request)); + } + + public function testNoCmfRoute() + { + $defaults = array('_route' => $this->buildMock('Symfony\\Component\\Routing\\Route')); + $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request)); + } +} + +class TargetDocument +{ +} + +class UnknownDocument +{ +} \ No newline at end of file diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteObject.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteObject.php new file mode 100644 index 0000000..6109339 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteObject.php @@ -0,0 +1,18 @@ +routeDocument = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'getRouteKey', 'compile')); + $this->routeCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute'); + + $this->context = $this->buildMock('Symfony\\Component\\Routing\\RequestContext'); + $this->request = Request::create($this->url); + + $this->matcher = new ConfigurableUrlMatcher(); + } + + public function testMatch() + { + $this->routeCompiled->expects($this->atLeastOnce()) + ->method('getStaticPrefix') + ->will($this->returnValue($this->url)) + ; + $this->routeCompiled->expects($this->atLeastOnce()) + ->method('getRegex') + ->will($this->returnValue('#'.str_replace('/', '\\/', $this->url).'#')) + ; + $this->routeDocument->expects($this->atLeastOnce()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + $this->routeDocument->expects($this->atLeastOnce()) + ->method('getDefaults') + ->will($this->returnValue(array('foo' => 'bar'))) + ; + + $mockCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute'); + $mockCompiled->expects($this->any()) + ->method('getStaticPrefix') + ->will($this->returnValue('/no/match')) + ; + $mockRoute = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock(); + $mockRoute->expects($this->any()) + ->method('compile') + ->will($this->returnValue($mockCompiled)) + ; + $routeCollection = new RouteCollection(); + $routeCollection->add('some', $mockRoute); + $routeCollection->add('_company_more', $this->routeDocument); + $routeCollection->add('other', $mockRoute); + + $results = $this->matcher->finalMatch($routeCollection, $this->request); + + $expected = array( + '_route_name' => '_company_more', + '_route' => $this->routeDocument, + 'foo' => 'bar', + ); + + $this->assertEquals($expected, $results); + } + + public function testMatchNoRouteObject() + { + $this->routeCompiled->expects($this->atLeastOnce()) + ->method('getStaticPrefix') + ->will($this->returnValue($this->url)) + ; + $this->routeCompiled->expects($this->atLeastOnce()) + ->method('getRegex') + ->will($this->returnValue('#'.str_replace('/', '\\/', $this->url).'#')) + ; + $this->routeDocument = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock(); + $this->routeDocument->expects($this->atLeastOnce()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + $this->routeDocument->expects($this->never()) + ->method('getRouteKey') + ; + $this->routeDocument->expects($this->atLeastOnce()) + ->method('getDefaults') + ->will($this->returnValue(array('foo' => 'bar'))) + ; + + $mockCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute'); + $mockCompiled->expects($this->any()) + ->method('getStaticPrefix') + ->will($this->returnValue('/no/match')) + ; + $mockRoute = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock(); + $mockRoute->expects($this->any()) + ->method('compile') + ->will($this->returnValue($mockCompiled)) + ; + $routeCollection = new RouteCollection(); + $routeCollection->add('some', $mockRoute); + $routeCollection->add('_company_more', $this->routeDocument); + $routeCollection->add('other', $mockRoute); + + $results = $this->matcher->finalMatch($routeCollection, $this->request); + + $expected = array( + '_route_name' => '_company_more', + '_route' => $this->routeDocument, + 'foo' => 'bar', + ); + + $this->assertEquals($expected, $results); + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/NestedMatcherTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/NestedMatcherTest.php new file mode 100644 index 0000000..2bcee0e --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/NestedMatcherTest.php @@ -0,0 +1,134 @@ +provider = $this->buildMock("Symfony\\Cmf\\Component\\Routing\\RouteProviderInterface"); + $this->routeFilter1 = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\NestedMatcher\\RouteFilterInterface'); + $this->routeFilter2 = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\NestedMatcher\\RouteFilterInterface'); + $this->finalMatcher = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\NestedMatcher\\FinalMatcherInterface'); + } + + public function testNestedMatcher() + { + $request = Request::create('/path/one'); + $routeCollection = new RouteCollection(); + $route = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock(); + $routeCollection->add('route', $route); + + $this->provider->expects($this->once()) + ->method('getRouteCollectionForRequest') + ->with($request) + ->will($this->returnValue($routeCollection)) + ; + $this->routeFilter1->expects($this->once()) + ->method('filter') + ->with($routeCollection, $request) + ->will($this->returnValue($routeCollection)) + ; + $this->routeFilter2->expects($this->once()) + ->method('filter') + ->with($routeCollection, $request) + ->will($this->returnValue($routeCollection)) + ; + $this->finalMatcher->expects($this->once()) + ->method('finalMatch') + ->with($routeCollection, $request) + ->will($this->returnValue(array('foo' => 'bar'))) + ; + + $matcher = new NestedMatcher($this->provider); + $matcher->addRouteFilter($this->routeFilter1); + $matcher->addRouteFilter($this->routeFilter2); + $matcher->setFinalMatcher($this->finalMatcher); + + $attributes = $matcher->matchRequest($request); + + $this->assertEquals(array('foo' => 'bar'), $attributes); + } + + /** + * Test priorities and exception handling + */ + public function testNestedMatcherPriority() + { + $request = Request::create('/path/one'); + $routeCollection = new RouteCollection(); + $route = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock(); + $routeCollection->add('route', $route); + + $wrongProvider = $this->buildMock("Symfony\\Cmf\\Component\\Routing\\RouteProviderInterface"); + $wrongProvider->expects($this->never()) + ->method('getRouteCollectionForRequest') + ; + $this->provider->expects($this->once()) + ->method('getRouteCollectionForRequest') + ->with($request) + ->will($this->returnValue($routeCollection)) + ; + $this->routeFilter1->expects($this->once()) + ->method('filter') + ->with($routeCollection, $request) + ->will($this->throwException(new ResourceNotFoundException())) + ; + $this->routeFilter2->expects($this->never()) + ->method('filter') + ; + $this->finalMatcher->expects($this->never()) + ->method('finalMatch') + ; + + $matcher = new NestedMatcher($wrongProvider); + $matcher->setRouteProvider($this->provider); + $matcher->addRouteFilter($this->routeFilter2, 10); + $matcher->addRouteFilter($this->routeFilter1, 20); + $matcher->setFinalMatcher($this->finalMatcher); + + try { + $matcher->matchRequest($request); + fail('nested matcher is eating exception'); + } catch(ResourceNotFoundException $e) + { + // expected + } + } + + public function testProviderNoMatch() + { + $request = Request::create('/path/one'); + $routeCollection = new RouteCollection(); + $this->provider->expects($this->once()) + ->method('getRouteCollectionForRequest') + ->with($request) + ->will($this->returnValue($routeCollection)) + ; + $this->finalMatcher->expects($this->never()) + ->method('finalMatch') + ; + + $matcher = new NestedMatcher($this->provider); + $matcher->setFinalMatcher($this->finalMatcher); + + $this->setExpectedException('Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException'); + $matcher->matchRequest($request); + } + +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/UrlMatcherTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/UrlMatcherTest.php new file mode 100644 index 0000000..08a1dec --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/UrlMatcherTest.php @@ -0,0 +1,148 @@ +hasMethod('getAttributes')) { + $this->markTestSkipped('This only works with symfony 2.2'); + } + + $this->routeDocument = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'getRouteKey', 'compile')); + $this->routeCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute'); + + $this->context = $this->buildMock('Symfony\\Component\\Routing\\RequestContext'); + $this->request = Request::create($this->url); + + $this->matcher = new UrlMatcher(new RouteCollection(), $this->context); + } + + public function testMatchRouteKey() + { + $this->doTestMatchRouteKey($this->url); + } + + public function testMatchNoKey() + { + $this->doTestMatchRouteKey(null); + } + + public function doTestMatchRouteKey($routeKey) + { + $this->routeCompiled->expects($this->atLeastOnce()) + ->method('getStaticPrefix') + ->will($this->returnValue($this->url)) + ; + $this->routeCompiled->expects($this->atLeastOnce()) + ->method('getRegex') + ->will($this->returnValue('#'.str_replace('/', '\\/', $this->url).'#')) + ; + $this->routeDocument->expects($this->atLeastOnce()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + $this->routeDocument->expects($this->atLeastOnce()) + ->method('getRouteKey') + ->will($this->returnValue($routeKey)) + ; + $this->routeDocument->expects($this->atLeastOnce()) + ->method('getDefaults') + ->will($this->returnValue(array('foo' => 'bar'))) + ; + + $mockCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute'); + $mockCompiled->expects($this->any()) + ->method('getStaticPrefix') + ->will($this->returnValue('/no/match')) + ; + $mockRoute = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock(); + $mockRoute->expects($this->any()) + ->method('compile') + ->will($this->returnValue($mockCompiled)) + ; + $routeCollection = new RouteCollection(); + $routeCollection->add('some', $mockRoute); + $routeCollection->add('_company_more', $this->routeDocument); + $routeCollection->add('other', $mockRoute); + + $results = $this->matcher->finalMatch($routeCollection, $this->request); + + $expected = array( + '_route_name' => ($routeKey) ? $routeKey : '_company_more', + '_route' => $this->routeDocument, + 'foo' => 'bar', + ); + + $this->assertEquals($expected, $results); + } + + public function testMatchNoRouteObject() + { + $this->routeCompiled->expects($this->atLeastOnce()) + ->method('getStaticPrefix') + ->will($this->returnValue($this->url)) + ; + $this->routeCompiled->expects($this->atLeastOnce()) + ->method('getRegex') + ->will($this->returnValue('#'.str_replace('/', '\\/', $this->url).'#')) + ; + $this->routeDocument = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock(); + $this->routeDocument->expects($this->atLeastOnce()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + $this->routeDocument->expects($this->never()) + ->method('getRouteKey') + ; + $this->routeDocument->expects($this->atLeastOnce()) + ->method('getDefaults') + ->will($this->returnValue(array('foo' => 'bar'))) + ; + + $mockCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute'); + $mockCompiled->expects($this->any()) + ->method('getStaticPrefix') + ->will($this->returnValue('/no/match')) + ; + $mockRoute = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock(); + $mockRoute->expects($this->any()) + ->method('compile') + ->will($this->returnValue($mockCompiled)) + ; + $routeCollection = new RouteCollection(); + $routeCollection->add('some', $mockRoute); + $routeCollection->add('_company_more', $this->routeDocument); + $routeCollection->add('other', $mockRoute); + + $results = $this->matcher->finalMatch($routeCollection, $this->request); + + $expected = array( + '_route_name' => '_company_more', + '_route' => $this->routeDocument, + 'foo' => 'bar', + ); + + $this->assertEquals($expected, $results); + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ChainRouterTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ChainRouterTest.php new file mode 100644 index 0000000..d76c1f9 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ChainRouterTest.php @@ -0,0 +1,617 @@ +router = new ChainRouter($this->getMock('Symfony\Component\HttpKernel\Log\LoggerInterface')); + $this->context = $this->getMock('Symfony\\Component\\Routing\\RequestContext'); + } + + public function testPriority() + { + $this->assertEquals(array(), $this->router->all()); + + list($low, $high) = $this->createRouterMocks(); + + $this->router->add($low, 10); + $this->router->add($high, 100); + + $this->assertEquals(array( + $high, + $low, + ), $this->router->all()); + } + + /** + * Routers are supposed to be sorted only once. + * This test will check that by trying to get all routers several times. + * + * @covers \Symfony\Cmf\Component\Routing\ChainRouter::sortRouters + * @covers \Symfony\Cmf\Component\Routing\ChainRouter::all + */ + public function testSortRouters() + { + list($low, $medium, $high) = $this->createRouterMocks(); + // We're using a mock here and not $this->router because we need to ensure that the sorting operation is done only once. + $router = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\ChainRouter', array('sortRouters')); + $router + ->expects($this->once()) + ->method('sortRouters') + ->will( + $this->returnValue( + array($high, $medium, $low) + ) + ) + ; + + $router->add($low, 10); + $router->add($medium, 50); + $router->add($high, 100); + $expectedSortedRouters = array($high, $medium, $low); + // Let's get all routers 5 times, we should only sort once. + for ($i = 0; $i < 5; ++$i) { + $this->assertSame($expectedSortedRouters, $router->all()); + } + } + + /** + * This test ensures that if a router is being added on the fly, the sorting is reset. + * + * @covers \Symfony\Cmf\Component\Routing\ChainRouter::sortRouters + * @covers \Symfony\Cmf\Component\Routing\ChainRouter::all + * @covers \Symfony\Cmf\Component\Routing\ChainRouter::add + */ + public function testReSortRouters() + { + list($low, $medium, $high) = $this->createRouterMocks(); + $highest = clone $high; + // We're using a mock here and not $this->router because we need to ensure that the sorting operation is done only once. + $router = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\ChainRouter', array('sortRouters')); + $router + ->expects($this->at(0)) + ->method('sortRouters') + ->will( + $this->returnValue( + array($high, $medium, $low) + ) + ) + ; + // The second time sortRouters() is called, we're supposed to get the newly added router ($highest) + $router + ->expects($this->at(1)) + ->method('sortRouters') + ->will( + $this->returnValue( + array($highest, $high, $medium, $low) + ) + ) + ; + + $router->add($low, 10); + $router->add($medium, 50); + $router->add($high, 100); + $this->assertSame(array($high, $medium, $low), $router->all()); + + // Now adding another router on the fly, sorting must have been reset + $router->add($highest, 101); + $this->assertSame(array($highest, $high, $medium, $low), $router->all()); + } + + /** + * context must be propagated to chained routers and be stored locally + */ + public function testContext() + { + list($low, $high) = $this->createRouterMocks(); + + $low + ->expects($this->once()) + ->method('setContext') + ->with($this->context) + ; + + $high + ->expects($this->once()) + ->method('setContext') + ->with($this->context) + ; + + + $this->router->add($low, 10); + $this->router->add($high, 100); + + $this->router->setContext($this->context); + $this->assertSame($this->context, $this->router->getContext()); + } + + /** + * context must be propagated also when routers are added after context is set + */ + public function testContextOrder() + { + list($low, $high) = $this->createRouterMocks(); + + $low + ->expects($this->once()) + ->method('setContext') + ->with($this->context) + ; + + $high + ->expects($this->once()) + ->method('setContext') + ->with($this->context) + ; + + $this->router->setContext($this->context); + + $this->router->add($low, 10); + $this->router->add($high, 100); + + $this->router->all(); + + $this->assertSame($this->context, $this->router->getContext()); + } + + /** + * The first usable match is used, no further routers are queried once a match is found + */ + public function testMatch() + { + $url = '/test'; + list($lower, $low, $high) = $this->createRouterMocks(); + + $high + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException)) + ; + $low + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->returnValue(array('test'))) + ; + $lower + ->expects($this->never()) + ->method('match'); + $this->router->add($lower, 5); + $this->router->add($low, 10); + $this->router->add($high, 100); + + $result = $this->router->match('/test'); + $this->assertEquals(array('test'), $result); + } + + /** + * The first usable match is used, no further routers are queried once a match is found + */ + public function testMatchRequest() + { + $url = '/test'; + list($lower, $low, $high) = $this->createRouterMocks(); + + $highest = $this->getMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RequestMatcher'); + + $request = Request::create('/test'); + + $highest + ->expects($this->once()) + ->method('matchRequest') + ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException)) + ; + $high + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException)) + ; + $low + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->returnValue(array('test'))) + ; + $lower + ->expects($this->never()) + ->method('match') + ; + + $this->router->add($lower, 5); + $this->router->add($low, 10); + $this->router->add($high, 100); + $this->router->add($highest, 200); + + $result = $this->router->matchRequest($request); + $this->assertEquals(array('test'), $result); + } + + /** + * If there is a method not allowed but another router matches, that one is used + */ + public function testMatchAndNotAllowed() + { + $url = '/test'; + list($low, $high) = $this->createRouterMocks(); + + $high + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array()))) + ; + $low + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->returnValue(array('test'))) + ; + $this->router->add($low, 10); + $this->router->add($high, 100); + + $result = $this->router->match('/test'); + $this->assertEquals(array('test'), $result); + } + + /** + * If there is a method not allowed but another router matches, that one is used + */ + public function testMatchRequestAndNotAllowed() + { + $url = '/test'; + list($low, $high) = $this->createRouterMocks(); + + $high + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array()))) + ; + $low + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->returnValue(array('test'))) + ; + $this->router->add($low, 10); + $this->router->add($high, 100); + + $result = $this->router->matchRequest(Request::create('/test')); + $this->assertEquals(array('test'), $result); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testMatchNotFound() + { + $url = '/test'; + list($low, $high) = $this->createRouterMocks(); + + $high + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException)) + ; + $low + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException)) + ; + $this->router->add($low, 10); + $this->router->add($high, 100); + + $this->router->match('/test'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testMatchRequestNotFound() + { + $url = '/test'; + list($low, $high) = $this->createRouterMocks(); + + $high + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException)) + ; + $low + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException)) + ; + $this->router->add($low, 10); + $this->router->add($high, 100); + + $this->router->matchRequest(Request::create('/test')); + } + + /** + * If any of the routers throws a not allowed exception and no other matches, we need to see this + * + * @expectedException \Symfony\Component\Routing\Exception\MethodNotAllowedException + */ + public function testMatchMethodNotAllowed() + { + $url = '/test'; + list($low, $high) = $this->createRouterMocks(); + + $high + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array()))) + ; + $low + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException)) + ; + $this->router->add($low, 10); + $this->router->add($high, 100); + + $this->router->match('/test'); + } + + /** + * If any of the routers throws a not allowed exception and no other matches, we need to see this + * + * @expectedException \Symfony\Component\Routing\Exception\MethodNotAllowedException + */ + public function testMatchRequestMethodNotAllowed() + { + $url = '/test'; + list($low, $high) = $this->createRouterMocks(); + + $high + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array()))) + ; + $low + ->expects($this->once()) + ->method('match') + ->with($url) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException)) + ; + $this->router->add($low, 10); + $this->router->add($high, 100); + + $this->router->matchRequest(Request::create('/test')); + } + + public function testGenerate() + { + $url = '/test'; + $name = 'test'; + $parameters = array('test' => 'value'); + list($lower, $low, $high) = $this->createRouterMocks(); + + $high + ->expects($this->once()) + ->method('generate') + ->with($name, $parameters, false) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\RouteNotFoundException())) + ; + $low + ->expects($this->once()) + ->method('generate') + ->with($name, $parameters, false) + ->will($this->returnValue($url)) + ; + $lower + ->expects($this->never()) + ->method('generate') + ; + + $this->router->add($low, 10); + $this->router->add($high, 100); + + $result = $this->router->generate($name, $parameters); + $this->assertEquals($url, $result); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testGenerateNotFound() + { + $url = '/test'; + $name = 'test'; + $parameters = array('test' => 'value'); + list($low, $high) = $this->createRouterMocks(); + + $high + ->expects($this->once()) + ->method('generate') + ->with($name, $parameters, false) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\RouteNotFoundException())) + ; + $low->expects($this->once()) + ->method('generate') + ->with($name, $parameters, false) + ->will($this->throwException(new \Symfony\Component\Routing\Exception\RouteNotFoundException())) + ; + $this->router->add($low, 10); + $this->router->add($high, 100); + + $result = $this->router->generate($name, $parameters); + $this->assertEquals($url, $result); + } + + public function testGenerateObjectName() + { + $name = new \stdClass(); + $parameters = array('test' => 'value'); + + $defaultRouter = $this->getMock('Symfony\\Component\\Routing\\RouterInterface'); + $chainedRouter = $this->getMock('Symfony\\Cmf\\Component\\Routing\\ChainedRouterInterface'); + + $defaultRouter + ->expects($this->never()) + ->method('generate') + ; + $chainedRouter + ->expects($this->once()) + ->method('supports') + ->will($this->returnValue(true)) + ; + $chainedRouter + ->expects($this->once()) + ->method('generate') + ->with($name, $parameters, false) + ->will($this->returnValue($name)) + ; + + $this->router->add($defaultRouter, 200); + $this->router->add($chainedRouter, 100); + + $result = $this->router->generate($name, $parameters); + $this->assertEquals($name, $result); + } + + public function testGenerateNonDefaultStringName() + { + $name = '/test/this'; + $parameters = array('test' => 'value'); + + $defaultRouter = $this->getMock('Symfony\\Component\\Routing\\RouterInterface'); + $chainedRouter = $this->getMock('Symfony\\Cmf\\Component\\Routing\\ChainedRouterInterface'); + + $defaultRouter + ->expects($this->never()) + ->method('generate') + ; + $chainedRouter + ->expects($this->once()) + ->method('supports') + ->will($this->returnValue(true)) + ; + $chainedRouter + ->expects($this->once()) + ->method('generate') + ->with($name, $parameters, false) + ->will($this->returnValue($name)) + ; + + $this->router->add($defaultRouter, 200); + $this->router->add($chainedRouter, 100); + + $result = $this->router->generate($name, $parameters); + $this->assertEquals($name, $result); + } + + public function testWarmup() + { + $dir = 'test_dir'; + list($low) = $this->createRouterMocks(); + + $low + ->expects($this->never()) + ->method('warmUp') + ; + $high = $this->getMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\WarmableRouterMock'); + $high + ->expects($this->once()) + ->method('warmUp') + ->with($dir) + ; + + $this->router->add($low, 10); + $this->router->add($high, 100); + + $this->router->warmUp($dir); + } + + public function testRouteCollection() + { + list($low, $high) = $this->createRouterMocks(); + $lowcol = new RouteCollection(); + $lowcol->add('low', $this->buildMock('Symfony\\Component\\Routing\\Route')); + $highcol = new RouteCollection(); + $highcol->add('high', $this->buildMock('Symfony\\Component\\Routing\\Route')); + + $low + ->expects($this->once()) + ->method('getRouteCollection') + ->will($this->returnValue($lowcol)) + ; + $high + ->expects($this->once()) + ->method('getRouteCollection') + ->will($this->returnValue($highcol)) + ; + + $this->router->add($low, 10); + $this->router->add($high, 100); + + $collection = $this->router->getRouteCollection(); + $this->assertInstanceOf('Symfony\\Component\\Routing\\RouteCollection', $collection); + + $names = array(); + foreach ($collection->all() as $name => $route) { + $this->assertInstanceOf('Symfony\\Component\\Routing\\Route', $route); + $names[] = $name; + } + $this->assertEquals(array('high', 'low'), $names); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testSupport() + { + + $router = $this->getMock('Symfony\Cmf\Component\Routing\ChainedRouterInterface'); + $router + ->expects($this->once()) + ->method('supports') + ->will($this->returnValue(false)) + ; + + $router + ->expects($this->never()) + ->method('generate') + ->will($this->returnValue(false)) + ; + + $this->router->add($router); + + $this->router->generate('foobar'); + } + + protected function createRouterMocks() + { + return array( + $this->getMock('Symfony\\Component\\Routing\\RouterInterface'), + $this->getMock('Symfony\\Component\\Routing\\RouterInterface'), + $this->getMock('Symfony\\Component\\Routing\\RouterInterface'), + ); + } +} + +abstract class WarmableRouterMock implements \Symfony\Component\Routing\RouterInterface, \Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface +{ +} + +abstract class RequestMatcher implements \Symfony\Component\Routing\RouterInterface, \Symfony\Component\Routing\Matcher\RequestMatcherInterface +{ +} \ No newline at end of file diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ContentAwareGeneratorTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ContentAwareGeneratorTest.php new file mode 100644 index 0000000..6ddf3c7 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ContentAwareGeneratorTest.php @@ -0,0 +1,285 @@ +contentDocument = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\RouteAwareInterface'); + $this->routeDocument = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'compile')); + $this->routeCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute'); + $this->provider = $this->buildMock("Symfony\\Cmf\\Component\\Routing\\RouteProviderInterface"); + $this->context = $this->buildMock('Symfony\\Component\\Routing\\RequestContext'); + + $this->generator = new TestableContentAwareGenerator($this->provider); + } + + + public function testGenerateFromContent() + { + $this->provider->expects($this->never()) + ->method('getRouteByName') + ; + $this->contentDocument->expects($this->once()) + ->method('getRoutes') + ->will($this->returnValue(array($this->routeDocument))) + ; + $this->routeDocument->expects($this->once()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + + $this->assertEquals('result_url', $this->generator->generate($this->contentDocument)); + } + + public function testGenerateFromContentId() + { + $this->provider->expects($this->never()) + ->method('getRouteByName') + ; + + $contentRepository = $this->buildMock("Symfony\\Cmf\\Component\\Routing\\ContentRepositoryInterface", array('findById')); + $contentRepository->expects($this->once()) + ->method('findById') + ->with('/content/id') + ->will($this->returnValue($this->contentDocument)) + ; + $this->generator->setContentRepository($contentRepository); + + $this->contentDocument->expects($this->once()) + ->method('getRoutes') + ->will($this->returnValue(array($this->routeDocument))) + ; + + $this->routeDocument->expects($this->once()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + + $this->assertEquals('result_url', $this->generator->generate('', array('content_id' => '/content/id'))); + } + + public function testGenerateEmptyRouteString() + { + $this->provider->expects($this->never()) + ->method('getRouteByName') + ; + + $this->contentDocument->expects($this->once()) + ->method('getRoutes') + ->will($this->returnValue(array($this->routeDocument))) + ; + + $this->routeDocument->expects($this->once()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + + $this->assertEquals('result_url', $this->generator->generate('', array('content'=>$this->contentDocument))); + } + + public function testGenerateRouteMultilang() + { + $route_en = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'compile', 'getRouteContent')); + $route_en->setLocale('en'); + $route_de = $this->routeDocument; + $route_de->setLocale('de'); + + $this->contentDocument->expects($this->once()) + ->method('getRoutes') + ->will($this->returnValue(array($route_en, $route_de))) + ; + $route_en->expects($this->once()) + ->method('getRouteContent') + ->will($this->returnValue($this->contentDocument)) + ; + $route_en->expects($this->never()) + ->method('compile') + ; + $route_de->expects($this->once()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + + $this->assertEquals('result_url', $this->generator->generate($route_en, array('_locale' => 'de'))); + } + + + public function testGenerateRouteMultilangNomatch() + { + $route_en = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'compile', 'getRouteContent')); + $route_en->setLocale('en'); + $route_de = $this->routeDocument; + $route_de->setLocale('de'); + + $this->contentDocument->expects($this->once()) + ->method('getRoutes') + ->will($this->returnValue(array($route_en, $route_de))) + ; + $route_en->expects($this->once()) + ->method('getRouteContent') + ->will($this->returnValue($this->contentDocument)) + ; + $route_en->expects($this->once()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + $route_de->expects($this->never()) + ->method('compile') + ; + + $this->assertEquals('result_url', $this->generator->generate($route_en, array('_locale' => 'fr'))); + } + + public function testGenerateNoncmfRouteMultilang() + { + $route_en = $this->buildMock('Symfony\\Component\\Routing\\Route', array('getDefaults', 'compile', 'getRouteContent')); + + $route_en->expects($this->once()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + + $this->assertEquals('result_url', $this->generator->generate($route_en, array('_locale' => 'de'))); + } + + public function testGenerateRoutenameMultilang() + { + $name = 'foo/bar'; + $route_en = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'compile', 'getRouteContent')); + $route_en->setLocale('en'); + $route_de = $this->routeDocument; + $route_de->setLocale('de'); + + $this->provider->expects($this->once()) + ->method('getRouteByName') + ->with($name) + ->will($this->returnValue($route_en)) + ; + $this->contentDocument->expects($this->once()) + ->method('getRoutes') + ->will($this->returnValue(array($route_en, $route_de))) + ; + $route_en->expects($this->once()) + ->method('getRouteContent') + ->will($this->returnValue($this->contentDocument)) + ; + $route_en->expects($this->never()) + ->method('compile') + ; + $route_de->expects($this->once()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + + $this->assertEquals('result_url', $this->generator->generate($name, array('_locale' => 'de'))); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testGenerateRoutenameMultilangNotFound() + { + $name = 'foo/bar'; + + $this->provider->expects($this->once()) + ->method('getRouteByName') + ->with($name) + ->will($this->returnValue(null)) + ; + + $this->generator->generate($name, array('_locale' => 'de')); + } + + public function testGenerateDocumentMultilang() + { + $route_en = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'compile')); + $route_en->setLocale('en'); + $route_de = $this->routeDocument; + $route_de->setLocale('de'); + + $this->contentDocument->expects($this->once()) + ->method('getRoutes') + ->will($this->returnValue(array($route_en, $route_de))) + ; + $route_en->expects($this->never()) + ->method('compile') + ; + $route_de->expects($this->once()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + + $this->assertEquals('result_url', $this->generator->generate('', array('content'=>$this->contentDocument, '_locale' => 'de'))); + } + + /** + * @expectedException Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testGenerateNoContent() + { + $this->generator->generate('', array()); + } + /** + * @expectedException Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testGenerateInvalidContent() + { + $this->generator->generate('', array('content' => $this)); + } + /** + * @expectedException Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testGenerateNoRoutes() + { + $this->contentDocument->expects($this->once()) + ->method('getRoutes') + ->will($this->returnValue(array())); + + $this->generator->generate('', array('content'=>$this->contentDocument)); + } + /** + * @expectedException Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testGenerateInvalidRoute() + { + $this->contentDocument->expects($this->once()) + ->method('getRoutes') + ->will($this->returnValue(array($this))); + + $this->generator->generate('', array('content'=>$this->contentDocument)); + } + + public function testSupports() + { + $this->assertTrue($this->generator->supports('')); + $this->assertTrue($this->generator->supports(null)); + $this->assertTrue($this->generator->supports($this->contentDocument)); + $this->assertFalse($this->generator->supports($this)); + } +} + +/** + * Overwrite doGenerate to reduce amount of mocking needed + */ +class TestableContentAwareGenerator extends ContentAwareGenerator +{ + protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute, $hostnameTokens = null) + { + return 'result_url'; + } +} \ No newline at end of file diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/DynamicRouterTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/DynamicRouterTest.php new file mode 100644 index 0000000..af790a8 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/DynamicRouterTest.php @@ -0,0 +1,241 @@ +routeDocument = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults')); + + $this->matcher = $this->buildMock('Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface'); + $this->generator = $this->buildMock('Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface', array('supports', 'generate', 'setContext', 'getContext')); + $this->enhancer = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Enhancer\\RouteEnhancerInterface', array('enhance')); + + $this->context = $this->buildMock('Symfony\\Component\\Routing\\RequestContext'); + $this->request = Request::create($this->url); + + $this->router = new DynamicRouter($this->context, $this->matcher, $this->generator); + $this->router->addRouteEnhancer($this->enhancer); + } + + /** + * rather trivial, but we want 100% coverage + */ + public function testContext() + { + $this->router->setContext($this->context); + $this->assertSame($this->context, $this->router->getContext()); + } + + public function testRouteCollection() + { + $collection = $this->router->getRouteCollection(); + $this->assertInstanceOf('Symfony\\Component\\Routing\\RouteCollection', $collection); + // TODO: once this is implemented, check content of collection + } + + + /// generator tests /// + + public function testGetGenerator() + { + $this->generator->expects($this->once()) + ->method('setContext') + ->with($this->equalTo($this->context)); + + $generator = $this->router->getGenerator(); + $this->assertInstanceOf('Symfony\Component\Routing\Generator\UrlGeneratorInterface', $generator); + $this->assertSame($this->generator, $generator); + } + + public function testGenerate() + { + $name = 'my_route_name'; + $parameters = array('foo' => 'bar'); + $absolute = true; + + $this->generator->expects($this->once()) + ->method('generate') + ->with($name, $parameters, $absolute) + ->will($this->returnValue('http://test')) + ; + + $url = $this->router->generate($name, $parameters, $absolute); + $this->assertEquals('http://test', $url); + } + + public function testSupports() + { + $name = 'foo/bar'; + $this->generator->expects($this->once()) + ->method('supports') + ->with($this->equalTo($name)) + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->router->supports($name)); + } + + /// match tests /// + + public function testGetMatcher() + { + $this->matcher->expects($this->once()) + ->method('setContext') + ->with($this->equalTo($this->context)); + + $matcher = $this->router->getMatcher(); + $this->assertInstanceOf('Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface', $matcher); + $this->assertSame($this->matcher, $matcher); + } + + public function testMatchUrl() + { + $routeDefaults = array('foo' => 'bar'); + $this->matcher->expects($this->once()) + ->method('match') + ->with($this->url) + ->will($this->returnValue($routeDefaults)) + ; + + $expected = array('this' => 'that'); + $this->enhancer->expects($this->once()) + ->method('enhance') + ->with($this->equalTo($routeDefaults), $this->equalTo($this->request)) + ->will($this->returnValue($expected)) + ; + + $results = $this->router->match($this->url); + + $this->assertEquals($expected, $results); + } + + + public function testMatchRequestWithUrlMatcher() + { + $routeDefaults = array('foo' => 'bar'); + + $this->matcher->expects($this->once()) + ->method('match') + ->with($this->url) + ->will($this->returnValue($routeDefaults)) + ; + + $expected = array('this' => 'that'); + $this->enhancer->expects($this->once()) + ->method('enhance') + // somehow request object gets confused, check on instance only + ->with($this->equalTo($routeDefaults), $this->isInstanceOf('Symfony\\Component\\HttpFoundation\\Request')) + ->will($this->returnValue($expected)) + ; + + $results = $this->router->matchRequest($this->request); + + $this->assertEquals($expected, $results); + } + + public function testMatchRequest() + { + $routeDefaults = array('foo' => 'bar'); + + $matcher = $this->buildMock('Symfony\\Component\\Routing\\Matcher\\RequestMatcherInterface', array('matchRequest', 'setContext', 'getContext')); + $router = new DynamicRouter($this->context, $matcher, $this->generator); + + $matcher->expects($this->once()) + ->method('matchRequest') + ->with($this->request) + ->will($this->returnValue($routeDefaults)) + ; + + $expected = array('this' => 'that'); + $this->enhancer->expects($this->once()) + ->method('enhance') + ->with($this->equalTo($routeDefaults), $this->equalTo($this->request)) // TODO: why do we not get the right thing? + ->will($this->returnValue($expected)) + ; + + $router->addRouteEnhancer($this->enhancer); + + $this->assertEquals($expected, $router->matchRequest($this->request)); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testMatchFilter() + { + $router = new DynamicRouter($this->context, $this->matcher, $this->generator, '#/different/prefix.*#'); + $router->addRouteEnhancer($this->enhancer); + + $this->matcher->expects($this->never()) + ->method('match') + ; + + $this->enhancer->expects($this->never()) + ->method('enhance') + ; + + $router->match($this->url); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testMatchRequestFilter() + { + $matcher = $this->buildMock('Symfony\\Component\\Routing\\Matcher\\RequestMatcherInterface', array('matchRequest', 'setContext', 'getContext')); + + $router = new DynamicRouter($this->context, $matcher, $this->generator, '#/different/prefix.*#'); + $router->addRouteEnhancer($this->enhancer); + + $matcher->expects($this->never()) + ->method('matchRequest') + ; + + $this->enhancer->expects($this->never()) + ->method('enhance') + ; + + $router->matchRequest($this->request); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testMatchUrlWithRequestMatcher() + { + $matcher = $this->buildMock('Symfony\\Component\\Routing\\Matcher\\RequestMatcherInterface', array('matchRequest', 'setContext', 'getContext')); + $router = new DynamicRouter($this->context, $matcher, $this->generator); + + $router->match($this->url); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidMatcher() + { + new DynamicRouter($this->context, $this, $this->generator); + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ProviderBasedGeneratorTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ProviderBasedGeneratorTest.php new file mode 100644 index 0000000..509232d --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ProviderBasedGeneratorTest.php @@ -0,0 +1,94 @@ +routeDocument = $this->buildMock('Symfony\\Component\\Routing\\Route', array('getDefaults', 'compile')); + $this->routeCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute'); + $this->provider = $this->buildMock("Symfony\\Cmf\\Component\\Routing\\RouteProviderInterface"); + $this->context = $this->buildMock('Symfony\\Component\\Routing\\RequestContext'); + + $this->generator= new TestableProviderBasedGenerator($this->provider); + } + + public function testGenerateFromName() + { + $name = 'foo/bar'; + + $this->provider->expects($this->once()) + ->method('getRouteByName') + ->with($name) + ->will($this->returnValue($this->routeDocument)) + ; + $this->routeDocument->expects($this->once()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + + $this->assertEquals('result_url', $this->generator->generate($name)); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testGenerateNotFound() + { + $name = 'foo/bar'; + + $this->provider->expects($this->once()) + ->method('getRouteByName') + ->with($name) + ->will($this->returnValue(null)) + ; + + $this->generator->generate($name); + } + + public function testGenerateFromRoute() + { + $this->provider->expects($this->never()) + ->method('getRouteByName') + ; + $this->routeDocument->expects($this->once()) + ->method('compile') + ->will($this->returnValue($this->routeCompiled)) + ; + + $this->assertEquals('result_url', $this->generator->generate($this->routeDocument)); + } + + public function testSupports() + { + $this->assertTrue($this->generator->supports('foo/bar')); + $this->assertTrue($this->generator->supports($this->routeDocument)); + $this->assertFalse($this->generator->supports($this)); + } +} + +/** + * Overwrite doGenerate to reduce amount of mocking needed + */ +class TestableProviderBasedGenerator extends ProviderBasedGenerator +{ + protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute, $hostnameTokens = null) + { + return 'result_url'; + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/RouteMock.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/RouteMock.php new file mode 100644 index 0000000..e028d2c --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/RouteMock.php @@ -0,0 +1,39 @@ +locale = $locale; + } + public function getRouteContent() + { + return null; + } + public function getDefaults() + { + $defaults = array(); + if (! is_null($this->locale)) { + $defaults['_locale'] = $this->locale; + } + return $defaults; + } + public function getRequirement($key) + { + if (! $key == '_locale') { + throw new \Exception; + } + return $this->locale; + } + public function getRouteKey() + { + return null; + } +} \ No newline at end of file diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/bootstrap.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/bootstrap.php new file mode 100644 index 0000000..cd9b88e --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/bootstrap.php @@ -0,0 +1,19 @@ +=5.3.2", + "symfony/routing": ">=2.1,<2.3-dev", + "symfony/http-kernel": ">=2.1,<2.3-dev" + }, + "autoload": { + "psr-0": { "Symfony\\Cmf\\Component\\Routing": "" } + }, + "target-dir": "Symfony/Cmf/Component/Routing", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/phpunit.xml.dist b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/phpunit.xml.dist new file mode 100644 index 0000000..2564ef1 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/phpunit.xml.dist @@ -0,0 +1,26 @@ + + + + + + + + ./Tests + + + + + + . + + Test/ + Tests/ + vendor/ + + + + +