CodeSampleX

Пример

symfony/console 7.2.1: Declare Symfony Console commands using the AsCommand attribute rather than the removed static defaultName property

Проверенный пример — composer symfony/console 7.2.1: Declare Symfony Console commands using the AsCommand attribute rather than the removed static…

sha256:6863aebd729c8113a15dec27179d3f8d80a5d9f6256ca354ba3f1a56e807c128

Эта сеть предлагает одно: образец, который собирается. Она запустила его в песочнице и сохранила подписанную квитанцию. Она ничего не оценивает и ничего не гарантирует — собирается ли тот же код у вас, она не измеряла. Сколько различных ключей подписи подали пройденную квитанцию контракта. Один — только автор; больше одного — значит, кто-то ещё тоже собрал. Ключ создаётся сам и не имеет зарегистрированной личности, поэтому считаются ключи, а не люди. MIT-0

Свидетельства выполнения

Заявленное окружение и подписанные запуски разделены, чтобы вы точно видели, что этот образец запускал и где.

Основа свидетельства
Подписанный контракт пройден
Квитанции проверки
2
Ключи подписи, собравшие его
2
Заявленная среда php linux x64 php php composer

Среды запусков проверки

Окружение Контракт Этапы Запуск
php 8 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · composer@1
2026-08-16
php 8 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · composer@1
2026-08-18

Кейс

HOW
Цель
Declare Symfony Console commands using the AsCommand attribute rather than the removed static defaultName property
Пакеты
Символы
  • Symfony\Component\Console\Command\Command
  • Symfony\Component\Console\Attribute\AsCommand
  • Symfony\Component\Console\Application
  • Symfony\Component\Console\Input\InputInterface
Окружение
php
Создан
2026-08-16T14:01:15Z

Контракт

  1. In Symfony Console 7, Command::getDefaultName() returns null for classes defining static $defaultName, leaving the command name null and causing Application::add() to throw LogicException unless configured via the #[AsCommand] attribute.
  2. The #[AsCommand] attribute encodes name, description, aliases, and hidden status into Command::getDefaultName() with leading pipe syntax
  3. Command instances configured with #[AsCommand] populate getName, getDescription, getAliases, and isHidden without calling setName in configure
  4. Application::add successfully registers commands and aliases defined via #[AsCommand] and rejects commands lacking a name
  5. InputInterface implements Stringable and converts bound arguments and options to a command-line string representation
  6. Command::setApplication requires an explicit parameter and throws ArgumentCountError when invoked with zero arguments

Файлы

  • NOTES.md
  • composer.json
  • composer.lock
  • csx.json
  • test/contract.php

Скачать артефакт с исходным кодом (tar.gz)

Исходный код

NOTES.md
# Notes

## Search Results
`search_known_solution` returned existing samples `sha256:403a4477823e3c31ef880554c7e1d83e9ee2e5182b4363b8e73e751a7cae9b95` (reentrancy and nested doRun state across sub-commands), `sha256:daa9d0d39f0c0c2c98dd7a96aa84ddffba6366d269e338ad0e2fc99133d716ea` (ANSI decoration, interactivity, and verbosity environment variables), and `sha256:64ffbaaec11e20300f534cf6b8b955bf63ed41b80dd1cf1bcba3f0df84e46ff7` (input option modes, default validations, and command tester prefixes). This sample addresses the breaking change in Symfony Console 7: the complete removal of `Command::$defaultName` and `Command::$defaultDescription` in favor of the `#[AsCommand]` attribute and strict method signatures.

## What a Naive Model Would Write
A naive model trained on earlier Symfony versions declares console commands by setting `protected static $defaultName = 'app:custom-name';` and `protected static $defaultDescription = '...';` directly on the class, expecting `$application->add(new CustomCommand())` to auto-discover the command name.

## How the Wrong Version Fails
Fails loudly at runtime: in Symfony Console 7, `Command::getDefaultName()` returns `null` when only static properties are present. The command's name remains `null`, causing `$application->add()` to throw `Symfony\Component\Console\Exception\LogicException: The command defined in "..." cannot have an empty name.`
composer.json
{
    "name": "csx/symfony-console-v7-as-command",
    "description": "Demonstrates Symfony Console 7 command declaration via AsCommand attribute and removal of static defaultName",
    "license": "MIT-0",
    "type": "project",
    "require": {
        "symfony/console": "7.2.1"
    }
}
composer.lock
{
    "_readme": [
        "This file locks the dependencies of your project to a known state",
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
        "This file is @generated automatically"
    ],
    "content-hash": "bf8c302b565484e9dd6f3f5138918b4a",
    "packages": [
        {
            "name": "psr/container",
            "version": "2.0.2",
            "source": {
                "type": "git",
                "url": "https://github.com/php-fig/container.git",
                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
                "shasum": ""
            },
            "require": {
                "php": ">=7.4.0"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "2.0.x-dev"
                }
            },
            "autoload": {
                "psr-4": {
                    "Psr\\Container\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage": "https://www.php-fig.org/"
                }
            ],
            "description": "Common Container Interface (PHP FIG PSR-11)",
            "homepage": "https://github.com/php-fig/container",
            "keywords": [
                "PSR-11",
                "container",
                "container-interface",
                "container-interop",
                "psr"
            ],
            "support": {
                "issues": "https://github.com/php-fig/container/issues",
                "source": "https://github.com/php-fig/container/tree/2.0.2"
            },
            "time": "2021-11-05T16:47:00+00:00"
        },
        {
            "name": "symfony/console",
            "version": "v7.2.1",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/console.git",
                "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3",
                "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3",
                "shasum": ""
            },
            "require": {
                "php": ">=8.2",
                "symfony/polyfill-mbstring": "~1.0",
                "symfony/service-contracts": "^2.5|^3",
                "symfony/string": "^6.4|^7.0"
            },
            "conflict": {
                "symfony/dependency-injection": "<6.4",
                "symfony/dotenv": "<6.4",
                "symfony/event-dispatcher": "<6.4",
                "symfony/lock": "<6.4",
                "symfony/process": "<6.4"
            },
            "provide": {
                "psr/log-implementation": "1.0|2.0|3.0"
            },
            "require-dev": {
                "psr/log": "^1|^2|^3",
                "symfony/config": "^6.4|^7.0",
                "symfony/dependency-injection": "^6.4|^7.0",
                "symfony/event-dispatcher": "^6.4|^7.0",
                "symfony/http-foundation": "^6.4|^7.0",
                "symfony/http-kernel": "^6.4|^7.0",
                "symfony/lock": "^6.4|^7.0",
                "symfony/messenger": "^6.4|^7.0",
                "symfony/process": "^6.4|^7.0",
                "symfony/stopwatch": "^6.4|^7.0",
                "symfony/var-dumper": "^6.4|^7.0"
            },
            "type": "library",
            "autoload": {
                "psr-4": {
                    "Symfony\\Component\\Console\\": ""
                },
                "exclude-from-classmap": [
                    "/Tests/"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Fabien Potencier",
                    "email": "fabien@symfony.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Eases the creation of beautiful and testable command line interfaces",
            "homepage": "https://symfony.com",
            "keywords": [
                "cli",
                "command-line",
                "console",
                "terminal"
            ],
            "support": {
                "source": "https://github.com/symfony/console/tree/v7.2.1"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "time": "2024-12-11T03:49:26+00:00"
        },
        {
            "name": "symfony/deprecation-contracts",
            "version": "v3.7.1",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/deprecation-contracts.git",
                "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d",
                "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d",
                "shasum": ""
            },
            "require": {
                "php": ">=8.1"
            },
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/contracts",
                    "name": "symfony/contracts"
                },
                "branch-alias": {
                    "dev-main": "3.7-dev"
                }
            },
            "autoload": {
                "files": [
                    "function.php"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "A generic function and convention to trigger deprecation notices",
            "homepage": "https://symfony.com",
            "support": {
                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "time": "2026-06-05T06:23:12+00:00"
        },
        {
            "name": "symfony/polyfill-ctype",
            "version": "v1.37.0",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-ctype.git",
                "reference": "141046a8f9477948ff284fa65be2095baafb94f2"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2",
                "reference": "141046a8f9477948ff284fa65be2095baafb94f2",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2"
            },
            "provide": {
                "ext-ctype": "*"
            },
            "suggest": {
                "ext-ctype": "For best performance"
            },
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Ctype\\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Gert de Pagter",
                    "email": "BackEndTea@gmail.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill for ctype functions",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "ctype",
                "polyfill",
                "portable"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "time": "2026-04-10T16:19:22+00:00"
        },
        {
            "name": "symfony/polyfill-intl-grapheme",
            "version": "v1.41.0",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
                "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
                "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2"
            },
            "suggest": {
                "ext-intl": "For best performance"
            },
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill for intl's grapheme_* functions",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "grapheme",
                "intl",
                "polyfill",
                "portable",
                "shim"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "time": "2026-07-28T08:25:59+00:00"
        },
        {
            "name": "symfony/polyfill-intl-normalizer",
            "version": "v1.38.0",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
                "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b",
                "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2"
            },
            "suggest": {
                "ext-intl": "For best performance"
            },
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
                },
                "classmap": [
                    "Resources/stubs"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill for intl's Normalizer class and related functions",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "intl",
                "normalizer",
                "polyfill",
                "portable",
                "shim"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "time": "2026-05-25T13:48:31+00:00"
        },
        {
            "name": "symfony/polyfill-mbstring",
            "version": "v1.38.2",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-mbstring.git",
                "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
                "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
                "shasum": ""
            },
            "require": {
                "ext-iconv": "*",
                "php": ">=7.2"
            },
            "provide": {
                "ext-mbstring": "*"
            },
            "suggest": {
                "ext-mbstring": "For best performance"
            },
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Mbstring\\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill for the Mbstring extension",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "mbstring",
                "polyfill",
                "portable",
                "shim"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "time": "2026-05-27T06:59:30+00:00"
        },
        {
            "name": "symfony/service-contracts",
            "version": "v3.7.1",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/service-contracts.git",
                "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0",
                "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0",
                "shasum": ""
            },
            "require": {
                "php": ">=8.1",
                "psr/container": "^1.1|^2.0",
                "symfony/deprecation-contracts": "^2.5|^3"
            },
            "conflict": {
                "ext-psr": "<1.1|>=2"
            },
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/contracts",
                    "name": "symfony/contracts"
                },
                "branch-alias": {
                    "dev-main": "3.7-dev"
                }
            },
            "autoload": {
                "psr-4": {
                    "Symfony\\Contracts\\Service\\": ""
                },
                "exclude-from-classmap": [
                    "/Test/"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Generic abstractions related to writing services",
            "homepage": "https://symfony.com",
            "keywords": [
                "abstractions",
                "contracts",
                "decoupling",
                "interfaces",
                "interoperability",
                "standards"
            ],
            "support": {
                "source": "https://github.com/symfony/service-contracts/tree/v3.7.1"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "time": "2026-06-16T09:55:08+00:00"
        },
        {
            "name": "symfony/string",
            "version": "v7.4.15",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/string.git",
                "reference": "e394af32256bf9e7bf80849d95e589167c10097b"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b",
                "reference": "e394af32256bf9e7bf80849d95e589167c10097b",
                "shasum": ""
            },
            "require": {
                "php": ">=8.2",
                "symfony/deprecation-contracts": "^2.5|^3.0",
                "symfony/polyfill-ctype": "~1.8",
                "symfony/polyfill-intl-grapheme": "~1.33",
                "symfony/polyfill-intl-normalizer": "~1.0",
                "symfony/polyfill-mbstring": "~1.0"
            },
            "conflict": {
                "symfony/translation-contracts": "<2.5"
            },
            "require-dev": {
                "symfony/emoji": "^7.1|^8.0",
                "symfony/http-client": "^6.4|^7.0|^8.0",
                "symfony/intl": "^6.4|^7.0|^8.0",
                "symfony/translation-contracts": "^2.5|^3.0",
                "symfony/var-exporter": "^6.4|^7.0|^8.0"
            },
            "type": "library",
            "autoload": {
                "files": [
                    "Resources/functions.php"
                ],
                "psr-4": {
                    "Symfony\\Component\\String\\": ""
                },
                "exclude-from-classmap": [
                    "/Tests/"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
            "homepage": "https://symfony.com",
            "keywords": [
                "grapheme",
                "i18n",
                "string",
                "unicode",
                "utf-8",
                "utf8"
            ],
            "support": {
                "source": "https://github.com/symfony/string/tree/v7.4.15"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "time": "2026-07-28T07:33:02+00:00"
        }
    ],
    "packages-dev": [],
    "aliases": [],
    "minimum-stability": "stable",
    "stability-flags": {},
    "prefer-stable": false,
    "prefer-lowest": false,
    "platform": {},
    "platform-dev": {},
    "plugin-api-version": "2.9.0"
}
csx.json
{"case":{"believed":"Declaring a protected static $defaultName property on a Command subclass sets the command name and description and allows registering it with Application::add().","caseId":"case:sha256:23e976a416c4662d857365341c9506c124824bd860e39d56cb68aa3d3f3dc97c","contract":["In Symfony Console 7, Command::getDefaultName() returns null for classes defining static $defaultName, leaving the command name null and causing Application::add() to throw LogicException unless configured via the #[AsCommand] attribute.","The #[AsCommand] attribute encodes name, description, aliases, and hidden status into Command::getDefaultName() with leading pipe syntax","Command instances configured with #[AsCommand] populate getName, getDescription, getAliases, and isHidden without calling setName in configure","Application::add successfully registers commands and aliases defined via #[AsCommand] and rejects commands lacking a name","InputInterface implements Stringable and converts bound arguments and options to a command-line string representation","Command::setApplication requires an explicit parameter and throws ArgumentCountError when invoked with zero arguments"],"goal":"Declare Symfony Console commands using the AsCommand attribute rather than the removed static defaultName property","kind":"HOW","packages":["pkg:composer/symfony/console@7.2.1"],"schemaVersion":1,"symbols":["Symfony\\Component\\Console\\Command\\Command","Symfony\\Component\\Console\\Attribute\\AsCommand","Symfony\\Component\\Console\\Application","Symfony\\Component\\Console\\Input\\InputInterface"]},"contractCommand":["php","test/contract.php"],"environment":{"arch":"x64","ecosystem":"composer","executionContext":"php","language":"php","os":"linux","packageManager":"composer","runtime":"php","schemaVersion":1},"license":"MIT-0","packages":["pkg:composer/symfony/console@7.2.1"],"schemaVersion":1,"symbols":["Symfony\\Component\\Console\\Command\\Command","Symfony\\Component\\Console\\Attribute\\AsCommand","Symfony\\Component\\Console\\Application","Symfony\\Component\\Console\\Input\\InputInterface"],"verifierAdapter":"composer@1"}
test/contract.php
<?php

declare(strict_types=1);

require __DIR__ . '/../vendor/autoload.php';

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;

// =============================================================================
// Contract 1: In Symfony Console 7, Command::getDefaultName() returns null for
// classes defining static $defaultName, leaving the command name null and
// causing Application::add() to throw LogicException unless configured via the
// #[AsCommand] attribute.
// =============================================================================

class LegacyStaticCommand extends Command
{
    // In Symfony <= 6, defining static properties was the standard mechanism.
    // In Symfony 7, Command::$defaultName and Command::$defaultDescription are removed.
    protected static $defaultName = 'legacy:export';
    protected static $defaultDescription = 'Legacy export description';
}

assert(
    LegacyStaticCommand::getDefaultName() === null,
    'Expected LegacyStaticCommand::getDefaultName() to return null in Symfony Console 7'
);

assert(
    LegacyStaticCommand::getDefaultDescription() === null,
    'Expected LegacyStaticCommand::getDefaultDescription() to return null in Symfony Console 7'
);

$legacyCommand = new LegacyStaticCommand();
assert(
    $legacyCommand->getName() === null,
    'Expected command instance name to be null when relying solely on static $defaultName'
);

assert(
    $legacyCommand->getDescription() === '',
    'Expected command description to be empty string when relying solely on static $defaultDescription'
);

$application = new Application();
$logicExceptionCaught = false;
try {
    $application->add($legacyCommand);
} catch (LogicException $e) {
    $logicExceptionCaught = true;
    assert(
        str_contains($e->getMessage(), 'cannot have an empty name'),
        'Expected LogicException message to indicate that the command cannot have an empty name'
    );
}
assert(
    $logicExceptionCaught === true,
    'Expected Application::add() to throw LogicException for legacy command without #[AsCommand]'
);

// =============================================================================
// Contract 2: The #[AsCommand] attribute encodes name, description, aliases,
// and hidden status into Command::getDefaultName() with leading pipe syntax.
// =============================================================================

#[AsCommand(
    name: 'app:sync',
    description: 'Synchronize database records',
    aliases: ['sync:all', 's:a'],
    hidden: true
)]
class ModernSyncCommand extends Command
{
}

assert(
    ModernSyncCommand::getDefaultName() === '|app:sync|sync:all|s:a',
    'Expected getDefaultName() to return pipe-concatenated name with leading pipe for hidden commands and aliases'
);

assert(
    ModernSyncCommand::getDefaultDescription() === 'Synchronize database records',
    'Expected getDefaultDescription() to return description from #[AsCommand]'
);

// =============================================================================
// Contract 3: Command instances configured with #[AsCommand] populate getName,
// getDescription, getAliases, and isHidden without calling setName in configure.
// =============================================================================

$modernCommand = new ModernSyncCommand();
assert(
    $modernCommand->getName() === 'app:sync',
    'Expected getName() to return primary command name extracted from #[AsCommand]'
);

assert(
    $modernCommand->getDescription() === 'Synchronize database records',
    'Expected getDescription() to match #[AsCommand] description'
);

assert(
    $modernCommand->getAliases() === ['sync:all', 's:a'],
    'Expected getAliases() to contain aliases from #[AsCommand]'
);

assert(
    $modernCommand->isHidden() === true,
    'Expected isHidden() to return true when hidden: true is declared in #[AsCommand]'
);

// =============================================================================
// Contract 4: Application::add successfully registers commands and aliases
// defined via #[AsCommand] and rejects commands lacking a name.
// =============================================================================

$registered = $application->add($modernCommand);
assert(
    $registered === $modernCommand,
    'Expected Application::add() to return the registered Command instance'
);

assert(
    $application->has('app:sync') === true,
    'Expected Application to find command by primary name'
);

assert(
    $application->has('sync:all') === true,
    'Expected Application to find command by alias sync:all'
);

assert(
    $application->has('s:a') === true,
    'Expected Application to find command by alias s:a'
);

assert(
    $application->get('app:sync') === $modernCommand,
    'Expected Application::get(app:sync) to retrieve the registered command'
);

assert(
    $application->get('sync:all') === $modernCommand,
    'Expected Application::get(sync:all) to retrieve the registered command'
);

// =============================================================================
// Contract 5: InputInterface implements Stringable and converts bound arguments
// and options to a command-line string representation.
// =============================================================================

$definition = new InputDefinition([
    new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Output format', 'json'),
    new InputArgument('batch', InputArgument::REQUIRED, 'Batch count'),
]);

$input = new ArrayInput(['--format' => 'json', 'batch' => '100'], $definition);

assert(
    $input instanceof \Stringable,
    'Expected InputInterface implementation to implement Stringable in Symfony Console 7'
);

$renderedInput = (string) $input;
assert(
    $renderedInput === '--format=json 100',
    sprintf('Expected rendered string "--format=json 100", got "%s"', $renderedInput)
);

// =============================================================================
// Contract 6: Command::setApplication requires an explicit parameter and throws
// ArgumentCountError when invoked with zero arguments.
// =============================================================================

$reflection = new \ReflectionMethod(Command::class, 'setApplication');
assert(
    $reflection->getNumberOfRequiredParameters() === 1,
    'Expected Command::setApplication() to have exactly 1 required parameter in Symfony Console 7'
);

$argumentCountCaught = false;
try {
    $modernCommand->setApplication();
} catch (\ArgumentCountError $e) {
    $argumentCountCaught = true;
}
assert(
    $argumentCountCaught === true,
    'Expected setApplication() without arguments to throw ArgumentCountError'
);

echo "Contract test passed successfully.\n";

Исходный сидер

csx-seed