Unverified Commit 37893880 authored by alexpott's avatar alexpott

Issue #2917331 by catch, martin107, Deepak Goyal, longwave, andypost,...

Issue #2917331 by catch, martin107, Deepak Goyal, longwave, andypost, ravi.shankar, alexpott, daffie, larowlan: Decouple from Symfony CMF
parent cd7455f3
......@@ -27,7 +27,7 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Site\Settings;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;
use Symfony\Component\Routing\Route;
// Change the directory to the Drupal root.
......
......@@ -32,7 +32,7 @@
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Url;
use Drupal\language\Entity\ConfigurableLanguage;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
......
......@@ -11,7 +11,7 @@
use Drupal\Component\Utility\ArgumentsResolverInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;
/**
* Attaches access check services to routes and runs them on request.
......
......@@ -4,7 +4,7 @@
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;
use Symfony\Component\Routing\Route;
/**
......
......@@ -2,7 +2,7 @@
namespace Drupal\Core\EventSubscriber;
use Symfony\Cmf\Component\Routing\RouteProviderInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
......@@ -20,14 +20,14 @@ class OptionsRequestSubscriber implements EventSubscriberInterface {
/**
* The route provider.
*
* @var \Symfony\Cmf\Component\Routing\RouteProviderInterface
* @var \Drupal\Core\Routing\RouteProviderInterface
*/
protected $routeProvider;
/**
* Creates a new OptionsRequestSubscriber instance.
*
* @param \Symfony\Cmf\Component\Routing\RouteProviderInterface $route_provider
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
* The route provider.
*/
public function __construct(RouteProviderInterface $route_provider) {
......
......@@ -4,7 +4,7 @@
use Drupal\Core\Routing\RouteBuildEvent;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;
use Symfony\Component\Routing\RouteCollection;
/**
......
......@@ -2,7 +2,7 @@
namespace Drupal\Core\ParamConverter;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;
use Symfony\Component\Routing\RouteCollection;
/**
......
......@@ -9,7 +9,7 @@
use Drupal\Core\Routing\RequestContext;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
......
......@@ -3,7 +3,7 @@
namespace Drupal\Core\Routing\Enhancer;
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
......
......@@ -3,7 +3,7 @@
namespace Drupal\Core\Routing\Enhancer;
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
......
......@@ -5,7 +5,7 @@
use Drupal\Core\ParamConverter\ParamConverterManagerInterface;
use Drupal\Core\ParamConverter\ParamNotConvertedException;
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
......
......@@ -2,11 +2,25 @@
namespace Drupal\Core\Routing;
use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* A route enhance service to determine route enhance rules.
*/
interface EnhancerInterface extends RouteEnhancerInterface {
interface EnhancerInterface {
/**
* Updates the defaults for a route definition based on the request.
*
* @param array $defaults
* The defaults, maps to '_defaults' in the route definition YAML.
* @param \Symfony\Component\HttpFoundation\Request $request
* The Request instance.
*
* @return array
* The modified defaults. Each enhancer MUST return the
* $defaults but may add or remove values.
*/
public function enhance(array $defaults, Request $request);
}
......@@ -2,11 +2,31 @@
namespace Drupal\Core\Routing;
use Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouteCollection;
/**
* A route filter service to filter down the collection of route instances.
*/
interface FilterInterface extends RouteFilterInterface {
interface FilterInterface {
/**
* Filters the route collection against a request and returns all matching
* routes.
*
* @param \Symfony\Component\Routing\RouteCollection $collection
* The collection against which to match.
* @param \Symfony\Component\HttpFoundation\Request $request
* A Request object against which to match.
*
* @return \Symfony\Component\Routing\RouteCollection
* A non-empty RouteCollection of matched routes
*
* @throws ResourceNotFoundException
* If none of the routes in $collection matches $request. This is a
* performance optimization to not continue the match process when a match
* will no longer be possible.
*/
public function filter(RouteCollection $collection, Request $request);
}
<?php
namespace Drupal\Core\Routing;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Component\Routing\RouteCollection;
class LazyRouteCollection extends RouteCollection {
/**
* The route provider for this generator.
*
* @var \Symfony\Component\Routing\RouteProviderInterface
*/
protected $provider;
/**
* Constructs a LazyRouteCollection.
*/
public function __construct(RouteProviderInterface $provider) {
$this->provider = $provider;
}
/**
* {@inheritdoc}
*/
public function getIterator() {
return new \ArrayIterator($this->all());
}
/**
* Gets the number of Routes in this collection.
*
* @return int
* The number of routes
*/
public function count() {
return count($this->all());
}
/**
* Returns all routes in this collection.
*
* @return \Symfony\Component\Routing\Route[]
* An array of routes
*/
public function all() {
return $this->provider->getRoutesByNames(NULL);
}
/**
* Gets a route by name.
*
* @param string $name
* The route name
*
* @return \Symfony\Component\Routing\Route|null
* A Route instance or null when not found
*/
public function get($name) {
try {
return $this->provider->getRouteByName($name);
}
catch (RouteNotFoundException $e) {
return;
}
}
}
......@@ -2,7 +2,6 @@
namespace Drupal\Core\Routing;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
......
<?php
namespace Drupal\Core\Routing;
/**
* Provides constants used for retrieving matched routes.
*/
interface RouteObjectInterface {
/**
* Key for the route name.
*
* @var string
*/
const ROUTE_NAME = '_route';
/**
* Key for the route object.
*
* @var string
*/
const ROUTE_OBJECT = '_route_object';
/**
* Key for the controller.
*/
const CONTROLLER_NAME = '_controller';
}
......@@ -10,8 +10,6 @@
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Cmf\Component\Routing\PagedRouteCollection;
use Symfony\Cmf\Component\Routing\PagedRouteProviderInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
......@@ -21,7 +19,7 @@
/**
* A Route Provider front-end for all Drupal-stored routes.
*/
class RouteProvider implements CacheableRouteProviderInterface, PreloadableRouteProviderInterface, PagedRouteProviderInterface, EventSubscriberInterface {
class RouteProvider implements CacheableRouteProviderInterface, PreloadableRouteProviderInterface, EventSubscriberInterface {
/**
* The database connection from which to read route information.
......@@ -401,7 +399,16 @@ protected function routeProviderRouteCompare(array $a, array $b) {
* {@inheritdoc}
*/
public function getAllRoutes() {
return new PagedRouteCollection($this);
$select = $this->connection->select($this->tableName, 'router')
->fields('router', ['name', 'route']);
$routes = $select->execute()->fetchAllKeyed();
$result = [];
foreach ($routes as $name => $route) {
$result[$name] = unserialize($route);
}
return $result;
}
/**
......@@ -422,9 +429,25 @@ public static function getSubscribedEvents() {
}
/**
* {@inheritdoc}
* Returns a chunk of routes.
*
* Should only be used in conjunction with an iterator.
*
* @param int $offset
* The query offset.
* @param int $length
* The number of records.
*
* @return \Symfony\Component\Routing\Route[]
* Routes keyed by the route name.
*
* @deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. No direct
* replacement is provided.
*
* @see https://www.drupal.org/node/3151009
*/
public function getRoutesPaged($offset, $length = NULL) {
@trigger_error(__METHOD__ . '() is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. No direct replacement is provided. See https://www.drupal.org/node/3151009', E_USER_DEPRECATED);
$select = $this->connection->select($this->tableName, 'router')
->fields('router', ['name', 'route']);
......@@ -443,9 +466,18 @@ public function getRoutesPaged($offset, $length = NULL) {
}
/**
* {@inheritdoc}
* Gets the total count of routes provided by the router.
*
* @return int
* Number of routes.
*
* @deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. No direct
* replacement is provided.
*
* @see https://www.drupal.org/node/3151009
*/
public function getRoutesCount() {
@trigger_error(__METHOD__ . '() is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. No direct replacement is provided. See https://www.drupal.org/node/3151009', E_USER_DEPRECATED);
return $this->connection->query("SELECT COUNT(*) FROM {" . $this->connection->escapeTable($this->tableName) . "}")->fetchField();
}
......
......@@ -2,6 +2,7 @@
namespace Drupal\Core\Routing;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Cmf\Component\Routing\RouteProviderInterface as RouteProviderBaseInterface;
/**
......@@ -11,6 +12,71 @@
*/
interface RouteProviderInterface extends RouteProviderBaseInterface {
/**
* Finds routes that may potentially match the request.
*
* 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.
*
* 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 \Symfony\Component\HttpFoundation\Request $request
* A request against which to match
*
* @return \Symfony\Component\Routing\RouteCollection
* All Routes that could potentially match $request.
* Empty collection if nothing can match
*/
public function getRouteCollectionForRequest(Request $request);
/**
* Find the route using the provided route name.
*
* @param string $name
* The route name to fetch
*
* @return \Symfony\Component\Routing\Route
* The Symfony route object.
*
* @throws \Symfony\Component\Routing\Exception\RouteNotFoundException
* If a matching route cannot be found.
*/
public function getRouteByName($name);
/**
* 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 or are not actually Route instances. It will just return the
* list of those Route instances it found.
*
* This method exists in order to allow performance optimizations. The
* simple implementation could be to just repeatedly call
* $this->getRouteByName() while catching and ignoring eventual exceptions.
*
* If $names is null, this method SHOULD return a collection of all routes
* known to this provider. If there are many routes to be expected, usage of
* a lazy loading collection is recommended. A provider MAY only return a
* subset of routes to e.g. support paging or other concepts.
*
* @param array|null $names
* The list of names to retrieve, In case of null, the provider will
* determine what routes to return
*
* @return \Symfony\Component\Routing\Route[]
* Iterable list with the keys being the names from the $names array
*/
public function getRoutesByNames($names);
/**
* Get all routes which match a certain pattern.
*
......
......@@ -2,14 +2,13 @@
namespace Drupal\Core\Routing;
use Symfony\Cmf\Component\Routing\PagedRouteProviderInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* A Route Provider front-end for all Drupal-stored routes.
*/
class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, PagedRouteProviderInterface, EventSubscriberInterface {
class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, EventSubscriberInterface {
/**
* The route provider service.
......@@ -122,16 +121,41 @@ public function reset() {
}
/**
* {@inheritdoc}
* Returns a chunk of routes.
*
* Should only be used in conjunction with an iterator.
*
* @param int $offset
* The query offset.
* @param int $length
* The number of records.
*
* @return \Symfony\Component\Routing\Route[]
* Routes keyed by the route name.
*
* @deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. No direct
* replacement is provided.
*
* @see https://www.drupal.org/node/3151009
*/
public function getRoutesPaged($offset, $length = NULL) {
@trigger_error(__METHOD__ . '() is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. No direct replacement is provided. See https://www.drupal.org/node/3151009', E_USER_DEPRECATED);
return $this->getRouteProvider()->getRoutesPaged($offset, $length);
}
/**
* {@inheritdoc}
* Gets the total count of routes provided by the router.
*
* @return int
* Number of routes.
*
* @deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. No direct
* replacement is provided.
*
* @see https://www.drupal.org/node/3151009
*/
public function getRoutesCount() {
@trigger_error(__METHOD__ . '() is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. No direct replacement is provided. See https://www.drupal.org/node/3151009', E_USER_DEPRECATED);
return $this->getRouteProvider()->getRoutesCount();
}
......
......@@ -3,8 +3,6 @@
namespace Drupal\Core\Routing;
use Drupal\Core\Path\CurrentPathStack;
use Symfony\Cmf\Component\Routing\LazyRouteCollection;
use Symfony\Cmf\Component\Routing\RouteProviderInterface as BaseRouteProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
......@@ -29,22 +27,13 @@
* regex. See ::matchCollection().
* 4. Enhance the list of route attributes, for example loading entity objects.
* See ::applyRouteEnhancers().
*
* This implementation uses ideas of the following routers:
* - \Symfony\Cmf\Component\Routing\DynamicRouter
* - \Drupal\Core\Routing\UrlMatcher
* - \Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
*
* @see \Symfony\Cmf\Component\Routing\DynamicRouter
* @see \Drupal\Core\Routing\UrlMatcher
* @see \Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
*/
class Router extends UrlMatcher implements RequestMatcherInterface, RouterInterface {
/**
* The route provider responsible for the first-pass match.
*
* @var \Symfony\Cmf\Component\Routing\RouteProviderInterface
* @var \Drupal\Core\Routing\RouteProviderInterface
*/
protected $routeProvider;
......@@ -72,14 +61,14 @@ class Router extends UrlMatcher implements RequestMatcherInterface, RouterInterf
/**
* Constructs a new Router.
*
* @param \Symfony\Cmf\Component\Routing\RouteProviderInterface $route_provider
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
* The route provider.
* @param \Drupal\Core\Path\CurrentPathStack $current_path
* The current path stack.
* @param \Symfony\Component\Routing\Generator\UrlGeneratorInterface $url_generator
* The URL generator.
*/
public function __construct(BaseRouteProviderInterface $route_provider, CurrentPathStack $current_path, BaseUrlGeneratorInterface $url_generator) {
public function __construct(RouteProviderInterface $route_provider, CurrentPathStack $current_path, BaseUrlGeneratorInterface $url_generator) {
parent::__construct($current_path);
$this->routeProvider = $route_provider;
$this->urlGenerator = $url_generator;
......
......@@ -2,14 +2,14 @@
namespace Drupal\Core\Routing;
use Symfony\Cmf\Component\Routing\VersatileGeneratorInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface as SymfonyUrlGeneratorInterface;
/**
* Defines an interface for generating a url from a route or system path.
*
* Provides additional methods and options not present in the base interface.
*/
interface UrlGeneratorInterface extends VersatileGeneratorInterface {
interface UrlGeneratorInterface extends SymfonyUrlGeneratorInterface {
/**
* Gets the internal path (system path) for a route.
......
......@@ -4,8 +4,9 @@
use Drupal\Core\Path\CurrentPathStack;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Cmf\Component\Routing\NestedMatcher\UrlMatcher as BaseUrlMatcher;
use Symfony\Component\Routing\Matcher\UrlMatcher as BaseUrlMatcher;
/**
* Drupal-specific URL Matcher; handles the Drupal "system path" mapping.
......@@ -41,4 +42,17 @@ public function finalMatch(RouteCollection $collection, Request $request) {
return $this->match($this->currentPath->getPath($request));
}
/**
* {@inheritdoc}
*/
protected function getAttributes(Route $route, $name, array $attributes) {
if ($route instanceof RouteObjectInterface && is_string($route->getRouteKey())) {
$name = $route->getRouteKey();
}
$attributes[RouteObjectInterface::ROUTE_NAME] = $name;
$attributes[RouteObjectInterface::ROUTE_OBJECT] = $route;
return $this->mergeDefaults($attributes, $route->getDefaults());
}
}
......@@ -21,7 +21,7 @@
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Yaml\Yaml as SymfonyYaml;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteObjectInterface;