8000 [DependencyInjection][FrameworkBundle] `lint:container` incorrectly marks service closures as not callable · Issue #40773 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[DependencyInjection][FrameworkBundle] lint:container incorrectly marks service closures as not callable #40773

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
derrabus opened this issue Apr 11, 2021 · 7 comments

Comments

@derrabus
Copy link
Member

Symfony version(s) affected: 5.2.6

Description
Under some circumstances, the lint:container command incorrectly fails on service closures being passed to a parameter declared as callable. This caused doctrine/DoctrineMigrationsBundle#413 and currently breaks the CI of the Symfony demo app.

How to reproduce
https://github.com/derrabus/di-sca-reproducer

Run the lint:container command and you'll get the following error.

[ERROR] Invalid definition for service "a": argument 2 of "App\A::setDependency()" accepts "callable", "App\B" passed.

However, run the testcommand and you'll see that the service definition is constructed successfully.

@nicolas-grekas
Copy link
Member

The reason for this issue is that in Yaml/XmlDumper, we have code like:

        if ($value instanceof ServiceClosureArgument) {
            $value = $value->getValues()[0];
        }

That is: we unwrap the service.

The deeper reason for this is that we don't have a way to dump service closures as yaml nor xml.

When using the command, in dev, the xml file is loaded and 💥
In prod env, this works as expected because the container is loaded from source, not from xml.

For thoughts :)

@alexandre-daubois
Copy link
Member
alexandre-daubois commented Apr 12, 2021

What I understood during my investigation is during values processing here...

$value->setValues($this->processValue($value->getValues()));

... the value is resolved, and it returns only the Reference, created during services register (Kernel.php in @derrabus example).

The reason tests are passing seems to be because the CheckTypeDeclarationPass is tested alone, not with other compilation passes. Adding this code in checkType of CheckTypeDeclarationPass solves the problem:

if ('callable' === $type && \is_object($value) && ($value instanceof Reference || $value instanceof Definition)) {
    return;
}

But the solution of @nicolas-grekas seems a better explanation!

@derrabus
Copy link
Member Author

Thanks for the analysis.

we don't have a way to dump service closures as yaml nor xml

This is something we could fix for 5.3. But that would basically mean that the linter remains broken in Symfony 4 perpetually. 😕

if ('callable' === $type && \is_object($value) && ($value instanceof Reference || $value instanceof Definition)) {
    return;
}

Yes, that would silence the error. Side note: Closure would also be a valid type declaration for a service closure.

But that change would basically mean that any reference or inlined definition would pass a callable type. We would fix a false positive at the cost of introducing false negatives. That would be unfortunate. 😕

@nicolas-grekas
Copy link
Member

We could skip using the xml in 5.2. That would make the command slower 🤷‍♂️

@stof
Copy link
Member
stof commented Apr 16, 2021

@alexandre-daubois the code you linked does not replace the ServiceClosureArgument. It process the value inside it, but keeps the argument itself.

@stof
Copy link
Member
stof commented Apr 16, 2021

@nicolas-grekas the other solution is to add a XML syntax to represent a ServiceClosureArgument, so that we can dump the compiled container properly

@derrabus
Copy link
Member Author

This is apparently a duplicate of #39259. Let's close this one then.

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
0