Skip to content

CLI: reject unknown tokens after a parent command with subcommands#19425

Merged
dereuromark merged 1 commit into
5.nextfrom
cli-strict-args-when-none-defined
May 12, 2026
Merged

CLI: reject unknown tokens after a parent command with subcommands#19425
dereuromark merged 1 commit into
5.nextfrom
cli-strict-args-when-none-defined

Conversation

@dereuromark
Copy link
Copy Markdown
Member

@dereuromark dereuromark commented May 4, 2026

Previously, running e.g. bin/cake i18n nonsense silently invoked I18nCommand and ignored the nonsense token, because:

  • i18n is a registered command (the interactive I18nCommand).
  • i18n defines no positional arguments, so ConsoleOptionParser::_parseArg() silently appends extras instead of rejecting them.

That hid genuine typos and made it look like CakePHP accepted any garbage after a known command name.

What this changes

In CommandRunner::run(), after the existing command-name resolution, when all of the following are true:

  • the matched command exists in the collection,
  • there is at least one more positional token,
  • that token is not option-like (does not start with -),
  • the matched command has sibling subcommands (i.e. there are other commands registered with <name> as a prefix),

then the next token is almost certainly meant to be a subcommand. If it doesn't match a registered subcommand, error out with a clear message listing the available subcommands.

$ bin/cake i18n nonsense
<error>Error: Unknown command `cake i18n nonsense`.
Available subcommands: `i18n extract`, `i18n init`.
Run `cake i18n --help` to see usage.</error>

What this does NOT change

  • Commands without sibling subcommands (the common case) — unchanged.
  • Commands that declare positional args via addArgument() — unchanged; the parser already validates them.
  • Commands like RoutesGenerateCommand that intentionally accept arbitrary positional args (bin/cake routes generate controller:Articles action:view 2) — unchanged, because routes generate has no sibling subcommands.
  • Option-like tokens (--help, -v, etc.) following the command name — unchanged.

Related

Alternative considered

Removing the silent-accept branch in ConsoleOptionParser::_parseArg() to make every parser strict by default. This was implemented and tested, but it breaks commands (including core's RoutesGenerateCommand) that intentionally accept arbitrary positional args without declaring them, and surfaces several test bugs around boolean options being passed values. That route would need a new opt-in API (e.g. a $parser->allowExtraArguments() flag) and a wider migration. Out of scope for this PR; happy to follow up if there's appetite.

@dereuromark dereuromark added this to the 5.4.0 milestone May 4, 2026
@dereuromark dereuromark marked this pull request as ready for review May 4, 2026 14:46
@ADmad ADmad requested a review from markstory May 4, 2026 19:19
Previously, running e.g. `bin/cake i18n nonsense` silently invoked
I18nCommand because `i18n` is a registered command, and the leftover
token `nonsense` was passed through as a positional argument that the
parser silently swallowed. That hid genuine typos.

When the matched command also has sibling subcommands (e.g. `i18n init`
and `i18n extract` exist alongside `i18n`), an unrecognized next token
is almost certainly a typo for a subcommand. Reject those cases up front
in CommandRunner with a message that lists the available subcommands.

Commands without siblings, commands with declared positional arguments,
and option-like tokens (`--help`, `-v`) are unaffected.
@dereuromark dereuromark force-pushed the cli-strict-args-when-none-defined branch from 925cc90 to b837960 Compare May 12, 2026 11:02
@dereuromark dereuromark merged commit 8d45b48 into 5.next May 12, 2026
14 of 15 checks passed
@dereuromark dereuromark deleted the cli-strict-args-when-none-defined branch May 12, 2026 12:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants