Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[HttpKernel] Bindings scalar named arguments to action method does not work #24555

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

Closed
yceruto opened this issue Oct 13, 2017 · 9 comments
Closed

Comments

@yceruto
Copy link
Member

yceruto commented Oct 13, 2017

Q A
Bug report? yes
Feature request? no
BC Break report? no
RFC? no
Symfony version 3.4

I have this controller/action with a scalar argument $env:

public function postShowAction(Post $post, string $env): Response
{
    // do something with $env
}

My configuration to bind this argument:

services:
    _defaults:
        # ...
        bind:
            $env: '%kernel.environment%'

    App\Controller\:
        resource: '../src/Controller'
        tags: ['controller.service_arguments']

> bin/console cache:clear:

Type error: Argument 1 passed to Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument::__construct() must be an instance of Symfony\Component\DependencyInjection\Reference, string given, called in /home/yceruto/github/symfony/symfony-demo/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php on line 81

Setting a plain string value ($args[$p->name] = $bindingValue; -> '%kernel.environment%')...

list($bindingValue, $bindingId) = $binding->getValues();
$binding->setValues(array($bindingValue, $bindingId, true));
$args[$p->name] = $bindingValue;

...that later will be passed to ServiceLocatorTagPass::register() which expects a Reference instance for each arg:

if ($args) {
$controllers[$id.':'.$r->name] = ServiceLocatorTagPass::register($container, $args);
}

I don't have idea how it should be solved, so repo/branch to reproduce the bug:

Note: This work fine with constructor arguments (as __constructor method is not included in the previous "pass" but ResolveBindingsPass).

ping @nicolas-grekas, @GuilhemN

@weaverryan
Copy link
Member

Hmmm, I know you can specify scalar args if you specify them on the controller.service_arguments tag... so the controller args system does support scalar args. I’m not in a spot to look at the code deeply, but there should be a simple fix: make bind do the same thing that happens with the processing of the args on the tag. Maybe that help... but me (later) or Nicolas may need to check this...

@nicolas-grekas
Copy link
Member

That's by design: the argument resolver is based on a service locator, and service locators are not compatible with scalar args. Changing this would be considered as a new feature, and would need some discussion because we consciously did not allow so.
The workaround is trivial: just inject the scalar on the constructor. In fact, technically speaking, there is no benefit to injecting scalars to actions, because there is no laziness required here.

@yceruto
Copy link
Member Author

yceruto commented Oct 15, 2017

That's by design: the argument resolver is based on a service locator, and service locators are not compatible with scalar args.

Then we need an appropriate exception message? otherwise it means to me as an incomplete feature.

The workaround is trivial: just inject the scalar on the constructor. In fact, technically speaking, there is no benefit to injecting scalars to actions, because there is no laziness required here.

Agree, that's what I did, but for DX it should be better IMHO, just:

-public function postShowAction(Post $post): Response
+public function postShowAction(Post $post, string $env): Response
{
    // do something with $env
}

vs

+private $env;

+public function __constructor(string $env)
+{
+    $this->env = $env;
+}

public function postShowAction(Post $post): Response
{
    // do something with $this->env
}

mainly if it's used in a single method (which is the case here).

@weaverryan
Copy link
Member

Ok, we definitely need a clear error :)

@nicolas-grekas
Copy link
Member

See #24582

@weaverryan
Copy link
Member

Actually... this is trickier. We can’t throw an exception, because binding a scalar value is legal in general, and having an argument with the same name as a bound value is also legal. All we can do (I think) is not pass the value to that arg. But, that will be quite unexpected: binding works for the constructor, method calls and most controller args (services, but not scalar). This looks like a wtf to me, and a tough error to spot (why is this arg not being bound). It’s unexpected... and even if it was intentional, it smells like a “bug” (that’s what users will think)

@yceruto
Copy link
Member Author

yceruto commented Oct 17, 2017

Absolutely agree with @weaverryan.

All we can do (I think) is not pass the value to that arg.

👍

@nicolas-grekas
Copy link
Member

👍 also

@yceruto
Copy link
Member Author

yceruto commented Oct 18, 2017

See #24600

symfony-splitter pushed a commit to symfony/http-kernel that referenced this issue Oct 18, 2017
… arguments (yceruto)

This PR was merged into the 3.4 branch.

Discussion
----------

[HttpKernel] Don't bind scalar values to controller method arguments

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | symfony/symfony#24555 (comment)
| License       | MIT
| Doc PR        | -

See linked issue.

Let's suppose we have this configuration:
```yaml
services:
    _defaults:
        # ...
        bind:
            $foo: '%foobar%'
```
`$foo` was successfully bound to any controller constructor, but in another controller I have this edit action (nothing to do with the intention of bind such a parameter, but it has the same name):
```php
/**
 * @route("/{foo}/edit")
 */
public function editAction(string $foo) {}
```
triggering:
> Type error: Argument 1 passed to Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument::__construct() must be an instance of Symfony\Component\DependencyInjection\Reference, string given, called in /home/yceruto/github/symfony/symfony-demo/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php on line 81

or after symfony/symfony#24582:
> Invalid service locator definition: only services can be referenced, "string" found for key "foo". Inject parameter values using constructors instead.

Commits
-------

a1df9af don't bind scalar values to controller method arguments
fabpot added a commit that referenced this issue Oct 18, 2017
… arguments (yceruto)

This PR was merged into the 3.4 branch.

Discussion
----------

[HttpKernel] Don't bind scalar values to controller method arguments

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #24555 (comment)
| License       | MIT
| Doc PR        | -

See linked issue.

Let's suppose we have this configuration:
```yaml
services:
    _defaults:
        # ...
        bind:
            $foo: '%foobar%'
```
`$foo` was successfully bound to any controller constructor, but in another controller I have this edit action (nothing to do with the intention of bind such a parameter, but it has the same name):
```php
/**
 * @route("/{foo}/edit")
 */
public function editAction(string $foo) {}
```
triggering:
> Type error: Argument 1 passed to Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument::__construct() must be an instance of Symfony\Component\DependencyInjection\Reference, string given, called in /home/yceruto/github/symfony/symfony-demo/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php on line 81

or after #24582:
> Invalid service locator definition: only services can be referenced, "string" found for key "foo". Inject parameter values using constructors instead.

Commits
-------

a1df9af don't bind scalar values to controller method arguments
nicolas-grekas added a commit that referenced this issue Oct 20, 2017
This PR was merged into the 3.3 branch.

Discussion
----------

[DI] Enhance service locator error message

| Q             | A
| ------------- | ---
| Branch?       | 3.3
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #24555
| License       | MIT
| Doc PR        | -

See linked issue.

Commits
-------

fdb0ea9 [DI] Enhance service locator error message
weaverryan added a commit to symfony/symfony-docs that referenced this issue Oct 29, 2017
…ers (weaverryan)

This PR was merged into the 3.3 branch.

Discussion
----------

Adding warning about non-service arguments for controllers

Hi guys!

See: symfony/symfony#24555 (comment)

This is for 3.3. In 3.4 we can improve this further: by updating the example of this to use `bind` (which is much nicer than the ugly tag) and probably show an example of using `bind` along with a scalar argument to the constructor. In other words, 3.4 is a todo once this is merged.

Thanks!

Commits
-------

72a0685 Adding warning about non-service arguments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants