Repository: symfony/symfony
File: src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php (PHP)

Code before:
                }
            }
        }

        foreach ($commandServices as $id => $commands) {
            $definition = $container->getDefinition($id);
            $class = $container->getParameterBag()->resolveValue($definition->getClass());

            if (!$r = $container->getReflectionClass($class)) {
                throw new InvalidArgumentException(\sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
            }

            foreach ($commands as $tags) {
                $this->registerCommand($container, $r, $id, $class, $tags, $definition, $serviceIds, $lazyCommandMap, $lazyCommandRefs);
            }
        }

        $container
            ->register('console.command_loader', ContainerCommandLoader::class)
            ->setPublic(true)
            ->addTag('container.no_preload')
            ->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]);

        $container->setParameter('console.command.ids', $serviceIds);
    }

    private function registerCommand(ContainerBuilder $container, \ReflectionClass $reflection, string $id, string $class, array $tags, Definition $definition, array &$serviceIds, array &$lazyCommandMap, array &$lazyCommandRefs): void
    {
        if (!$reflection->isSubclassOf(Command::class)) {
            $method = $tags[0]['method'] ?? '__invoke';

            if (!$reflection->hasMethod($method)) {
                throw new InvalidArgumentException(\sprintf('The service "%s" tagged "%s" must either be a subclass of "%s" or have an "%s()" method.', $id, 'console.command', Command::class, $method));
            }

            $reflection = $reflection->getMethod($method);

            if (!$reflection->isPublic() || $reflection->isStatic()) {
                throw new InvalidArgumentException(\sprintf('The method "%s::%s()" must be public and non-static to be used as a console command.', $class, $method));
            }

            if ('__invoke' === $method) {
                $callableRef = new Reference($id);
                $id .= '.command';
            } else {
                $callableRef = [new Reference($id), $method];
                $id .= '.'.$method.'.command';
            }
            $class = Command::class;

            $closureDefinition = new Definition(\Closure::class)

Review comment (bug):
reading `$tags[0]` is broken when processing the registration for other tags. It will not validate the right method (and won't register the right one either)

Code after:
        foreach ($commandServices as $id => $commands) {
            $definition = $container->getDefinition($id);
            $class = $container->getParameterBag()->resolveValue($definition->getClass());

            if (!$r = $container->getReflectionClass($class)) {
                throw new InvalidArgumentException(\sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
            }

            foreach ($commands as $tags) {
                $this->registerCommand($container, $r, $id, $class, $tags, $definition, $serviceIds, $lazyCommandMap, $lazyCommandRefs);
            }
        }

        $container
            ->register('console.command_loader', ContainerCommandLoader::class)
            ->setPublic(true)
            ->addTag('container.no_preload')
            ->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]);

        $container->setParameter('console.command.ids', $serviceIds);
    }

    private function registerCommand(ContainerBuilder $container, \ReflectionClass $reflection, string $id, string $class, array $tags, Definition $definition, array &$serviceIds, array &$lazyCommandMap, array &$lazyCommandRefs): void
    {
        if (!$reflection->isSubclassOf(Command::class)) {
            $method = $tags[0]['method'] ?? '__invoke';

            if (!$reflection->hasMethod($method)) {
                throw new InvalidArgumentException(\sprintf('The service "%s" tagged "%s" must either be a subclass of "%s" or have an "%s()" method.', $id, 'console.command', Command::class, $method));
            }

            $reflection = $reflection->getMethod($method);

            if (!$reflection->isPublic() || $reflection->isStatic()) {
                throw new InvalidArgumentException(\sprintf('The method "%s::%s()" must be public and non-static to be used as a console command.', $class, $method));
            }

            if ('__invoke' === $method) {
                $callableRef = new Reference($id);
                $id .= '.command';
            } else {
                $callableRef = [new Reference($id), $method];
                $id .= '.'.$method.'.command';
            }
            $class = Command::class;

            $closureDefinition = new Definition(\Closure::class)
                ->setFactory([\Closure::class, 'fromCallable'])
                ->setArguments([$callableRef]);

            $definition = $container->register($id, $class)