diff --git a/core/core.services.yml b/core/core.services.yml index 3f68677200..91db25a357 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -640,10 +640,10 @@ services: arguments: ['@menu.link_tree', '@entity.manager', '@string_translation'] plugin.manager.menu.local_action: class: Drupal\Core\Menu\LocalActionManager - arguments: ['@controller_resolver', '@request_stack', '@current_route_match', '@router.route_provider', '@module_handler', '@cache.discovery', '@language_manager', '@access_manager', '@current_user'] + arguments: ['@http_kernel.controller.argument_resolver', '@request_stack', '@current_route_match', '@router.route_provider', '@module_handler', '@cache.discovery', '@language_manager', '@access_manager', '@current_user'] plugin.manager.menu.local_task: class: Drupal\Core\Menu\LocalTaskManager - arguments: ['@controller_resolver', '@request_stack', '@current_route_match', '@router.route_provider', '@module_handler', '@cache.discovery', '@language_manager', '@access_manager', '@current_user'] + arguments: ['@http_kernel.controller.argument_resolver', '@request_stack', '@current_route_match', '@router.route_provider', '@module_handler', '@cache.discovery', '@language_manager', '@access_manager', '@current_user'] plugin.manager.menu.contextual_link: class: Drupal\Core\Menu\ContextualLinkManager arguments: ['@controller_resolver', '@module_handler', '@cache.discovery', '@language_manager', '@access_manager', '@current_user', '@request_stack'] @@ -708,12 +708,37 @@ services: - [setContainer, ['@service_container']] title_resolver: class: Drupal\Core\Controller\TitleResolver - arguments: ['@controller_resolver', '@string_translation'] + arguments: ['@controller_resolver', '@string_translation', '@http_kernel.controller.argument_resolver'] http_kernel: class: Stack\StackedHttpKernel http_kernel.basic: class: Symfony\Component\HttpKernel\HttpKernel - arguments: ['@event_dispatcher', '@controller_resolver', '@request_stack'] + arguments: ['@event_dispatcher', '@controller_resolver', '@request_stack', '@http_kernel.controller.argument_resolver'] + http_kernel.controller.argument_resolver: + class: Symfony\Component\HttpKernel\Controller\ArgumentResolver + arguments: ['@http_kernel.controller.argument_metadata_factory', ['@argument_resolver.request_attribute', '@argument_resolver.raw_parameter', '@argument_resolver.request', '@argument_resolver.psr7_request', '@argument_resolver.route_match', '@argument_resolver.default']] + http_kernel.controller.argument_metadata_factory: + class: Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory + public: false + argument_resolver.request_attribute: + class: Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver + public: false + argument_resolver.raw_parameter: + class: Drupal\Core\Controller\ArgumentResolver\RawParameterValueResolver + public: false + argument_resolver.request: + class: Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver + public: false + argument_resolver.psr7_request: + class: Drupal\Core\Controller\ArgumentResolver\Psr7RequestValueResolver + arguments: ['@psr7.http_message_factory'] + public: false + argument_resolver.route_match: + class: Drupal\Core\Controller\ArgumentResolver\RouteMatchValueResolver + public: false + argument_resolver.default: + class: Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver + public: false http_middleware.negotiation: class: Drupal\Core\StackMiddleware\NegotiationMiddleware tags: @@ -1077,10 +1102,10 @@ services: - { name: render.main_content_renderer, format: drupal_modal } controller.form: class: Drupal\Core\Controller\HtmlFormController - arguments: ['@controller_resolver', '@form_builder', '@class_resolver'] + arguments: ['@http_kernel.controller.argument_resolver', '@form_builder', '@class_resolver'] controller.entity_form: class: Drupal\Core\Entity\HtmlEntityFormController - arguments: ['@controller_resolver', '@form_builder', '@entity.manager'] + arguments: ['@http_kernel.controller.argument_resolver', '@form_builder', '@entity.manager'] form_ajax_response_builder: class: Drupal\Core\Form\FormAjaxResponseBuilder arguments: ['@main_content_renderer.ajax', '@current_route_match'] @@ -1634,7 +1659,7 @@ services: arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@render_placeholder_generator', '@render_cache', '@request_stack', '%renderer.config%'] early_rendering_controller_wrapper_subscriber: class: Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber - arguments: ['@controller_resolver', '@renderer'] + arguments: ['@http_kernel.controller.argument_resolver', '@renderer'] tags: - { name: event_subscriber } # Placeholder strategies for rendering placeholders. diff --git a/core/lib/Drupal/Core/Controller/ArgumentResolver/Psr7RequestValueResolver.php b/core/lib/Drupal/Core/Controller/ArgumentResolver/Psr7RequestValueResolver.php new file mode 100644 index 0000000000..f5ce9f39a1 --- /dev/null +++ b/core/lib/Drupal/Core/Controller/ArgumentResolver/Psr7RequestValueResolver.php @@ -0,0 +1,47 @@ +httpMessageFactory = $http_message_factory; + } + + /** + * {@inheritdoc} + */ + public function supports(Request $request, ArgumentMetadata $argument) { + return $argument->getType() == ServerRequestInterface::class; + } + + /** + * {@inheritdoc} + */ + public function resolve(Request $request, ArgumentMetadata $argument) { + yield $this->httpMessageFactory->createRequest($request); + } + +} diff --git a/core/lib/Drupal/Core/Controller/ArgumentResolver/RawParameterValueResolver.php b/core/lib/Drupal/Core/Controller/ArgumentResolver/RawParameterValueResolver.php new file mode 100644 index 0000000000..7e2a35a470 --- /dev/null +++ b/core/lib/Drupal/Core/Controller/ArgumentResolver/RawParameterValueResolver.php @@ -0,0 +1,28 @@ +isVariadic() && $request->attributes->has('_raw_variables') && array_key_exists($argument->getName(), $request->attributes->get('_raw_variables')); + } + + /** + * {@inheritdoc} + */ + public function resolve(Request $request, ArgumentMetadata $argument) { + yield $request->attributes->get('_raw_variables')[$argument->getName()]; + } + +} diff --git a/core/lib/Drupal/Core/Controller/ArgumentResolver/RouteMatchValueResolver.php b/core/lib/Drupal/Core/Controller/ArgumentResolver/RouteMatchValueResolver.php new file mode 100644 index 0000000000..d5846fea20 --- /dev/null +++ b/core/lib/Drupal/Core/Controller/ArgumentResolver/RouteMatchValueResolver.php @@ -0,0 +1,30 @@ +getType() == RouteMatchInterface::class || is_subclass_of($argument->getType(), RouteMatchInterface::class); + } + + /** + * {@inheritdoc} + */ + public function resolve(Request $request, ArgumentMetadata $argument) { + yield RouteMatch::createFromRequest($request); + } + +} diff --git a/core/lib/Drupal/Core/Controller/ControllerResolver.php b/core/lib/Drupal/Core/Controller/ControllerResolver.php index c16a087449..3bae3c53b6 100644 --- a/core/lib/Drupal/Core/Controller/ControllerResolver.php +++ b/core/lib/Drupal/Core/Controller/ControllerResolver.php @@ -129,6 +129,10 @@ protected function createController($controller) { * {@inheritdoc} */ protected function doGetArguments(Request $request, $controller, array $parameters) { + // Note this duplicates the deprecation message of + // Symfony\Component\HttpKernel\Controller\ControllerResolver::getArguments() + // to ensure it is removed in Drupal 9. + @trigger_error(sprintf('%s is deprecated as of 8.6.0 and will be removed in 9.0. Inject the "http_kernel.controller.argument_resolver" service instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED); $attributes = $request->attributes->all(); $raw_parameters = $request->attributes->has('_raw_variables') ? $request->attributes->get('_raw_variables') : []; $arguments = []; diff --git a/core/lib/Drupal/Core/Controller/FormController.php b/core/lib/Drupal/Core/Controller/FormController.php index 7af86df179..acbcaf6c67 100644 --- a/core/lib/Drupal/Core/Controller/FormController.php +++ b/core/lib/Drupal/Core/Controller/FormController.php @@ -7,6 +7,7 @@ use Drupal\Core\Form\FormState; use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; /** * Common base class for form interstitial controllers. @@ -16,10 +17,24 @@ abstract class FormController { use DependencySerializationTrait; + /** + * The argument resolver. + * + * @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface + */ + protected $argumentResolver; + /** * The controller resolver. * * @var \Drupal\Core\Controller\ControllerResolverInterface + * + * @deprecated + * Deprecated property that is only assigned when the 'controller_resolver' + * service is used as the first parameter to FormController::__construct(). + * + * @see https://www.drupal.org/node/2959408 + * @see \Drupal\Core\Controller\FormController::__construct() */ protected $controllerResolver; @@ -33,13 +48,17 @@ /** * Constructs a new \Drupal\Core\Controller\FormController object. * - * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver - * The controller resolver. + * @param \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver + * The argument resolver. * @param \Drupal\Core\Form\FormBuilderInterface $form_builder * The form builder. */ - public function __construct(ControllerResolverInterface $controller_resolver, FormBuilderInterface $form_builder) { - $this->controllerResolver = $controller_resolver; + public function __construct(ArgumentResolverInterface $argument_resolver, FormBuilderInterface $form_builder) { + $this->argumentResolver = $argument_resolver; + if ($argument_resolver instanceof ControllerResolverInterface) { + @trigger_error("Using the 'controller_resolver' service as the first argument is deprecated, use the 'http_kernel.controller.argument_resolver' instead. If your subclass requires the 'controller_resolver' service add it as an additional argument. See https://www.drupal.org/node/2959408.", E_USER_DEPRECATED); + $this->controllerResolver = $argument_resolver; + } $this->formBuilder = $form_builder; } @@ -63,7 +82,7 @@ public function getContentResult(Request $request, RouteMatchInterface $route_ma $form_state = new FormState(); $request->attributes->set('form', []); $request->attributes->set('form_state', $form_state); - $args = $this->controllerResolver->getArguments($request, [$form_object, 'buildForm']); + $args = $this->argumentResolver->getArguments($request, [$form_object, 'buildForm']); $request->attributes->remove('form'); $request->attributes->remove('form_state'); diff --git a/core/lib/Drupal/Core/Controller/HtmlFormController.php b/core/lib/Drupal/Core/Controller/HtmlFormController.php index fe63038037..16bcc58537 100644 --- a/core/lib/Drupal/Core/Controller/HtmlFormController.php +++ b/core/lib/Drupal/Core/Controller/HtmlFormController.php @@ -5,6 +5,7 @@ use Drupal\Core\Form\FormBuilderInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\DependencyInjection\ClassResolverInterface; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; /** * Wrapping controller for forms that serve as the main page body. @@ -21,15 +22,15 @@ class HtmlFormController extends FormController { /** * Constructs a new \Drupal\Core\Routing\Enhancer\FormEnhancer object. * - * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver - * The controller resolver. + * @param \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver + * The argument resolver. * @param \Drupal\Core\Form\FormBuilderInterface $form_builder * The form builder. * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver * The class resolver. */ - public function __construct(ControllerResolverInterface $controller_resolver, FormBuilderInterface $form_builder, ClassResolverInterface $class_resolver) { - parent::__construct($controller_resolver, $form_builder); + public function __construct(ArgumentResolverInterface $argument_resolver, FormBuilderInterface $form_builder, ClassResolverInterface $class_resolver) { + parent::__construct($argument_resolver, $form_builder); $this->classResolver = $class_resolver; } diff --git a/core/lib/Drupal/Core/Controller/TitleResolver.php b/core/lib/Drupal/Core/Controller/TitleResolver.php index 954835d754..85fce7732c 100644 --- a/core/lib/Drupal/Core/Controller/TitleResolver.php +++ b/core/lib/Drupal/Core/Controller/TitleResolver.php @@ -5,6 +5,7 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; use Symfony\Component\Routing\Route; /** @@ -20,6 +21,13 @@ class TitleResolver implements TitleResolverInterface { */ protected $controllerResolver; + /** + * The argument resolver. + * + * @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface + */ + protected $argumentResolver; + /** * Constructs a TitleResolver instance. * @@ -27,10 +35,13 @@ class TitleResolver implements TitleResolverInterface { * The controller resolver. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The translation manager. + * @param \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver + * The argument resolver. */ - public function __construct(ControllerResolverInterface $controller_resolver, TranslationInterface $string_translation) { + public function __construct(ControllerResolverInterface $controller_resolver, TranslationInterface $string_translation, ArgumentResolverInterface $argument_resolver) { $this->controllerResolver = $controller_resolver; $this->stringTranslation = $string_translation; + $this->argumentResolver = $argument_resolver; } /** @@ -43,7 +54,7 @@ public function getTitle(Request $request, Route $route) { // trying to use empty values. if ($callback = $route->getDefault('_title_callback')) { $callable = $this->controllerResolver->getControllerFromDefinition($callback); - $arguments = $this->controllerResolver->getArguments($request, $callable); + $arguments = $this->argumentResolver->getArguments($request, $callable); $route_title = call_user_func_array($callable, $arguments); } elseif ($title = $route->getDefault('_title')) { diff --git a/core/lib/Drupal/Core/Entity/HtmlEntityFormController.php b/core/lib/Drupal/Core/Entity/HtmlEntityFormController.php index 73e828fdd4..586938eb18 100644 --- a/core/lib/Drupal/Core/Entity/HtmlEntityFormController.php +++ b/core/lib/Drupal/Core/Entity/HtmlEntityFormController.php @@ -2,10 +2,10 @@ namespace Drupal\Core\Entity; -use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\Controller\FormController; use Drupal\Core\Form\FormBuilderInterface; use Drupal\Core\Routing\RouteMatchInterface; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; /** * Wrapping controller for entity forms that serve as the main page body. @@ -22,15 +22,15 @@ class HtmlEntityFormController extends FormController { /** * Constructs a new \Drupal\Core\Routing\Enhancer\FormEnhancer object. * - * @param \Drupal\Core\Controller\ControllerResolverInterface $resolver - * The controller resolver. + * @param \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver + * The argument resolver. * @param \Drupal\Core\Form\FormBuilderInterface $form_builder * The form builder. * @param \Drupal\Core\Entity\EntityManagerInterface $manager * The entity manager. */ - public function __construct(ControllerResolverInterface $resolver, FormBuilderInterface $form_builder, EntityManagerInterface $manager) { - parent::__construct($resolver, $form_builder); + public function __construct(ArgumentResolverInterface $argument_resolver, FormBuilderInterface $form_builder, EntityManagerInterface $manager) { + parent::__construct($argument_resolver, $form_builder); $this->entityManager = $manager; } diff --git a/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php index 75ea5fd86a..0bb5b29a93 100644 --- a/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php @@ -5,12 +5,12 @@ use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Cache\CacheableResponseInterface; -use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\Render\AttachmentsInterface; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\Render\RenderContext; use Drupal\Core\Render\RendererInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; use Symfony\Component\HttpKernel\KernelEvents; @@ -55,11 +55,11 @@ class EarlyRenderingControllerWrapperSubscriber implements EventSubscriberInterface { /** - * The controller resolver. + * The argument resolver. * - * @var \Drupal\Core\Controller\ControllerResolverInterface + * @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface */ - protected $controllerResolver; + protected $argumentResolver; /** * The renderer. @@ -71,13 +71,13 @@ class EarlyRenderingControllerWrapperSubscriber implements EventSubscriberInterf /** * Constructs a new EarlyRenderingControllerWrapperSubscriber instance. * - * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver - * The controller resolver. + * @param \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver + * The argument resolver. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer. */ - public function __construct(ControllerResolverInterface $controller_resolver, RendererInterface $renderer) { - $this->controllerResolver = $controller_resolver; + public function __construct(ArgumentResolverInterface $argument_resolver, RendererInterface $renderer) { + $this->argumentResolver = $argument_resolver; $this->renderer = $renderer; } @@ -91,7 +91,7 @@ public function onController(FilterControllerEvent $event) { $controller = $event->getController(); // See \Symfony\Component\HttpKernel\HttpKernel::handleRaw(). - $arguments = $this->controllerResolver->getArguments($event->getRequest(), $controller); + $arguments = $this->argumentResolver->getArguments($event->getRequest(), $controller); $event->setController(function () use ($controller, $arguments) { return $this->wrapControllerExecutionInRenderContext($controller, $arguments); diff --git a/core/lib/Drupal/Core/Menu/LocalActionManager.php b/core/lib/Drupal/Core/Menu/LocalActionManager.php index f262d4724d..bd527375e4 100644 --- a/core/lib/Drupal/Core/Menu/LocalActionManager.php +++ b/core/lib/Drupal/Core/Menu/LocalActionManager.php @@ -5,6 +5,7 @@ use Drupal\Core\Access\AccessManagerInterface; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\DefaultPluginManager; @@ -15,7 +16,7 @@ use Drupal\Core\Routing\RouteProviderInterface; use Drupal\Core\Url; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; use Drupal\Core\Session\AccountInterface; /** @@ -47,10 +48,25 @@ class LocalActionManager extends DefaultPluginManager implements LocalActionMana 'class' => 'Drupal\Core\Menu\LocalActionDefault', ]; + /** + * An argument resolver object. + * + * @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface + */ + protected $argumentResolver; + /** * A controller resolver object. * * @var \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface + * + * @deprecated + * Using the 'controller_resolver' service as the first argument is + * deprecated, use the 'http_kernel.controller.argument_resolver' instead. + * If your subclass requires the 'controller_resolver' service add it as an + * additional argument. + * + * @see https://www.drupal.org/node/2959408 */ protected $controllerResolver; @@ -99,8 +115,8 @@ class LocalActionManager extends DefaultPluginManager implements LocalActionMana /** * Constructs a LocalActionManager object. * - * @param \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface $controller_resolver - * An object to use in introspecting route methods. + * @param \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver + * An object to use in resolving route arguments. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request stack. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match @@ -118,11 +134,15 @@ class LocalActionManager extends DefaultPluginManager implements LocalActionMana * @param \Drupal\Core\Session\AccountInterface $account * The current user. */ - public function __construct(ControllerResolverInterface $controller_resolver, RequestStack $request_stack, RouteMatchInterface $route_match, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, AccessManagerInterface $access_manager, AccountInterface $account) { + public function __construct(ArgumentResolverInterface $argument_resolver, RequestStack $request_stack, RouteMatchInterface $route_match, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, AccessManagerInterface $access_manager, AccountInterface $account) { // Skip calling the parent constructor, since that assumes annotation-based // discovery. $this->factory = new ContainerFactory($this, 'Drupal\Core\Menu\LocalActionInterface'); - $this->controllerResolver = $controller_resolver; + $this->argumentResolver = $argument_resolver; + if ($argument_resolver instanceof ControllerResolverInterface) { + @trigger_error("Using the 'controller_resolver' service as the first argument is deprecated, use the 'http_kernel.controller.argument_resolver' instead. If your subclass requires the 'controller_resolver' service add it as an additional argument. See https://www.drupal.org/node/2959408.", E_USER_DEPRECATED); + $this->controllerResolver = $argument_resolver; + } $this->requestStack = $request_stack; $this->routeMatch = $route_match; $this->routeProvider = $route_provider; @@ -150,7 +170,7 @@ protected function getDiscovery() { */ public function getTitle(LocalActionInterface $local_action) { $controller = [$local_action, 'getTitle']; - $arguments = $this->controllerResolver->getArguments($this->requestStack->getCurrentRequest(), $controller); + $arguments = $this->argumentResolver->getArguments($this->requestStack->getCurrentRequest(), $controller); return call_user_func_array($controller, $arguments); } diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php index 1046a9f13b..d20bef0054 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php @@ -20,6 +20,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; /** * Provides the default local task manager using YML as primary definition. @@ -50,10 +51,25 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI 'id' => '', ]; + /** + * An argument resolver object. + * + * @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface + */ + protected $argumentResolver; + /** * A controller resolver object. * - * @var \Drupal\Core\Controller\ControllerResolverInterface + * @var \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface + * + * @deprecated + * Using the 'controller_resolver' service as the first argument is + * deprecated, use the 'http_kernel.controller.argument_resolver' instead. + * If your subclass requires the 'controller_resolver' service add it as an + * additional argument. + * + * @see https://www.drupal.org/node/2959408 */ protected $controllerResolver; @@ -109,8 +125,8 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI /** * Constructs a \Drupal\Core\Menu\LocalTaskManager object. * - * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver - * An object to use in introspecting route methods. + * @param \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver + * An object to use in resolving route arguments. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request object to use for building titles and paths for plugin instances. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match @@ -128,9 +144,13 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI * @param \Drupal\Core\Session\AccountInterface $account * The current user. */ - public function __construct(ControllerResolverInterface $controller_resolver, RequestStack $request_stack, RouteMatchInterface $route_match, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, AccessManagerInterface $access_manager, AccountInterface $account) { + public function __construct(ArgumentResolverInterface $argument_resolver, RequestStack $request_stack, RouteMatchInterface $route_match, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, AccessManagerInterface $access_manager, AccountInterface $account) { $this->factory = new ContainerFactory($this, '\Drupal\Core\Menu\LocalTaskInterface'); - $this->controllerResolver = $controller_resolver; + $this->argumentResolver = $argument_resolver; + if ($argument_resolver instanceof ControllerResolverInterface) { + @trigger_error("Using the 'controller_resolver' service as the first argument is deprecated, use the 'http_kernel.controller.argument_resolver' instead. If your subclass requires the 'controller_resolver' service add it as an additional argument. See https://www.drupal.org/node/2959408.", E_USER_DEPRECATED); + $this->controllerResolver = $argument_resolver; + } $this->requestStack = $request_stack; $this->routeMatch = $route_match; $this->routeProvider = $route_provider; @@ -170,7 +190,7 @@ public function processDefinition(&$definition, $plugin_id) { public function getTitle(LocalTaskInterface $local_task) { $controller = [$local_task, 'getTitle']; $request = $this->requestStack->getCurrentRequest(); - $arguments = $this->controllerResolver->getArguments($request, $controller); + $arguments = $this->argumentResolver->getArguments($request, $controller); return call_user_func_array($controller, $arguments); } diff --git a/core/lib/Drupal/Core/Update/UpdateKernel.php b/core/lib/Drupal/Core/Update/UpdateKernel.php index a686c8865e..31b86c835a 100644 --- a/core/lib/Drupal/Core/Update/UpdateKernel.php +++ b/core/lib/Drupal/Core/Update/UpdateKernel.php @@ -106,7 +106,9 @@ protected function handleRaw(Request $request) { $this->setupRequestMatch($request); - $arguments = $controller_resolver->getArguments($request, $db_update_controller); + /** @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver */ + $argument_resolver = $container->get('http_kernel.controller.argument_resolver'); + $arguments = $argument_resolver->getArguments($request, $db_update_controller); return call_user_func_array($db_update_controller, $arguments); } diff --git a/core/tests/Drupal/Tests/Core/Controller/ControllerResolverTest.php b/core/tests/Drupal/Tests/Core/Controller/ControllerResolverTest.php index 5c4166834e..a14f338aeb 100644 --- a/core/tests/Drupal/Tests/Core/Controller/ControllerResolverTest.php +++ b/core/tests/Drupal/Tests/Core/Controller/ControllerResolverTest.php @@ -72,6 +72,7 @@ protected function setUp() { * @see \Drupal\Core\Controller\ControllerResolver::doGetArguments() * * @group legacy + * @expectedDeprecation Drupal\Core\Controller\ControllerResolver::doGetArguments is deprecated as of 8.6.0 and will be removed in 9.0. Inject the "http_kernel.controller.argument_resolver" service instead. */ public function testGetArguments() { $controller = function (EntityInterface $entity, $user, RouteMatchInterface $route_match, ServerRequestInterface $psr_7) { diff --git a/core/tests/Drupal/Tests/Core/Controller/FormControllerTest.php b/core/tests/Drupal/Tests/Core/Controller/FormControllerTest.php new file mode 100644 index 0000000000..195c36fb46 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Controller/FormControllerTest.php @@ -0,0 +1,30 @@ +getMockBuilder(ControllerResolver::class)->disableOriginalConstructor()->getMock(); + $form_builder = $this->getMockBuilder(FormBuilderInterface::class)->getMock(); + $class_resolver = $this->getMockBuilder(ClassResolverInterface::class)->getMock(); + // Use the HtmlFormController as FormController is abstract. + new HtmlFormController($controller_resolver, $form_builder, $class_resolver); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php b/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php index 7633f78a13..1818f8a2be 100644 --- a/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php +++ b/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php @@ -34,6 +34,13 @@ class TitleResolverTest extends UnitTestCase { */ protected $translationManager; + /** + * The mocked argument resolver. + * + * @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $argumentResolver; + /** * The actual tested title resolver. * @@ -44,8 +51,9 @@ class TitleResolverTest extends UnitTestCase { protected function setUp() { $this->controllerResolver = $this->getMock('\Drupal\Core\Controller\ControllerResolverInterface'); $this->translationManager = $this->getMock('\Drupal\Core\StringTranslation\TranslationInterface'); + $this->argumentResolver = $this->getMock('\Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface'); - $this->titleResolver = new TitleResolver($this->controllerResolver, $this->translationManager); + $this->titleResolver = new TitleResolver($this->controllerResolver, $this->translationManager, $this->argumentResolver); } /** @@ -108,7 +116,7 @@ public function testDynamicTitle() { ->method('getControllerFromDefinition') ->with('Drupal\Tests\Core\Controller\TitleCallback::example') ->will($this->returnValue($callable)); - $this->controllerResolver->expects($this->once()) + $this->argumentResolver->expects($this->once()) ->method('getArguments') ->with($request, $callable) ->will($this->returnValue(['example'])); diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php index 0f1f848c92..58b453a11b 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php @@ -13,8 +13,11 @@ use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResultForbidden; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Controller\ControllerResolver; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Language\Language; use Drupal\Core\Menu\LocalActionManager; +use Drupal\Core\Menu\LocalTaskManager; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteProviderInterface; use Drupal\Core\Session\AccountInterface; @@ -22,7 +25,7 @@ use Drupal\Tests\UnitTestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; /** * @coversDefaultClass \Drupal\Core\Menu\LocalActionManager @@ -31,11 +34,11 @@ class LocalActionManagerTest extends UnitTestCase { /** - * The mocked controller resolver. + * The mocked argument resolver. * - * @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $controllerResolver; + protected $argumentResolver; /** * The mocked request. @@ -104,7 +107,7 @@ class LocalActionManagerTest extends UnitTestCase { * {@inheritdoc} */ protected function setUp() { - $this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface'); + $this->argumentResolver = $this->getMock('\Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface'); $this->request = $this->getMock('Symfony\Component\HttpFoundation\Request'); $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); @@ -120,7 +123,7 @@ protected function setUp() { $this->factory = $this->getMock('Drupal\Component\Plugin\Factory\FactoryInterface'); $route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface'); - $this->localActionManager = new TestLocalActionManager($this->controllerResolver, $this->request, $route_match, $this->routeProvider, $this->moduleHandler, $this->cacheBackend, $this->accessManager, $this->account, $this->discovery, $this->factory); + $this->localActionManager = new TestLocalActionManager($this->argumentResolver, $this->request, $route_match, $this->routeProvider, $this->moduleHandler, $this->cacheBackend, $this->accessManager, $this->account, $this->discovery, $this->factory); } /** @@ -132,7 +135,7 @@ public function testGetTitle() { ->method('getTitle') ->with('test'); - $this->controllerResolver->expects($this->once()) + $this->argumentResolver->expects($this->once()) ->method('getArguments') ->with($this->request, [$local_action, 'getTitle']) ->will($this->returnValue(['test'])); @@ -161,7 +164,7 @@ public function testGetActionsForRoute($route_appears, array $plugin_definitions $plugin->expects($this->any()) ->method('getTitle') ->will($this->returnValue($plugin_definition['title'])); - $this->controllerResolver->expects($this->any()) + $this->argumentResolver->expects($this->any()) ->method('getArguments') ->with($this->request, [$plugin, 'getTitle']) ->will($this->returnValue([])); @@ -169,7 +172,7 @@ public function testGetActionsForRoute($route_appears, array $plugin_definitions $plugin->expects($this->any()) ->method('getWeight') ->will($this->returnValue($plugin_definition['weight'])); - $this->controllerResolver->expects($this->any()) + $this->argumentResolver->expects($this->any()) ->method('getArguments') ->with($this->request, [$plugin, 'getTitle']) ->will($this->returnValue([])); @@ -381,17 +384,37 @@ public function getActionsForRouteProvider() { return $data; } + /** + * @expectedDeprecation Using the 'controller_resolver' service as the first argument is deprecated, use the 'http_kernel.controller.argument_resolver' instead. If your subclass requires the 'controller_resolver' service add it as an additional argument. See https://www.drupal.org/node/2959408. + * @group legacy + */ + public function testControllerResolverDeprecation() { + $controller_resolver = $this->getMockBuilder(ControllerResolver::class)->disableOriginalConstructor()->getMock(); + $route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface'); + $request_stack = new RequestStack(); + $request_stack->push($this->request); + $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); + $module_handler->expects($this->any()) + ->method('getModuleDirectories') + ->willReturn([]); + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); + $language_manager->expects($this->any()) + ->method('getCurrentLanguage') + ->will($this->returnValue(new Language(['id' => 'en']))); + new LocalTaskManager($controller_resolver, $request_stack, $route_match, $this->routeProvider, $this->moduleHandler, $this->cacheBackend, $language_manager, $this->accessManager, $this->account); + } + } class TestLocalActionManager extends LocalActionManager { - public function __construct(ControllerResolverInterface $controller_resolver, Request $request, RouteMatchInterface $route_match, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, AccessManagerInterface $access_manager, AccountInterface $account, DiscoveryInterface $discovery, FactoryInterface $factory) { + public function __construct(ArgumentResolverInterface $argument_resolver, Request $request, RouteMatchInterface $route_match, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, AccessManagerInterface $access_manager, AccountInterface $account, DiscoveryInterface $discovery, FactoryInterface $factory) { $this->discovery = $discovery; $this->factory = $factory; $this->routeProvider = $route_provider; $this->accessManager = $access_manager; $this->account = $account; - $this->controllerResolver = $controller_resolver; + $this->argumentResolver = $argument_resolver; $this->requestStack = new RequestStack(); $this->requestStack->push($request); $this->routeMatch = $route_match; diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTestBase.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTestBase.php index 839f8ae083..c7d54e21a8 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTestBase.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTestBase.php @@ -61,10 +61,10 @@ protected function getLocalTaskManager($module_dirs, $route_name, $route_params) ->setMethods(NULL) ->getMock(); - $controllerResolver = $this->getMock('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface'); - $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'controllerResolver'); + $argumentResolver = $this->getMock('Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface'); + $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'argumentResolver'); $property->setAccessible(TRUE); - $property->setValue($manager, $controllerResolver); + $property->setValue($manager, $argumentResolver); // todo mock a request with a route. $request_stack = new RequestStack(); diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php index 624d592214..ee63c8dee4 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php @@ -7,6 +7,7 @@ use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\Context\CacheContextsManager; +use Drupal\Core\Controller\ControllerResolver; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Language\Language; use Drupal\Core\Menu\LocalTaskInterface; @@ -31,11 +32,11 @@ class LocalTaskManagerTest extends UnitTestCase { protected $manager; /** - * The mocked controller resolver. + * The mocked argument resolver. * * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $controllerResolver; + protected $argumentResolver; /** * The test request. @@ -99,7 +100,7 @@ class LocalTaskManagerTest extends UnitTestCase { protected function setUp() { parent::setUp(); - $this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface'); + $this->argumentResolver = $this->getMock('Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface'); $this->request = new Request(); $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); $this->pluginDiscovery = $this->getMock('Drupal\Component\Plugin\Discovery\DiscoveryInterface'); @@ -237,7 +238,7 @@ public function testGetTitle() { $menu_local_task->expects($this->once()) ->method('getTitle'); - $this->controllerResolver->expects($this->once()) + $this->argumentResolver->expects($this->once()) ->method('getArguments') ->with($this->request, [$menu_local_task, 'getTitle']) ->will($this->returnValue([])); @@ -245,6 +246,26 @@ public function testGetTitle() { $this->manager->getTitle($menu_local_task); } + /** + * @expectedDeprecation Using the 'controller_resolver' service as the first argument is deprecated, use the 'http_kernel.controller.argument_resolver' instead. If your subclass requires the 'controller_resolver' service add it as an additional argument. See https://www.drupal.org/node/2959408. + * @group legacy + */ + public function testControllerResolverDeprecation() { + $controller_resolver = $this->getMockBuilder(ControllerResolver::class)->disableOriginalConstructor()->getMock(); + $request_stack = new RequestStack(); + $request_stack->push($this->request); + $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); + $module_handler->expects($this->any()) + ->method('getModuleDirectories') + ->willReturn([]); + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); + $language_manager->expects($this->any()) + ->method('getCurrentLanguage') + ->will($this->returnValue(new Language(['id' => 'en']))); + + new LocalTaskManager($controller_resolver, $request_stack, $this->routeMatch, $this->routeProvider, $module_handler, $this->cacheBackend, $language_manager, $this->accessManager, $this->account); + } + /** * Setups the local task manager for the test. */ @@ -260,7 +281,7 @@ protected function setupLocalTaskManager() { ->method('getCurrentLanguage') ->will($this->returnValue(new Language(['id' => 'en']))); - $this->manager = new LocalTaskManager($this->controllerResolver, $request_stack, $this->routeMatch, $this->routeProvider, $module_handler, $this->cacheBackend, $language_manager, $this->accessManager, $this->account); + $this->manager = new LocalTaskManager($this->argumentResolver, $request_stack, $this->routeMatch, $this->routeProvider, $module_handler, $this->cacheBackend, $language_manager, $this->accessManager, $this->account); $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'discovery'); $property->setAccessible(TRUE); @@ -424,7 +445,7 @@ public function testGetTasksBuildWithCacheabilityMetadata() { $this->setupFactoryAndLocalTaskPlugins($definitions, 'menu_local_task_test_tasks_view'); $this->setupLocalTaskManager(); - $this->controllerResolver->expects($this->any()) + $this->argumentResolver->expects($this->any()) ->method('getArguments') ->willReturn([]); diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php index 08c84a19c6..b5a6ea9149 100644 --- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php +++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php @@ -99,8 +99,6 @@ private function willBeIsolated($test) { */ public static function getSkippedDeprecations() { return [ - 'As of 3.1 an Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface is used to resolve arguments. In 4.0 the $argumentResolver becomes the Symfony\Component\HttpKernel\Controller\ArgumentResolver if no other is provided instead of using the $resolver argument.', - 'Symfony\Component\HttpKernel\Controller\ControllerResolver::getArguments is deprecated as of 3.1 and will be removed in 4.0. Implement the Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface and inject it in the HttpKernel instead.', 'The Twig_Environment::getCacheFilename method is deprecated since version 1.22 and will be removed in Twig 2.0.', 'Install profile will be a mandatory parameter in Drupal 9.0.', 'Setting the strict option of the Choice constraint to false is deprecated since version 3.2 and will be removed in 4.0.',