-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[HttpKernel] Fix ArgumentValueResolver for arguments default null #21333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There is no BC break nor deprecation here, please remove the corresponding labels, my bad sorry. |
50b4bc7
to
d49390d
Compare
@@ -58,7 +58,7 @@ public function createArgumentMetadata($controller) | |||
} | |||
|
|||
foreach ($reflection->getParameters() as $param) { | |||
$arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $param->allowsNull()); | |||
$arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $this->isDefaultNull($param)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ReflectionParameter::allowsNull()
returns true for mandatory arguments which don't have any type hint i.e: function ($foo); // allowsNull(): true
Here we only want to know if the argument is default null, or has a typehint which allows null i.e. ?type
d49390d
to
0473c18
Compare
How come it injects |
Not sure but I believe the current implementation makes the Request resolver is just skipped so the default value wins. It can be reproduced with a fresh SE, changing the provided |
But that would mean that this code is not hit properly: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestValueResolver.php#L30-L38 Can you confirm this? Because this code is responsible for setting the request if it's supported and should work with the original setup (in theory). |
@iltar I can confirm this since using But right now I'm not able to say what exactly is wrong with the current implem, the patch made here avoids the only doubts I have about the origin of the issue (one being |
0473c18
to
46758c7
Compare
I'll try to reproduce it tomorrow. Maybe it's a sequence issue, because in theory this is only not called when something else already supports it and the |
46758c7
to
86931d5
Compare
Right! I looked deeper into and fact is that the RequestAttributeValueResolver is the culprit. |
@chalasr is there a specific reason that this line is returning true? https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php#L30 Perhaps the |
Did not considered that :/ then the current fix is not good, it just gives a lower priority to the request attribute resolver so it is called after the request/user resolver.
Right again! A Not sure what's the proper way to fix this :/ Any idea @iltar? |
I think we have to check why the attributes are created but empty, perhaps this is done for the parameter converters? An additional check could be to check if it |
Not sure but there're good chances. Note that these attributes stay null at the end (from the controller itself) since I do not have any param converter explicitly configured
Could you please develop?
If you mean adding an additional check inside |
The edit: Not PC but attribute resolver should be called first |
86931d5
to
426fafc
Compare
I updated this for reverting the previous changes and only adding the additional |
|
||
$argumentName = $argument->getName(); | ||
|
||
return $request->attributes->has($argumentName) && null !== $request->attributes->get($argumentName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I'm curious what happens if you have this:
$request->attributes->set('required', null);
public function fooAction(string $required);
edit: probably already broken/proper error in an old version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using:
/**
* @Route("/", name="homepage", defaults={"required": null})
*/
public function indexAction(string $required)
{
}
Controller "AppBundle\Controller\DefaultController::indexAction()" requires that you provide a value for the "$required" argument [...]
Changing the signature to indexAction(string $required = null);
or indexAction(?string $required);
works of course.
Same behavior on 3.0, run into the same exception if the attribute is null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this will now break the case where an attribute was set to null
deliberately. Isn't that a BC break?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, it is...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this signature it would fail already, you just simply can't set it to null
anymore and it will trigger the resolver.
// call this with null, it will still trigger the resolver
public function fooAction(UserInterface $user = null);
But I'm still curious as of why the request values are null in the attribute bag.
@xabbuh regarding the above example, that would fail already. Also I don't think that values not mapped in the route should be affecting the parameter resolvers:
/{required}
with(string $required)
= allowed to be set to null but will throw an error as its required/
with(string $required)
!= allowed to set to null, as it was not added in the route definition
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iltar it happens only when the argument has a default, I'll look if avoiding this breaks something but I think you're right. If we can remove this magic then the resolver stays as it is and continue to work as expected in case the attribute is set explicitly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you ask me, routing information should not be affected by arguments in the method that are not mapped in the route, but that's exactly what happens right now 😢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iltar No, I mean a parameter without a type hint and without a default value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xabbuh it could even break for ones that are type-hinted as objects with null as default value I think, which is what we are trying to fix. The current approach is definitely not the good one.
routing information should not be affected by arguments in the method that are not mapped in the route
I agree, but I don't know if it is expected somewhere internally (if someone has an hint?), nor where it happens
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xabbuh I agree on that point. If the compiled route would be without the default value, everything will work as expected
@iltar My bad, I forgot to revert it. The diff is clean now |
c9f527f
to
e31ad5a
Compare
Nice! 👍 So just for the confirmation, this behavior is only present with annotations? |
Yes, it is only present for annotations, which make this bug exists only when using them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like a double fix to me
e31ad5a
to
2c808ca
Compare
@fabpot I found it and was about to add a comment for clarifying this patch after discussed it with @iltar and @xabbuh on slack. The code removed here seems unnecessary, arguments with default values properly keep their default value even after removing it, same on 3.0. @lyrixx Could you please confirm that this code is no more needed and that removing it in 2.7 would not break anything? The bug fixed here exists on 2.7 as well, before that argument value resolvers were introduced. |
If the tests added back then by @lyrixx still work after the removal, I would say that this is safe to do on 2.7 instead of 3.1. Any drawbacks? |
2c808ca
to
f014cb6
Compare
f014cb6
to
d3fa8a1
Compare
Changed the target to 2.7. Tests seem green, let's see the build. |
Actually I don't know. The best way to know it is to test in a real project I guess. |
Ok thanks, tried out on 2.7 and it works as expected (for the record: screenshot on a fresh 2.7). |
Thank you @chalasr. |
…lt null (chalasr) This PR was merged into the 2.7 branch. Discussion ---------- [HttpKernel] Fix ArgumentValueResolver for arguments default null | Q | A | ------------- | --- | Branch? | 2.7 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | n/a If an argument has `null` as default value __and annotations are used for routing__, then it is not resolved by the ArgumentResolver e.g: ```php public function showAction(Request $request) { dump($request); // Request object } public function showAction(?Request $request) { dump($request); // Request object } public function showAction(Request $request = null) { dump($request); // null } ``` To me, the last example should have been a Request object too. Note that it is the same behavior when using `UserInterface` or whatever, I ran into this issue while trying to dump a user for a route accepting both anonymous and authenticated requests, the argument was always null while `$this->getUser()` returned the expected value. According to http://symfony.com/blog/new-in-symfony-3-2-user-value-resolver-for-controllers it should have worked through the argument. I propose to make it works as a bugfix. Any suggestion for improving the patch will be really welcomed. ping @iltar Commits ------- d3fa8a1 Avoid setting request attributes from signature arguments in AnnotationClassLoader
… attributes handling (chalasr) This PR was merged into the 2.7 branch. Discussion ---------- [Routing] Fix BC break in AnnotationClassLoader defaults attributes handling | Q | A | ------------- | --- | Branch? | 2.7 | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | 77289b9#commitcomment-20572462 | License | MIT | Doc PR | n/a This fixes a BC break introduced in #21333. Instead of removing the automatic request attributes creation, we keep it but only for attributes that are mandatory (i.e. present in the route path). Thanks to @iltar for the idea. Commits ------- 1d298f0 [Routing] Fix BC break in AnnotationClassLoader defaults attributes handling
If an argument has
null
as default value and annotations are used for routing, then it is not resolved by the ArgumentResolver e.g:To me, the last example should have been a Request object too.
Note that it is the same behavior when using
UserInterface
or whatever, I ran into this issue while trying to dump a user for a route accepting both anonymous and authenticated requests, the argument was always null while$this->getUser()
returned the expected value. According to http://symfony.com/blog/new-in-symfony-3-2-user-value-resolver-for-controllers it should have worked through the argument.I propose to make it works as a bugfix.
Any suggestion for improving the patch will be really welcomed. ping @iltar