Skip to content

[HttpKernel] Make ServiceValueResolver work if controller namespace starts with a backslash in routing #26773

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

Merged
merged 1 commit into from
Apr 14, 2018

Conversation

mathieutu
Copy link
Contributor

@mathieutu mathieutu commented Apr 3, 2018

Q A
Branch? 3.4
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #26772
License MIT

Hi folks,

This PR fixes #26772:

Controller "App\Controllers\Foo" requires that you provide a value for the "$foo" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.

Thank you for your work!

PS: This is my first (of many planned!) PR to Symfony, so please let me know if I did something wrong.

@mathieutu mathieutu changed the title Make ServiceValueResolver working if controller has a backslash at first character in routing. [HttpKernel][Fix] Make ServiceValueResolver working if controller namespace start with a backslash in routing. Apr 3, 2018
@mathieutu mathieutu changed the title [HttpKernel][Fix] Make ServiceValueResolver working if controller namespace start with a backslash in routing. [HttpKernel][Fix] Make ServiceValueResolver working if controller namespace starts with a backslash in routing. Apr 3, 2018
@@ -41,6 +41,8 @@ public function supports(Request $request, ArgumentMetadata $argument)
$controller = $controller[0].'::'.$controller[1];
}

$controller = ltrim($controller, '\\');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trim takes a list of characters, thus having two backslashes is redundant. You may want to use \ltrim too but not sure about the convention here

Copy link
Contributor Author

@mathieutu mathieutu Apr 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Meroje if I don't use the two backslashes, it will consider that I'm escaping the last ' and never close the string:

ltrim($controller, '\')

And for the \ltrim, I've just done the same that in the rest of the code.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha nvm then, thought for a second it wouldn't work at the end of a string

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's useful for ltrim: PHP-CS-Fixer/PHP-CS-Fixer#3048

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to put the ltrim in a condition.
Basically, I'd suggest doing this:

if (\is_array($controller) && \is_callable($controller, true) && \is_string($controller[0])) {
    $controller = $controller[0].'::'.$controller[1];
} elseif (!\is_string($controller) || '' === $controller) {
    return false;
}
if ('\\' === $controller[0]) {
    $controller = ltrim($controller, '\\');
}

return \$this->container->has($controller) && $this->container->get($controller)->has($argument->getName()); 

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Congrats for your first PR!
Here are some nitpicking :P

@@ -41,6 +41,8 @@ public function supports(Request $request, ArgumentMetadata $argument)
$controller = $controller[0].'::'.$controller[1];
}

$controller = ltrim($controller, '\\');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to put the ltrim in a condition.
Basically, I'd suggest doing this:

if (\is_array($controller) && \is_callable($controller, true) && \is_string($controller[0])) {
    $controller = $controller[0].'::'.$controller[1];
} elseif (!\is_string($controller) || '' === $controller) {
    return false;
}
if ('\\' === $controller[0]) {
    $controller = ltrim($controller, '\\');
}

return \$this->container->has($controller) && $this->container->get($controller)->has($argument->getName()); 

@@ -53,6 +55,8 @@ public function resolve(Request $request, ArgumentMetadata $argument)
$controller = $controller[0].'::'.$controller[1];
}

$controller = ltrim($controller, '\\');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consistently with the previous comment, wrap this call in if ('\\' === $controller[0]) {...}

Copy link
Contributor Author

@mathieutu mathieutu Apr 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks Nicolas.
One question, why the condition to format the controller if it's not an array is not the same in both supports and resolve methods?

Couldn't we extract this in its own method to make it consistent?

}

if ('\\' === $controller[0]) {
$controller = ltrim($controller, '\\');
}

return \is_string($controller) && $this->container->has($controller) && $this->container->get($controller)->has($argument->getName());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_string($controller) is useless here now, as it will always be true (due to the new check added above)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, thx!

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(for 3.4)

}

return \is_string($controller) && $this->container->has($controller) && $this->container->get($controller)->has($argument->getName());
if ('\\' === $controller[0]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to test first and then trim? Why not always trim?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trim means triggering copy-on-write when the controller string usually comes from shared memory, which means also recomputing the hash of the string, even if it doesn't change.

@stof
Copy link
Member

stof commented Apr 4, 2018

another solution could be to perform this trimming during the cache warmup of the router (we already have some logic there to resolve the bundle notation to class names there to optimize things). This way, we would have the controller class without leading backslash in the _controller key

@mathieutu
Copy link
Contributor Author

Yeah could be a good idea, maybe a bit more consistent, and need less operations.
As you want guys, let me know and I'll try to do it on router!

@nicolas-grekas
Copy link
Member

Worth a try for sure!

@nicolas-grekas nicolas-grekas changed the title [HttpKernel][Fix] Make ServiceValueResolver working if controller namespace starts with a backslash in routing. [HttpKernel][Fix] Make ServiceValueResolver work if controller namespace starts with a backslash in routing. Apr 5, 2018
@nicolas-grekas nicolas-grekas changed the title [HttpKernel][Fix] Make ServiceValueResolver work if controller namespace starts with a backslash in routing. [HttpKernel][Fix] Make ServiceValueResolver work if controller namespace starts with a backslash in routing Apr 5, 2018
@nicolas-grekas nicolas-grekas changed the title [HttpKernel][Fix] Make ServiceValueResolver work if controller namespace starts with a backslash in routing [HttpKernel] Make ServiceValueResolver work if controller namespace starts with a backslash in routing Apr 5, 2018
@Tobion
Copy link
Contributor

Tobion commented Apr 7, 2018

another solution could be to perform this trimming during the cache warmup of the router (we already have some logic there to resolve the bundle notation to class names there to optimize things).

Since the bundle notation is deprecated, this part might not survive long. So I would either do it in the Route class itself, so it it always set in a normalized way for all consumers. Or do it like proposed here.

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's do it this way: this responsibility on ServiceValueResolver looks legit to me

@nicolas-grekas nicolas-grekas changed the base branch from 4.0 to 3.4 April 14, 2018 15:06
@nicolas-grekas nicolas-grekas force-pushed the fixes/route-trailing-slash branch from 52ef950 to 3b47441 Compare April 14, 2018 15:06
@nicolas-grekas
Copy link
Member

Thank you @mathieutu.

@nicolas-grekas nicolas-grekas merged commit 3b47441 into symfony:3.4 Apr 14, 2018
nicolas-grekas added a commit that referenced this pull request Apr 14, 2018
…namespace starts with a backslash in routing (mathieutu)

This PR was submitted for the 4.0 branch but it was squashed and merged into the 3.4 branch instead (closes #26773).

Discussion
----------

[HttpKernel] Make ServiceValueResolver work if controller namespace starts with a backslash in routing

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #26772
| License       | MIT

Hi folks,

This PR fixes #26772:
>Controller "App\Controllers\Foo" requires that you provide a value for the "$foo" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.

Thank you for your work!

PS: This is my first (of many planned!) PR to Symfony, so please let me know if I did something wrong.

Commits
-------

3b47441 [HttpKernel] Make ServiceValueResolver work if controller namespace starts with a backslash in routing
This was referenced Apr 30, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants