Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
Drupal 8.5.x comes with symfony 3.1 which has a new dependency on the argument resolver.
if (null === $this->argumentResolver) {
@trigger_error(sprintf('As of 3.1 an %s is used to resolve arguments. In 4.0 the $argumentResolver becomes the %s if no other is provided instead of using the $resolver argument.', ArgumentResolverInterface::class, ArgumentResolver::class), E_USER_DEPRECATED);
// fallback in case of deprecations
$this->argumentResolver = $resolver;
}
Proposed resolution
Adapt the core.services.yml
file to add the argument resolver.
Remaining tasks
User interface changes
API changes
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#50 | 2912169-50.patch | 48.75 KB | alexpott |
#50 | 43-50-interdiff.txt | 1.4 KB | alexpott |
#43 | 2912169-43.patch | 48.79 KB | alexpott |
#43 | 40-43-interdiff.txt | 12.63 KB | alexpott |
#40 | 2912169-40.patch | 40.47 KB | alexpott |
Comments
Comment #2
tobiberlinI am a novice and would like to offer my patch - it defines the Symfony ArgumentResolver as a service in core.services.yml and adds it as an argument for HttpKernel.
Looking at ArgumentResolver->__construct():
This constructor defines optional parameters. In core,services.yml I did not add arguments to the service definition for ArgumentResolver. There is only one implementation of ArgumentMetadataFactoryInterface: ArgumentMetadataFactory which does not need and parameters on construction by itself.
Is that the way?
Comment #3
dawehnerOne less @trigger_error call on every request! I set it to needs review to let the testbot pick it up.
+1 for prefixing the service with http_kernel. Its a different argument resolver than what we have. Do you mind opening up a new issue to detect whether we might could deprecate our own one?
Comment #4
tobiberlinThank you for your feedback. I wrote a new issue as you suggested and referenced to this one: https://www.drupal.org/node/2916310
Comment #7
alexpottMerged #2949627: Fix deprecated usages of Symfony HttpKernel in. Crediting @grahamC.
Comment #9
alexpottHmmm... I don't think we can inject the symfony one in. Our controllers and arguments don't work that way. We need to separate the ControllerResolverInterface and ArgumentResolverInterface from Drupal's ControllerResolver. Fun.
Comment #10
alexpottThis is going to be super hard to fix properly in Drupal 8 because it is going to be changes to things like \Drupal\Core\Controller\FormController which is extended by modules like ctools - so changing the constructor arguments in core will break that. We need to inject a new ArgumentResolverInterface service in order to conform to the expectations for Symfony 4.
Comment #11
alexpottHmmm maybe #10 is okay because if something passes in the controller resolver it still implements ArgumentresolverInterface - and any usages of the methods will get a deprecation message. But nothing should be broken.
Patch attached is a implementation.
Comment #12
alexpottMore things we need to swap out.
Comment #13
alexpottA few more
Comment #15
alexpottMore...
Comment #19
alexpottFinal fix.
Comment #20
alexpottI've run the
Drupal\ctools\Tests\Wizard\CToolsWizardTest
which tests ctool's form controllers still works with this patch applied. (It currently fails on 8.6.x HEAD and this patch in the same way - but the ctools.wizard.form services are not broken because the@controller_resolver
implements ArgumentResolverInterface.Comment #21
Berdirentity_browser also has a subclass of this.
I don't quite understand yet how this doesn't break, those two interfaces do not seem to share anything?
Comment #22
alexpott@Berdir yeah it's super fun - basically it is clever Symfony. Our \Drupal\Core\Controller\ControllerResolver extends \Symfony\Component\HttpKernel\Controller\ControllerResolver which implements \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface and for BC \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface. So our concrete implementation implements both of those interfaces too. And \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface::getArguments and \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface::getArguments have the same signature apart from the fact that ControllerResolverInterface::getArguments is deprecated. It took me a while to get my head around this.
Comment #23
alexpottFixing @todo.
I'm making this a major because without this patch we are currently triggering a silenced deprecation on every request made to a site.
Comment #24
alexpottAdd a change record https://www.drupal.org/node/2959408
Comment #25
dawehnerWe should totally move over the existing tests ...
I don't get why we need to change all these places too. These don't resolve the problem of httpKernel throwing an error.
Comment #26
Berdir#22: Ok, I get it now. If anyone else is confused, here is the class diagram:
We did the type hint on our own ControllerResolverInterface, which implements both symfony interfaces now. So the old class is compatible with both type hints. The new class however just implements the new one, so there is a unlikely chance that someone has the old type hint and somehow ends up getting the new implementation, but I don't quite see how.
@dawehner: The change is not necessary for the first deprecation message that we are removing, but I think it is for the second because that' about calling getArguments() I guess we could split it up and do those parts separataly but it's pretty closely related and conceptually about the same deprecation/change?
Comment #27
larowlannit, Note this instead of Note the
Comment #28
alexpottLooking at https://github.com/symfony/symfony/pull/18308 I wonder if I'm doing this right. Maybe we should be using Symfony's \Symfony\Component\HttpKernel\Controller\ArgumentResolver and adding new \Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface objects for our special ways to resolve arguments.
Comment #29
alexpottThis would potentially allow as to cache the result of \Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory::createArgumentMetadata() and not do multiple relections on the controller in a request.
Comment #30
alexpottTo go the full Symfony way we need to fix our container dumper - see #2961380: Support IteratorArgument in \Drupal\Component\DependencyInjection\Dumper\OptimizedPhpArrayDumper::dumpValue.
Patch attach reworks this to use value resolvers. We need to performance test this since this is on the critical path. No interdiff because this is a new approach.
In the future it'll allow us to support variadic arguments and more. For example, we could do something like \Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver and then just inject services into controllers with no need for the ::create method we have a the moment. For way less boilerplate.
Comment #31
alexpottDiscussed with @dawehner he pointed out that we shouldn't really add a new extension point here. We might choose to do that later maybe to remove param convertors. Also not adding the tagged service extension point here allows us to not need #2961380: Support IteratorArgument in \Drupal\Component\DependencyInjection\Dumper\OptimizedPhpArrayDumper::dumpValue.
Comment #32
alexpottWhoops forgot a bit.
Comment #35
alexpottHo hum.
Comment #37
alexpottI tried to profile this change wrt to requesting the node view or a single node using blackfire. It didn't even show up in the graphs because blackfire will ignore anything that
Comment #38
alexpottResolved the @todo's.
Comment #39
BerdirMaybe we should profile it on a minimal controller response that returns a response object, to avoid most of the other things, something like an autocomplete controller for example or even something that returns a static response?
looks like this is the only one left with the tag, is that really needed now? did not review the recent patches.
still needa bunch of docblocks on these classes.Crosspost :)
so the type might be backwards compatible, but what about the varible name? What if someone overrides the class, uses this but did not change the constructor?
protected variables are excluded I guess, but still unfortunate if we break it like this?
This one for example adds a new argument and keeps the old one, what's the difference?
Comment #40
alexpottThanks for the review @Berdir
1. Fixed
2. :)
3. Hmmm... do we have any examples of this in contrib? I feel that not changing the arguments to the constructor is better for BC. I guess we could keep the property and mark it for removal in 9? Can't do anything to trigger a deprecation though :(
4. Because the TitleResolver does
$callable = $this->controllerResolver->getControllerFromDefinition($callback);
Comment #41
Berdir3. We could if we remove it and go through __get(). Not sure if it's worth doing that, but it should work ;)
Comment #42
alexpottI've thought about @Berdir's comment and I think we need to implement a more complicated BC layer :(
Comment #43
alexpottHere's a patch with more BC.
Comment #44
BerdirI think this is fine now.
The only BC thing I can see is that controllerResolver is now only set if the old service is passed in, If there is a subclass out there that uses $this->controllerResolver without overriding the constructor then it would be broken. But I guess that's not very likely and AFAIK, protected properties are technically excluded from BC (so are constructors...)
Comment #45
dawehnerOne question: The current name suggests some less generic applicability of this class than before. Is this something we could change to 'argument_resolver' directly maybe?
Comment #46
alexpott@dawehner I named it this to steer well clear of \Drupal\Component\Utility\ArgumentsResolver - so I went with a service name that indicated its for resolving arguments for a controller as part of the http kernel.
Comment #47
alexpott@dawehner see your comment #3 :)
Comment #48
dawehnerHa :) Yeah I agree with myself
Comment #49
catchAll nitpicks but just enough to mark CNW.
It's a little bit odd that this doesn't refer to the property. Would expect it to read something like "Deprecated property that is only assigned when the the controller_resolver service is used as the first parameter" or similar. Also it's not technically the service that's deprecated but passing an instance of ControllerResolverInterface - I can never quite figure out when to refer to interfaces vs. services when they're 1-1 in practice. The service reference tells you where to fix it I guess but I also sit there thinking "How do you know I injected this particularly named service".
I saw Berdir's comment before the code, then had to read this a couple of times - but agreed it's fine. The constructor and the property are both fair game, and this allows the deprecated property and bc layer to be dropped cleanly in 9.x. Don't think we've done it like this before, but it probably won't be the last time.
We should either remove 'that' or finish the sentence.
Comment #50
alexpottThanks for the review @catch.
that
so we can add additional test coverage of FormController here without having to change things.Comment #51
catchChanges look good so moving back to RTBC.
Comment #52
catchCommitted 13768e8 and pushed to 8.6.x. Thanks!
Comment #55
quietone CreditAttribution: quietone at PreviousNext commentedPublish the change record