diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php index 75f4246..b4f3f90 100644 --- a/core/lib/Drupal/Core/CoreBundle.php +++ b/core/lib/Drupal/Core/CoreBundle.php @@ -10,6 +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\RegisterPathProcessorsPass; use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass; use Drupal\Core\DependencyInjection\Compiler\RegisterSerializationClassesPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -230,6 +231,7 @@ public function build(ContainerBuilder $container) { ->addTag('event_subscriber'); $container->register('path_subscriber', 'Drupal\Core\EventSubscriber\PathSubscriber') ->addArgument(new Reference('path.alias_manager.cached')) + ->addArgument(new Reference('path_processor_manager')) ->addTag('event_subscriber'); $container->register('legacy_request_subscriber', 'Drupal\Core\EventSubscriber\LegacyRequestSubscriber') ->addTag('event_subscriber'); @@ -255,6 +257,8 @@ public function build(ContainerBuilder $container) { ->addTag('event_subscriber') ->addArgument(array(new Reference('exception_controller'), 'execute')); + $this->registerPathProcessors($container); + $container ->register('transliteration', 'Drupal\Core\Transliteration\PHPTransliteration'); @@ -373,4 +377,27 @@ protected function registerTwig(ContainerBuilder $container) { // @see http://drupal.org/node/1804998 ->addMethodCall('addExtension', array(new Definition('Twig_Extension_Debug'))); } + + /** + * Register services related to path manipulation. + */ + protected function registerPathProcessors(ContainerBuilder $container) { + // Register the path manipulator manager service. + $container->register('path_processor_manager', 'Drupal\Core\PathProcessor\PathProcessorManager'); + // Register the manipulator that urldecodes the path. + $container->register('path_processor_decode', 'Drupal\Core\PathProcessor\PathProcessorDecode') + ->addTag('incoming_path_processor', array('priority' => 300)); + // Register the manipulator that resolves the front page. + $container->register('path_processor_front', 'Drupal\Core\PathProcessor\PathProcessorFront') + ->addArgument(new Reference('config.factory')) + ->addTag('incoming_path_processor', array('priority' => 200)); + // Register the alias path manipulator. + $container->register('path_processor_alias', 'Drupal\Core\PathProcessor\PathProcessorAlias') + ->addArgument(new Reference('path.alias_manager')) + ->addTag('incoming_path_processor', array('priority' => 100)); + + // Add the compiler pass that will process the tagged services. + $container->addCompilerPass(new RegisterPathProcessorsPass()); + } + } diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterPathProcessorsPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterPathProcessorsPass.php new file mode 100644 index 0000000..08d31c1 --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterPathProcessorsPass.php @@ -0,0 +1,35 @@ +hasDefinition('path_processor_manager')) { + return; + } + $manager = $container->getDefinition('path_processor_manager'); + foreach ($container->findTaggedServiceIds('incoming_path_processor') as $id => $attributes) { + $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $manager->addMethodCall('addIncoming', array(new Reference($id), $priority)); + } + } +} diff --git a/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php index 698057b..42fa304 100644 --- a/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php @@ -8,6 +8,7 @@ namespace Drupal\Core\EventSubscriber; use Drupal\Core\CacheDecorator\AliasManagerCacheDecorator; +use Drupal\Core\PathProcessor\IncomingPathProcessorInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -20,24 +21,24 @@ class PathSubscriber extends PathListenerBase implements EventSubscriberInterface { protected $aliasManager; + protected $manipulatorManager; - public function __construct(AliasManagerCacheDecorator $alias_manager) { + public function __construct(AliasManagerCacheDecorator $alias_manager, IncomingPathProcessorInterface $manipulator) { $this->aliasManager = $alias_manager; + $this->manipulator = $manipulator; } /** - * Resolve the system path. + * Converts the request path to a system path. * * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event * The Event to process. */ - public function onKernelRequestPathResolve(GetResponseEvent $event) { + public function onKernelRequestConvertPath(GetResponseEvent $event) { $request = $event->getRequest(); - $path = $this->extractPath($request); - $path = $this->aliasManager->getSystemPath($path); - $this->setPath($request, $path); - // If this is the master request, set the cache key for the caching of all - // system paths looked up during the request. + $path = trim($request->getPathInfo(), '/'); + $path = $this->manipulator->processIncoming($path, $request); + $request->attributes->set('system_path', $path); if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { $this->aliasManager->setCacheKey($path); } @@ -51,81 +52,14 @@ public function onKernelTerminate(PostResponseEvent $event) { } /** - * Resolve the front-page default path. - * - * @todo The path system should be objectified to remove the function calls in - * this method. - * - * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event - * The Event to process. - */ - public function onKernelRequestFrontPageResolve(GetResponseEvent $event) { - $request = $event->getRequest(); - $path = $this->extractPath($request); - - if (empty($path)) { - // @todo Temporary hack. Fix when configuration is injectable. - $path = config('system.site')->get('page.front'); - if (empty($path)) { - $path = 'user'; - } - } - - $this->setPath($request, $path); - } - - /** - * Decode language information embedded in the request path. - * - * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event - * The Event to process. - */ - public function onKernelRequestLanguageResolve(GetResponseEvent $event) { - $request = $event->getRequest(); - $path = _language_resolved_path(); - if ($path !== NULL) { - $this->setPath($request, $path); - } - } - - /** - * Decodes the path of the request. - * - * Parameters in the URL sometimes represent code-meaningful strings. It is - * therefore useful to always urldecode() those values so that individual - * controllers need not concern themselves with it. This is Drupal-specific - * logic and may not be familiar for developers used to other Symfony-family - * projects. - * - * @todo Revisit whether or not this logic is appropriate for here or if - * controllers should be required to implement this logic themselves. If we - * decide to keep this code, remove this TODO. - * - * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event - * The Event to process. - */ - public function onKernelRequestDecodePath(GetResponseEvent $event) { - $request = $event->getRequest(); - $path = $this->extractPath($request); - - $path = urldecode($path); - - $this->setPath($request, $path); - } - - /** * Registers the methods in this class that should be listeners. * * @return array * An array of event listener definitions. */ static function getSubscribedEvents() { - $events[KernelEvents::REQUEST][] = array('onKernelRequestDecodePath', 200); - $events[KernelEvents::REQUEST][] = array('onKernelRequestLanguageResolve', 150); - $events[KernelEvents::REQUEST][] = array('onKernelRequestFrontPageResolve', 101); - $events[KernelEvents::REQUEST][] = array('onKernelRequestPathResolve', 100); + $events[KernelEvents::REQUEST][] = array('onKernelRequestConvertPath', 200); $events[KernelEvents::TERMINATE][] = array('onKernelTerminate', 200); - return $events; } } diff --git a/core/lib/Drupal/Core/PathProcessor/IncomingPathProcessorInterface.php b/core/lib/Drupal/Core/PathProcessor/IncomingPathProcessorInterface.php new file mode 100644 index 0000000..a634210 --- /dev/null +++ b/core/lib/Drupal/Core/PathProcessor/IncomingPathProcessorInterface.php @@ -0,0 +1,28 @@ +aliasManager = $alias_manager; + } + + /** + * Implements Drupal\Core\PathProcessor\IncomingPathProcessorInterface::processIncoming(). + */ + public function processIncoming($path, Request $request) { + $path = $this->aliasManager->getSystemPath($path); + return $path; + } + +} diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorDecode.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorDecode.php new file mode 100644 index 0000000..cfc8039 --- /dev/null +++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorDecode.php @@ -0,0 +1,35 @@ +config = $config; + } + + /** + * Implements Drupal\Core\PathProcessor\IncomingPathProcessorInterface::processIncoming(). + */ + public function processIncoming($path, Request $request) { + if (empty($path)) { + $path = $this->config->get('system.site')->get('page.front'); + if (empty($path)) { + $path = 'user'; + } + } + return $path; + } + +} diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorManager.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorManager.php new file mode 100644 index 0000000..6b5e6cb --- /dev/null +++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorManager.php @@ -0,0 +1,96 @@ +incomingProcessors[$priority])) { + $this->incomingProcessors[$priority] = array(); + } + + $this->incomingProcessors[$priority][] = $processor; + $this->sortedIncoming = array(); + } + + /** + * Implements Drupal\Core\PathProcessor\IncomingPathProcessorInterface::processIncoming(). + */ + public function processIncoming($path, Request $request) { + $processors = $this->getIncoming(); + foreach ($processors as $processor) { + $path = $processor->processIncoming($path, $request); + } + return $path; + } + + /** + * Returns the sorted array of incoming processors. + * + * @return array + * An array of processor objects. + */ + protected function getIncoming() { + if (empty($this->sortedIncoming)) { + $this->sortedIncoming = $this->sortProcessors('incomingProcessors'); + } + + return $this->sortedIncoming; + } + + /** + * Sorts the processors according to priority. + * + * @param string $type + * The processor type to sort, e.g. 'incomingProcessors'. + */ + protected function sortProcessors($type) { + $sorted = array(); + krsort($this->{$type}); + + foreach ($this->{$type} as $processors) { + $sorted = array_merge($sorted, $processors); + } + return $sorted; + } +} diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc index 280caf9..e89658f 100644 --- a/core/modules/language/language.negotiation.inc +++ b/core/modules/language/language.negotiation.inc @@ -281,11 +281,7 @@ function language_from_url($languages, Request $request = NULL) { case LANGUAGE_NEGOTIATION_URL_PREFIX: $request_path = urldecode(trim($request->getPathInfo(), '/')); - list($language, $path) = language_url_split_prefix($request_path, $languages); - // Store the correct system path, i.e., the request path without the - // language prefix. - _language_resolved_path($path); if ($language !== FALSE) { $language_url = $language->langcode; diff --git a/core/modules/language/lib/Drupal/language/LanguageBundle.php b/core/modules/language/lib/Drupal/language/LanguageBundle.php new file mode 100644 index 0000000..d16b334 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageBundle.php @@ -0,0 +1,29 @@ +register('path_processor_language', 'Drupal\language\PathProcessorLanguage') + ->addArgument(new Reference('module_handler')) + ->addTag('incoming_path_processor', array('priority' => 250)); + } + +} diff --git a/core/modules/language/lib/Drupal/language/PathProcessorLanguage.php b/core/modules/language/lib/Drupal/language/PathProcessorLanguage.php new file mode 100644 index 0000000..f380639 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/PathProcessorLanguage.php @@ -0,0 +1,37 @@ +moduleHandler = $module_handler; + } + + /** + * Implements Drupal\Core\PathProcessor\IncomingPathProcessorInterface::processIncoming(). + */ + public function processIncoming($path, Request $request) { + // Sigh. + include_once DRUPAL_ROOT . '/core/includes/language.inc'; + $this->moduleHandler->loadInclude('language', 'inc', 'language.negotiation'); + $languages = language_list(); + list($language, $path) = language_url_split_prefix($path, $languages); + return $path; + } + +}