Skip to content

Linting container does not warn about private constructors #59658

@dreis2211

Description

@dreis2211

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:

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions