Skip to content

Command::setDescription called with null when using a Symfony command and symfony/runtime is installed #12605

@jtattevin

Description

@jtattevin

When using composer 2.9.0 + symfony/runtime:7.3 + a Symfony command in a script, the following error is thrown :

PHP Fatal error:  Uncaught TypeError: Symfony\Component\Console\Command\Command::setDescription(): Argument #1 ($description) must be of type string, null given, called in phar:///usr/bin/composer/src/Composer/Console/Application.php on line 396 and defined in phar:///usr/bin/composer/vendor/symfony/console/Command/Command.php:541

If symfony/runtime is enabled, a second loader is triggered that load the class and change the condition here from false to true :

if (is_string($dummy) && class_exists($dummy) && is_subclass_of($dummy, SymfonyCommand::class)) {

When the plugin is not enabled, only one loader is triggered which doesn't seem to find the class.

If the command doesn't have a description set, $description stay null and the error happen.
Implementing the method configure and calling setDescription avoid the issue.
Adding the scripts-descriptions entry for the command also avoid the issue.

From my test, this can be solved by defaulting $description to 'Runs the '.$script.' script as defined in composer.json' instead of null or not calling setDescription if null.

I'm ok with creating the PR with the prefered solution if needed.


MyCommand.php:

<?php

namespace MyVendor;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class MyCommand extends Command
{
    public function execute(InputInterface $input, OutputInterface $output): int
    {
        return 0;
    }
}

My composer.json:

{
  "name": "project/composertest",
  "version": "1.0.0",
  "autoload": {
    "psr-4": {
      "MyVendor\\": "src/MyVendor"
    }
  },
  "require": {
    "symfony/runtime": "^7.3"
  },
  "scripts": {
    "hello": "MyVendor\\MyCommand"
  },
  "config": {
    "allow-plugins": {
      "symfony/runtime": true
    }
  }
}

Output of composer diagnose:

Checking composer.json: WARNING
No license specified, it is recommended to do so. For closed-source software you may use "proprietary" as license.
The version field is present, it is recommended to leave it out if the package is published on Packagist.
Checking composer.lock: OK
Checking platform settings: OK
Checking git settings: OK git version 2.48.1
Checking http connectivity to packagist: OK
Checking https connectivity to packagist: OK
Checking github.com oauth access: OK does not expire
Checking disk free space: OK
Checking Composer and its dependencies for vulnerabilities: OK
Composer version: 2.9.999-dev+source
PHP version: 8.4.11
PHP binary path: /usr/bin/php8.4
OpenSSL version: OpenSSL 3.0.13 30 Jan 2024
curl version: 8.12.1 libz 1.3.1 brotli brotli/1.1.0 zstd supported ssl OpenSSL/3.4.1 HTTP 1.0, 1.1, 2
zip: extension present, unzip present, 7-Zip not available

When I run this command:

composer -vvv list

I get the following output:

Reading ./composer.json (/tmp/composertest/composer.json)
Loading config file /home/julien/.config/composer/config.json
Loading config file /home/julien/.config/composer/auth.json
Loading config file ./composer.json (/tmp/composertest/composer.json)
Checked CA file /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem does not exist or it is not a file.
Checked directory /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem does not exist or it is not a directory.
Checked CA file /etc/pki/tls/certs/ca-bundle.crt does not exist or it is not a file.
Checked directory /etc/pki/tls/certs/ca-bundle.crt does not exist or it is not a directory.
Checked CA file /etc/ssl/certs/ca-certificates.crt: valid
Reading /home/julien/.config/composer/composer.json
Loading config file /home/julien/.config/composer/config.json
Loading config file /home/julien/.config/composer/auth.json
Loading config file /home/julien/.config/composer/composer.json (/home/julien/.config/composer/composer.json)
Loading config file /home/julien/.config/composer/auth.json
Reading /home/julien/.config/composer/auth.json
Reading /tmp/composertest/vendor/composer/installed.json
Reading /home/julien/.config/composer/vendor/composer/installed.json
Loading plugin Symfony\Component\Runtime\Internal\ComposerPlugin (from symfony/runtime)
Running 2.9.999-dev+source (@release_date@) with PHP 8.4.11 on Linux / 6.6.87.2-microsoft-standard-WSL2
PHP Fatal error:  Uncaught TypeError: Symfony\Component\Console\Command\Command::setDescription(): Argument #1 ($description) must be of type string, null given, called in /tmp/composer/src/Composer/Console/Application.php on line 396 and defined in /tmp/composer/vendor/symfony/console/Command/Command.php:541
Stack trace:
#0 /tmp/composer/src/Composer/Console/Application.php(396): Symfony\Component\Console\Command\Command->setDescription()
#1 /tmp/composer/vendor/symfony/console/Application.php(171): Composer\Console\Application->doRun()
#2 /tmp/composer/src/Composer/Console/Application.php(138): Symfony\Component\Console\Application->run()
#3 /tmp/composer/bin/composer(113): Composer\Console\Application->run()
#4 {main}
  thrown in /tmp/composer/vendor/symfony/console/Command/Command.php on line 541

And I expected this to happen:

   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 2.9.999-dev+source @release_date@

Usage:
  command [options] [arguments]

Options:
  -h, --help                     Display help for the given command. When no command is given display help for the list command
  -q, --quiet                    Do not output any message
  -V, --version                  Display this application version
      --ansi|--no-ansi           Force (or disable --no-ansi) ANSI output
  -n, --no-interaction           Do not ask any interactive question
      --profile                  Display timing and memory usage information
      --no-plugins               Whether to disable plugins.
      --no-scripts               Skips the execution of all scripts defined in composer.json file.
  -d, --working-dir=WORKING-DIR  If specified, use the given directory as working directory.
      --no-cache                 Prevent use of the cache
  -v|vv|vvv, --verbose           Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  about                Shows a short information about Composer
[...]
  hello                Runs the hello script as defined in composer.json

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions