Command
extends Filter
in package
Slash-command matcher. Port of `aiogram.filters.command.Command` (`aiogram/filters/command.py:25-198`). Accepts a message and matches its `text` (or fallback `caption`) against one or more registered command patterns. On match, returns `['command' => CommandObject]` so the parsed pieces flow into the handler as a `$command` kwarg.
Constructor shape
Upstream's signature is Command(*values, *, prefix, ignore_case, ignore_mention, magic). PHP forbids parameters after a variadic, so the
port collapses the variadic into a string|list<string> first argument
and exposes Command::of(...$cmds) as a variadic-friendly factory.
new Command(['start', 'help']) // array form new Command('start') // single string new Command('start', ignoreCase: true) // single + flags Command::of('start', 'help') // variadic shorthand new Command(['start'], prefix: '/!') // string form: upstream parity ('/!' -> ['/', '!']) new Command(['start'], prefix: ['/', '!']) // list form: same result new Command(['start'], prefix: ['!cmd']) // list-only: multi-char prefix (PHP extension)
Match algorithm (mirrors upstream parse_command)
- Reject early when the event is not a
Messageor when bothtextandcaptionare absent / empty. - Find the longest prefix from
$prefixthat the text starts with; bail if none match. - Split the post-prefix tail on the first whitespace run — left side
is the candidate command, right side is the args (or
null). - If the candidate contains
@, split on the FIRST@only; the right side becomes the mention (matching upstream'spartition('@')). Mirrors upstream issue aiogram/aiogram#1013: a missing mention surfaces asnull, never as''. - If
ignoreMentionis false AND a bot is supplied AND a mention is present, compare againstbot.me().usernamecase-insensitively. Mismatch → reject. Missing bot (e.g. unit tests that don't wire one) → skip the mention check (parity with upstream where the dispatcher always supplies one). Failures insideme()(network, mocked fixtures without canned response) are absorbed so tests don't have to seedgetMeresponses for every command-filter scenario. - Walk the registered commands in declaration order, comparing with
strcasecmpor===depending on$ignoreCase. The first match builds theCommandObjectand short-circuits.
Phase 4.7 scope
Strings only — regex / BotCommand / magic-filter post-validation /
deep-link handling all land in later tasks. The class is structured so
those surfaces can be added without churning the constructor or call
shape.
Table of Contents
Properties
- $commands : array<int, string>
- $ignoreCase : bool
- $ignoreMention : bool
- $prefix : array<int, string>
Methods
- __construct() : mixed
- __invoke() : array<string, mixed>|false
- Filter entry point. Returns either `false` (reject) or `['command' => CommandObject]` (accept with a single kwarg). See class docblock for the per-step algorithm.
- all() : AndFilter
- Compose an AND across filters: every child must accept, kwargs cascade. PHP equivalent of Python's `f1 & f2`.
- any() : OrFilter
- Compose an OR across filters: the first accepting child wins, no cascade. PHP equivalent of Python's `f1 | f2`.
- invertOf() : InvertFilter
- Invert a filter's accept/reject decision. Named `invertOf` rather than `not` because PHP forbids a static and an instance method sharing one name in a single class (the instance-side `$f->not()` convenience may land in a later task).
- of() : self
- Variadic-friendly factory mirroring upstream's `Command('start', 'help')` call shape. Equivalent to `new Command([$cmd1, $cmd2, ...])`.
- commandMatches() : bool
- Compare a parsed command name against a registered pattern. Uses `strcasecmp` when `$ignoreCase` is set (functionally equivalent to upstream's casefolding for ASCII command strings, which is what the upstream parametrize block exercises). Strict `===` otherwise.
- parseCommand() : CommandObject|null
- Parse `$text` into a `CommandObject` if it matches one of `$this->commands`.
Properties
$commands read-only
public
array<int, string>
$commands
Registered command patterns.
$ignoreCase read-only
public
bool
$ignoreCase
= false
$ignoreMention read-only
public
bool
$ignoreMention
= false
$prefix read-only
public
array<int, string>
$prefix
Acceptable command prefixes (default ['/']).
Methods
__construct()
public
__construct(array<int, string>|string $commands[, array<int, string>|string $prefix = ['/'] ][, bool $ignoreCase = false ][, bool $ignoreMention = false ]) : mixed
Parameters
- $commands : array<int, string>|string
-
Either a single command string or a list. Throws on empty list.
- $prefix : array<int, string>|string = ['/']
-
Acceptable prefixes (default
['/']). Accepts either alist<string>of prefix strings (PHP-side extension; allows multi-character prefixes like['!cmd']) or a plain string whose individual characters are each treated as a prefix — matching upstream'sCommand(prefix='/!')syntax where'/!'means "either/or!". First match wins during parsing. - $ignoreCase : bool = false
- $ignoreMention : bool = false
__invoke()
Filter entry point. Returns either `false` (reject) or `['command' => CommandObject]` (accept with a single kwarg). See class docblock for the per-step algorithm.
public
__invoke(object $event, mixed ...$kwargs) : array<string, mixed>|false
Parameters
- $event : object
- $kwargs : mixed
Return values
array<string, mixed>|falseall()
Compose an AND across filters: every child must accept, kwargs cascade. PHP equivalent of Python's `f1 & f2`.
public
static all(Filter ...$filters) : AndFilter
Parameters
- $filters : Filter
Return values
AndFilterany()
Compose an OR across filters: the first accepting child wins, no cascade. PHP equivalent of Python's `f1 | f2`.
public
static any(Filter ...$filters) : OrFilter
Parameters
- $filters : Filter
Return values
OrFilterinvertOf()
Invert a filter's accept/reject decision. Named `invertOf` rather than `not` because PHP forbids a static and an instance method sharing one name in a single class (the instance-side `$f->not()` convenience may land in a later task).
public
static invertOf(Filter $filter) : InvertFilter
Parameters
- $filter : Filter
Return values
InvertFilterof()
Variadic-friendly factory mirroring upstream's `Command('start', 'help')` call shape. Equivalent to `new Command([$cmd1, $cmd2, ...])`.
public
static of(string ...$commands) : self
Parameters
- $commands : string
Return values
selfcommandMatches()
Compare a parsed command name against a registered pattern. Uses `strcasecmp` when `$ignoreCase` is set (functionally equivalent to upstream's casefolding for ASCII command strings, which is what the upstream parametrize block exercises). Strict `===` otherwise.
private
commandMatches(string $candidate, string $registered) : bool
Parameters
- $candidate : string
- $registered : string
Return values
boolparseCommand()
Parse `$text` into a `CommandObject` if it matches one of `$this->commands`.
private
parseCommand(string $text, Bot|null $bot) : CommandObject|null
Returns null on any mismatch (unknown prefix, wrong command name,
mention mismatch).
Kept private — external callers should go through __invoke. The
upstream method is public, but it is also async and tightly coupled
to the filter's own validation methods (validate_prefix,
validate_mention, validate_command). Inlining those into one
routine here matches the PHP idiom and removes a layer of
indirection.
Parameters
- $text : string
- $bot : Bot|null