-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Description
Symfony version(s) affected
6.4.x
Description
Hi 👋
we recently had a problem in our code that a Command wasn't registered and we spend way too long to figure out why the command couldn't be executed. In the end this was caused by the constructor in that class being private.
#[AsCommand(
name: 'our:cool:command',
description: 'Lorem ipsum'
)]
class OurCoolCommand extends Command
{
private function __construct(
private readonly SomeDependency $dependency
){
parent::__construct();
}
....
Aside from the simple fix, I was wondering why linting the container via the lint:container
command did not warn us about that, which would have possibly saved us quite some headaches and time. After some investigation I traced this back to the following logic in FileLoader:
symfony/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
Lines 303 to 305 in 0549c6f
if ($r->isInstantiable() || $r->isInterface()) { | |
$classes[$class] = null; | |
} |
ReflectionClass::isInstantiable
returns false
for classes with a private constructor, so the loader never loads these classes and as such doesn't register them e.g. as a command in this case.
How to reproduce
Have a private constructor in e.g. a Symfony Command
and run lint:container
in the commandline. Notice how the linting successfully passes without warning you.
Possible Solution
My naive approach for testing reasons was the following:
...
if ($r->isInstantiable() || $r->isInterface() || $this->hasPrivateConstructor($r)) {
$classes[$class] = null;
}
...
private function hasPrivateConstructor(\ReflectionClass $r): bool
{
$constructor = $r->getConstructor();
return $constructor !== null && $constructor->isPrivate();
}
With the additional check for a private constructor the class makes it further through the flow and when running lint:container
I get the expected and much more helpful message
# php bin/console lint:container
In DefinitionErrorExceptionPass.php line 51:
Invalid service "Our\Namespace\For\OurCoolCommand": its constructor must be public.
Would this be something you might be interested to see as a contribution?
Additional Context
No response