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 b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing new file mode 160000 index 0000000..04da0f0 --- /dev/null +++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing @@ -0,0 +1 @@ +Subproject commit 04da0f0a8e308e199c52549d1d04b208cc2058db