Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
100.00% |
157 / 157 |
|
97.52% |
157 / 161 |
|
36.26% |
66 / 182 |
|
80.00% |
16 / 20 |
CRAP | |
0.00% |
0 / 1 |
| Runner | |
100.00% |
157 / 157 |
|
97.52% |
157 / 161 |
|
36.26% |
66 / 182 |
|
100.00% |
20 / 20 |
1531.41 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
2 / 2 |
|
100.00% |
4 / 4 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| add | |
100.00% |
16 / 16 |
|
93.33% |
14 / 15 |
|
42.86% |
6 / 14 |
|
100.00% |
1 / 1 |
12.72 | |||
| showHelp | |
100.00% |
18 / 18 |
|
100.00% |
10 / 10 |
|
20.00% |
3 / 15 |
|
100.00% |
1 / 1 |
12.19 | |||
| showSection | |
100.00% |
3 / 3 |
|
100.00% |
4 / 4 |
|
66.67% |
2 / 3 |
|
100.00% |
1 / 1 |
2.15 | |||
| showCommands | |
100.00% |
3 / 3 |
|
100.00% |
4 / 4 |
|
33.33% |
1 / 3 |
|
100.00% |
1 / 1 |
3.19 | |||
| names | |
100.00% |
15 / 15 |
|
100.00% |
16 / 16 |
|
5.56% |
2 / 36 |
|
100.00% |
1 / 1 |
48.28 | |||
| run | |
100.00% |
18 / 18 |
|
100.00% |
19 / 19 |
|
50.00% |
12 / 24 |
|
100.00% |
1 / 1 |
19.12 | |||
| dispatch | |
100.00% |
13 / 13 |
|
100.00% |
22 / 22 |
|
23.81% |
10 / 42 |
|
100.00% |
1 / 1 |
54.23 | |||
| message | |
100.00% |
1 / 1 |
|
100.00% |
4 / 4 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| showTraceback | |
100.00% |
9 / 9 |
|
83.33% |
5 / 6 |
|
33.33% |
1 / 3 |
|
100.00% |
1 / 1 |
5.67 | |||
| runCommand | |
100.00% |
6 / 6 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| showCommandHelp | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| showGroup | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| showCommand | |
100.00% |
4 / 4 |
|
100.00% |
4 / 4 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| showAmbiguousMessage | |
100.00% |
6 / 6 |
|
100.00% |
4 / 4 |
|
33.33% |
1 / 3 |
|
100.00% |
1 / 1 |
3.19 | |||
| getCommand | |
100.00% |
11 / 11 |
|
91.67% |
11 / 12 |
|
50.00% |
5 / 10 |
|
100.00% |
1 / 1 |
8.12 | |||
| notFound | |
100.00% |
2 / 2 |
|
100.00% |
4 / 4 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| entries | |
100.00% |
13 / 13 |
|
100.00% |
14 / 14 |
|
55.56% |
5 / 9 |
|
100.00% |
1 / 1 |
11.30 | |||
| entry | |
100.00% |
11 / 11 |
|
100.00% |
8 / 8 |
|
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
4 | |||
| validClass | |
100.00% |
3 / 3 |
|
83.33% |
5 / 6 |
|
50.00% |
2 / 4 |
|
100.00% |
1 / 1 |
2.50 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Celema\Console; |
| 6 | |
| 7 | use Celema\Console\Exception\InvalidUsage; |
| 8 | use Closure; |
| 9 | use Throwable; |
| 10 | use ValueError; |
| 11 | |
| 12 | /** |
| 13 | * @api |
| 14 | */ |
| 15 | final class Runner |
| 16 | { |
| 17 | private const int AMBIGUOUS = 1; |
| 18 | |
| 19 | /** The built-in commands: the description and usage of their help screen. */ |
| 20 | private const array BUILTINS = [ |
| 21 | 'commands' => ['Lists all available commands', 'commands'], |
| 22 | 'help' => ['Displays the overview of all commands, or the help of a command', 'help [<command>]'], |
| 23 | ]; |
| 24 | |
| 25 | /** |
| 26 | * The commands indexed by prefix and name. |
| 27 | * |
| 28 | * @var array<string, array<string, Entry>> |
| 29 | */ |
| 30 | private array $toc = []; |
| 31 | |
| 32 | /** |
| 33 | * The commands indexed by name only. |
| 34 | * |
| 35 | * @var array<string, list<Entry>> |
| 36 | */ |
| 37 | private array $list = []; |
| 38 | |
| 39 | /** The widest listed name; the built-in `commands` sets the minimum. */ |
| 40 | private int $longestName = 8; |
| 41 | |
| 42 | /** @var null|Closure(class-string): object */ |
| 43 | private readonly ?Closure $resolve; |
| 44 | |
| 45 | /** @param null|callable(class-string): object $resolve */ |
| 46 | public function __construct( |
| 47 | array|object|string $commands = [], |
| 48 | private readonly Io $io = new Io(), |
| 49 | private bool $debug = false, |
| 50 | ?callable $resolve = null, |
| 51 | ) { |
| 52 | $this->resolve = $resolve === null ? null : Closure::fromCallable($resolve); |
| 53 | $this->add($commands); |
| 54 | } |
| 55 | |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 77 | } |
| 78 | |
| 79 | $this->toc = $toc; |
| 80 | $this->list = $list; |
| 81 | $this->longestName = $longestName; |
| 82 | |
| 83 | return $this; |
| 84 | } |
| 85 | |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 118 | |
| 119 | /** @param array<string, Entry> $entries Keyed by full name */ |
| 120 | private function showSection(array $entries): void |
| 121 | { |
| 122 | ksort($entries, SORT_STRING); |
| 123 | |
| 124 | foreach ($entries as $entry) { |
| 125 | $this->showCommand($entry->meta->prefix, $entry->meta->name, $entry->meta->description); |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | /** |
| 130 | * Displays a list of all available commands. |
| 131 | */ |
| 132 | private function showCommands(): int |
| 133 | { |
| 134 | foreach ($this->names() as $name) { |
| 135 | $this->io->line('%s', $name); |
| 136 | } |
| 137 | |
| 138 | return 0; |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | * The names that resolve to a command, sorted. |
| 143 | * |
| 144 | * With and without namespace/group. If a bare name is shared, e. g. |
| 145 | * by foo:cmd and bar:cmd, only the namespaced forms are included — |
| 146 | * unless the bare name belongs to an unprefixed command, which always |
| 147 | * resolves exactly. |
| 148 | * |
| 149 | * @return list<string> |
| 150 | */ |
| 151 | private function names(): array |
| 152 | { |
| 153 | /** @var array<array-key, int> $list */ |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * Runs the command named in the argument vector, `$_SERVER['argv']` by |
| 188 | * default. A given vector has the same shape: it starts with the script |
| 189 | * name, which help screens and usage hints display. |
| 190 | * |
| 191 | * @param null|list<string> $argv |
| 192 | */ |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 201 | $arg = $argv[2] ?? null; |
| 202 | } |
| 203 | |
| 204 | try { |
| 205 | return $arg === null |
| 206 | ? $this->showHelp($script) |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 208 | } catch (Throwable $e) { |
| 209 | // `$arg` names the effective target, e.g. `x` for `help x`. |
| 210 | $this->io->error( |
| 211 | "Error while running command '%s':\n\n%s", |
| 212 | $arg ?? '<no command given>', |
| 213 | $this->message($e), |
| 214 | ); |
| 215 | |
| 216 | // A usage error is the input's fault; its trace shows nothing. |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 218 | $this->showTraceback($e); |
| 219 | } |
| 220 | |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | /** |
| 226 | * Runs the command `$arg` names, or shows its help in a help call. |
| 227 | * |
| 228 | * @param list<string> $tokens |
| 229 | */ |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 237 | // Only `commands` gets here without a help call. |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 242 | } |
| 243 | |
| 244 | return $this->showCommands(); |
| 245 | } |
| 246 | |
| 247 | try { |
| 248 | $entry = $this->getCommand($cmd); |
| 249 | } catch (InvalidUsage $e) { |
| 250 | if ($e->getCode() === self::AMBIGUOUS) { |
| 251 | return $this->showAmbiguousMessage($cmd); |
| 252 | } |
| 253 | |
| 254 | throw $e; |
| 255 | } |
| 256 | |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 258 | } |
| 259 | |
| 260 | /** |
| 261 | * The class stands in for a missing message. |
| 262 | */ |
| 263 | private function message(Throwable $e): string |
| 264 | { |
| 265 | return $e->getMessage() === '' ? $e::class : $e->getMessage(); |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * Lists where the exception was thrown and its stack trace, then |
| 270 | * the same for each previous exception. |
| 271 | */ |
| 272 | private function showTraceback(Throwable $e): void |
| 273 | { |
| 274 | $io = $this->io->stderr(); |
| 275 | $io->line("\n<yellow>Traceback:</yellow>"); |
| 276 | |
| 277 | while (true) { |
| 278 | $io->line('%s in %s:%d', $e::class, $e->getFile(), $e->getLine()); |
| 279 | $io->line('%s', $e->getTraceAsString()); |
| 280 | $e = $e->getPrevious(); |
| 281 | |
| 282 | if ($e === null) { |
| 283 | return; |
| 284 | } |
| 285 | |
| 286 | $io->line("\n<yellow>Caused by:</yellow> %s", $this->message($e)); |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | /** @param list<string> $tokens */ |
| 291 | private function runCommand(Entry $entry, array $tokens, string $script): int |
| 292 | { |
| 293 | $signature = $entry->signature(); |
| 294 | |
| 295 | if ($signature->asksForHelp($tokens)) { |
| 296 | return $this->showCommandHelp($entry, $script); |
| 297 | } |
| 298 | |
| 299 | $values = $signature->bind($tokens, $this->io); |
| 300 | |
| 301 | // The signature checked that __invoke() exists; PHP requires it |
| 302 | // to be public. |
| 303 | /** @var callable-object $command */ |
| 304 | $command = $entry->command(); |
| 305 | |
| 306 | /** @var int Guaranteed by the declared return type under strict_types */ |
| 307 | return $command(...$signature->match($values, $command)); |
| 308 | } |
| 309 | |
| 310 | private function showCommandHelp(Entry $entry, string $script): int |
| 311 | { |
| 312 | new Help($this->io, $script)->showFor($entry->class); |
| 313 | |
| 314 | return 0; |
| 315 | } |
| 316 | |
| 317 | private function showGroup(string $title): void |
| 318 | { |
| 319 | $this->io->line("\n<yellow>%s</yellow>", $title); |
| 320 | } |
| 321 | |
| 322 | /** |
| 323 | * The description is markup; the names are data. |
| 324 | */ |
| 325 | private function showCommand(string $prefix, string $name, string $desc): void |
| 326 | { |
| 327 | $prefix = $prefix === '' ? '' : $prefix . ':'; |
| 328 | |
| 329 | // The longest name includes every listed one, so the gap is at |
| 330 | // least 2. |
| 331 | $pad = str_repeat(' ', $this->longestName + 2 - strlen($prefix . $name)); |
| 332 | $this->io->write(' %s<green>%s</green>%s', $prefix, $name, $pad); |
| 333 | $this->io->line($desc); |
| 334 | } |
| 335 | |
| 336 | private function showAmbiguousMessage(string $cmd): int |
| 337 | { |
| 338 | $this->io->error("Ambiguous command. Please add the group name:\n"); |
| 339 | $entries = $this->list[$cmd]; |
| 340 | usort($entries, static fn(Entry $a, Entry $b): int => strcmp($a->meta->full(), $b->meta->full())); |
| 341 | |
| 342 | foreach ($entries as $entry) { |
| 343 | $this->io->error(' <yellow>%s</yellow>:%s', $entry->meta->prefix, $entry->meta->name); |
| 344 | } |
| 345 | |
| 346 | return 2; |
| 347 | } |
| 348 | |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 356 | /** @var array{0: string, 1: string} $parts */ |
| 357 | $parts = explode(':', $cmd, limit: 2); |
| 358 | |
| 359 | return $this->toc[$parts[0]][$parts[1]] ?? throw $this->notFound($cmd); |
| 360 | } |
| 361 | |
| 362 | $entry = $this->toc[''][$cmd] ?? null; |
| 363 | |
| 364 | if ($entry !== null) { |
| 365 | return $entry; |
| 366 | } |
| 367 | |
| 368 | if (array_key_exists($cmd, $this->list)) { |
| 369 | if (count($this->list[$cmd]) === 1) { |
| 370 | return $this->list[$cmd][0]; |
| 371 | } |
| 372 | |
| 373 | throw new InvalidUsage('Ambiguous command', self::AMBIGUOUS); |
| 374 | } |
| 375 | |
| 376 | throw $this->notFound($cmd); |
| 377 | } |
| 378 | |
| 379 | private function notFound(string $cmd): InvalidUsage |
| 380 | { |
| 381 | $name = Suggestion::find($cmd, $this->names()); |
| 382 | |
| 383 | return new InvalidUsage($name === null ? 'Command not found' : "Command not found. Did you mean '{$name}'?"); |
| 384 | } |
| 385 | |
| 386 | /** @return list<Entry> */ |
| 387 | private function entries(array|object|string $commands): array |
| 388 | { |
| 389 | if (!is_array($commands)) { |
| 390 | return [$this->entry($commands)]; |
| 391 | } |
| 392 | |
| 393 | $entries = []; |
| 394 | |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 397 | if (!$item instanceof Closure) { |
| 398 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 399 | } |
| 400 | |
| 401 | $entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 402 | |
| 403 | continue; |
| 404 | } |
| 405 | |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 407 | throw new ValueError('Invalid command registration'); |
| 408 | } |
| 409 | |
| 410 | $entries[] = $this->entry($item); |
| 411 | } |
| 412 | |
| 413 | return $entries; |
| 414 | } |
| 415 | |
| 416 | private function entry(object|string $command): Entry |
| 417 | { |
| 418 | if ($command instanceof Closure) { |
| 419 | throw new ValueError( |
| 420 | 'Closure commands are not supported; use an anonymous class with a #[Command] attribute', |
| 421 | ); |
| 422 | } |
| 423 | |
| 424 | if (is_object($command)) { |
| 425 | return Entry::fromInstance($command); |
| 426 | } |
| 427 | |
| 428 | $class = $this->validClass($command); |
| 429 | $resolve = $this->resolve; |
| 430 | |
| 431 | return $resolve === null |
| 432 | ? Entry::fromClass($class) |
| 433 | : Entry::fromFactory($class, static fn(): mixed => $resolve($class)); |
| 434 | } |
| 435 | |
| 436 | /** @return class-string */ |
| 437 | private function validClass(string $class): string |
| 438 | { |
| 439 | if (!class_exists($class)) { |
| 440 | throw new ValueError("Unknown command class '{$class}'"); |
| 441 | } |
| 442 | |
| 443 | return $class; |
| 444 | } |
| 445 | } |
Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not
necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 47 | array|object|string $commands = [], |
| 48 | private readonly Io $io = new Io(), |
| 49 | private bool $debug = false, |
| 50 | ?callable $resolve = null, |
| 51 | ) { |
| 52 | $this->resolve = $resolve === null ? null : Closure::fromCallable($resolve); |
| 52 | $this->resolve = $resolve === null ? null : Closure::fromCallable($resolve); |
| 52 | $this->resolve = $resolve === null ? null : Closure::fromCallable($resolve); |
| 53 | $this->add($commands); |
| 54 | } |
| 47 | array|object|string $commands = [], |
| 48 | private readonly Io $io = new Io(), |
| 49 | private bool $debug = false, |
| 50 | ?callable $resolve = null, |
| 51 | ) { |
| 52 | $this->resolve = $resolve === null ? null : Closure::fromCallable($resolve); |
| 52 | $this->resolve = $resolve === null ? null : Closure::fromCallable($resolve); |
| 52 | $this->resolve = $resolve === null ? null : Closure::fromCallable($resolve); |
| 53 | $this->add($commands); |
| 54 | } |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 77 | } |
| 78 | |
| 79 | $this->toc = $toc; |
| 80 | $this->list = $list; |
| 81 | $this->longestName = $longestName; |
| 82 | |
| 83 | return $this; |
| 84 | } |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 77 | } |
| 78 | |
| 79 | $this->toc = $toc; |
| 80 | $this->list = $list; |
| 81 | $this->longestName = $longestName; |
| 82 | |
| 83 | return $this; |
| 84 | } |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 77 | } |
| 78 | |
| 79 | $this->toc = $toc; |
| 80 | $this->list = $list; |
| 81 | $this->longestName = $longestName; |
| 82 | |
| 83 | return $this; |
| 84 | } |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 77 | } |
| 78 | |
| 79 | $this->toc = $toc; |
| 80 | $this->list = $list; |
| 81 | $this->longestName = $longestName; |
| 82 | |
| 83 | return $this; |
| 84 | } |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 77 | } |
| 78 | |
| 79 | $this->toc = $toc; |
| 80 | $this->list = $list; |
| 81 | $this->longestName = $longestName; |
| 82 | |
| 83 | return $this; |
| 84 | } |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 77 | } |
| 78 | |
| 79 | $this->toc = $toc; |
| 80 | $this->list = $list; |
| 81 | $this->longestName = $longestName; |
| 82 | |
| 83 | return $this; |
| 84 | } |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 77 | } |
| 78 | |
| 79 | $this->toc = $toc; |
| 80 | $this->list = $list; |
| 81 | $this->longestName = $longestName; |
| 82 | |
| 83 | return $this; |
| 84 | } |
| 56 | public function add(array|object|string $commands): self |
| 57 | { |
| 58 | // Index into copies so a rejected call registers nothing. |
| 59 | $toc = $this->toc; |
| 60 | $list = $this->list; |
| 61 | $longestName = $this->longestName; |
| 62 | |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 63 | foreach ($this->entries($commands) as $entry) { |
| 64 | $meta = $entry->meta; |
| 65 | |
| 66 | if ($meta->prefix === '' && ($meta->name === 'help' || $meta->name === 'commands')) { |
| 67 | throw new ValueError("Command name '{$meta->name}' is reserved"); |
| 68 | } |
| 69 | |
| 70 | if (array_key_exists($meta->name, $toc[$meta->prefix] ?? [])) { |
| 71 | throw new ValueError("Duplicate command '{$meta->full()}'"); |
| 72 | } |
| 73 | |
| 74 | $toc[$meta->prefix][$meta->name] = $entry; |
| 75 | $list[$meta->name][] = $entry; |
| 76 | $longestName = max($longestName, strlen($meta->full())); |
| 77 | } |
| 78 | |
| 79 | $this->toc = $toc; |
| 80 | $this->list = $list; |
| 81 | $this->longestName = $longestName; |
| 82 | |
| 83 | return $this; |
| 84 | } |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 247 | try { |
| 248 | $entry = $this->getCommand($cmd); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 258 | } |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 247 | try { |
| 248 | $entry = $this->getCommand($cmd); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 258 | } |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 247 | try { |
| 248 | $entry = $this->getCommand($cmd); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 258 | } |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 247 | try { |
| 248 | $entry = $this->getCommand($cmd); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 258 | } |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 247 | try { |
| 248 | $entry = $this->getCommand($cmd); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 258 | } |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 247 | try { |
| 248 | $entry = $this->getCommand($cmd); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 258 | } |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 239 | new Help($this->io, $script)->showSummary(...$builtin); |
| 240 | |
| 241 | return 0; |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 238 | if ($isHelpCall || in_array('--help', $tokens, strict: true) || in_array('-h', $tokens, strict: true)) { |
| 244 | return $this->showCommands(); |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 247 | try { |
| 248 | $entry = $this->getCommand($cmd); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 258 | } |
| 230 | private function dispatch(string $arg, bool $isHelpCall, array $tokens, string $script): int |
| 231 | { |
| 232 | // `help --help` asks for the help of `help`. |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 233 | $cmd = $arg === '--help' || $arg === '-h' ? 'help' : strtolower($arg); |
| 234 | $builtin = self::BUILTINS[$cmd] ?? null; |
| 235 | |
| 236 | if ($builtin !== null) { |
| 247 | try { |
| 248 | $entry = $this->getCommand($cmd); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 257 | return $isHelpCall ? $this->showCommandHelp($entry, $script) : $this->runCommand($entry, $tokens, $script); |
| 258 | } |
| 249 | } catch (InvalidUsage $e) { |
| 250 | if ($e->getCode() === self::AMBIGUOUS) { |
| 251 | return $this->showAmbiguousMessage($cmd); |
| 249 | } catch (InvalidUsage $e) { |
| 250 | if ($e->getCode() === self::AMBIGUOUS) { |
| 254 | throw $e; |
| 387 | private function entries(array|object|string $commands): array |
| 388 | { |
| 389 | if (!is_array($commands)) { |
| 390 | return [$this->entry($commands)]; |
| 387 | private function entries(array|object|string $commands): array |
| 388 | { |
| 389 | if (!is_array($commands)) { |
| 393 | $entries = []; |
| 394 | |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 397 | if (!$item instanceof Closure) { |
| 398 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 387 | private function entries(array|object|string $commands): array |
| 388 | { |
| 389 | if (!is_array($commands)) { |
| 393 | $entries = []; |
| 394 | |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 397 | if (!$item instanceof Closure) { |
| 401 | $entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 402 | |
| 403 | continue; |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 397 | if (!$item instanceof Closure) { |
| 398 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 399 | } |
| 400 | |
| 401 | $entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 402 | |
| 403 | continue; |
| 404 | } |
| 405 | |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 407 | throw new ValueError('Invalid command registration'); |
| 408 | } |
| 409 | |
| 410 | $entries[] = $this->entry($item); |
| 411 | } |
| 412 | |
| 413 | return $entries; |
| 414 | } |
| 387 | private function entries(array|object|string $commands): array |
| 388 | { |
| 389 | if (!is_array($commands)) { |
| 393 | $entries = []; |
| 394 | |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 407 | throw new ValueError('Invalid command registration'); |
| 387 | private function entries(array|object|string $commands): array |
| 388 | { |
| 389 | if (!is_array($commands)) { |
| 393 | $entries = []; |
| 394 | |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 397 | if (!$item instanceof Closure) { |
| 398 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 399 | } |
| 400 | |
| 401 | $entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 402 | |
| 403 | continue; |
| 404 | } |
| 405 | |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 407 | throw new ValueError('Invalid command registration'); |
| 408 | } |
| 409 | |
| 410 | $entries[] = $this->entry($item); |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 397 | if (!$item instanceof Closure) { |
| 398 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 399 | } |
| 400 | |
| 401 | $entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 402 | |
| 403 | continue; |
| 404 | } |
| 405 | |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 407 | throw new ValueError('Invalid command registration'); |
| 408 | } |
| 409 | |
| 410 | $entries[] = $this->entry($item); |
| 411 | } |
| 412 | |
| 413 | return $entries; |
| 414 | } |
| 387 | private function entries(array|object|string $commands): array |
| 388 | { |
| 389 | if (!is_array($commands)) { |
| 393 | $entries = []; |
| 394 | |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 407 | throw new ValueError('Invalid command registration'); |
| 387 | private function entries(array|object|string $commands): array |
| 388 | { |
| 389 | if (!is_array($commands)) { |
| 393 | $entries = []; |
| 394 | |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 397 | if (!$item instanceof Closure) { |
| 398 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 399 | } |
| 400 | |
| 401 | $entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 402 | |
| 403 | continue; |
| 404 | } |
| 405 | |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 407 | throw new ValueError('Invalid command registration'); |
| 408 | } |
| 409 | |
| 410 | $entries[] = $this->entry($item); |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 397 | if (!$item instanceof Closure) { |
| 398 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 399 | } |
| 400 | |
| 401 | $entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 402 | |
| 403 | continue; |
| 404 | } |
| 405 | |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 407 | throw new ValueError('Invalid command registration'); |
| 408 | } |
| 409 | |
| 410 | $entries[] = $this->entry($item); |
| 411 | } |
| 412 | |
| 413 | return $entries; |
| 414 | } |
| 387 | private function entries(array|object|string $commands): array |
| 388 | { |
| 389 | if (!is_array($commands)) { |
| 393 | $entries = []; |
| 394 | |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 397 | if (!$item instanceof Closure) { |
| 398 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 399 | } |
| 400 | |
| 401 | $entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 402 | |
| 403 | continue; |
| 404 | } |
| 405 | |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 407 | throw new ValueError('Invalid command registration'); |
| 408 | } |
| 409 | |
| 410 | $entries[] = $this->entry($item); |
| 411 | } |
| 412 | |
| 413 | return $entries; |
| 414 | } |
| 387 | private function entries(array|object|string $commands): array |
| 388 | { |
| 389 | if (!is_array($commands)) { |
| 393 | $entries = []; |
| 394 | |
| 395 | foreach ($commands as $key => $item) { |
| 395 | foreach ($commands as $key => $item) { |
| 396 | if (is_string($key)) { |
| 397 | if (!$item instanceof Closure) { |
| 398 | throw new ValueError("Factory for command class '{$key}' must be a closure"); |
| 399 | } |
| 400 | |
| 401 | $entries[] = Entry::fromFactory($this->validClass($key), $item); |
| 402 | |
| 403 | continue; |
| 404 | } |
| 405 | |
| 406 | if (!is_object($item) && !is_string($item)) { |
| 407 | throw new ValueError('Invalid command registration'); |
| 408 | } |
| 409 | |
| 410 | $entries[] = $this->entry($item); |
| 411 | } |
| 412 | |
| 413 | return $entries; |
| 414 | } |
| 416 | private function entry(object|string $command): Entry |
| 417 | { |
| 418 | if ($command instanceof Closure) { |
| 419 | throw new ValueError( |
| 420 | 'Closure commands are not supported; use an anonymous class with a #[Command] attribute', |
| 416 | private function entry(object|string $command): Entry |
| 417 | { |
| 418 | if ($command instanceof Closure) { |
| 424 | if (is_object($command)) { |
| 425 | return Entry::fromInstance($command); |
| 416 | private function entry(object|string $command): Entry |
| 417 | { |
| 418 | if ($command instanceof Closure) { |
| 424 | if (is_object($command)) { |
| 428 | $class = $this->validClass($command); |
| 429 | $resolve = $this->resolve; |
| 430 | |
| 431 | return $resolve === null |
| 432 | ? Entry::fromClass($class) |
| 433 | : Entry::fromFactory($class, static fn(): mixed => $resolve($class)); |
| 434 | } |
| 416 | private function entry(object|string $command): Entry |
| 417 | { |
| 418 | if ($command instanceof Closure) { |
| 424 | if (is_object($command)) { |
| 428 | $class = $this->validClass($command); |
| 429 | $resolve = $this->resolve; |
| 430 | |
| 431 | return $resolve === null |
| 433 | : Entry::fromFactory($class, static fn(): mixed => $resolve($class)); |
| 433 | : Entry::fromFactory($class, static fn(): mixed => $resolve($class)); |
| 434 | } |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 357 | $parts = explode(':', $cmd, limit: 2); |
| 358 | |
| 359 | return $this->toc[$parts[0]][$parts[1]] ?? throw $this->notFound($cmd); |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 362 | $entry = $this->toc[''][$cmd] ?? null; |
| 363 | |
| 364 | if ($entry !== null) { |
| 365 | return $entry; |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 362 | $entry = $this->toc[''][$cmd] ?? null; |
| 363 | |
| 364 | if ($entry !== null) { |
| 368 | if (array_key_exists($cmd, $this->list)) { |
| 369 | if (count($this->list[$cmd]) === 1) { |
| 370 | return $this->list[$cmd][0]; |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 362 | $entry = $this->toc[''][$cmd] ?? null; |
| 363 | |
| 364 | if ($entry !== null) { |
| 368 | if (array_key_exists($cmd, $this->list)) { |
| 369 | if (count($this->list[$cmd]) === 1) { |
| 373 | throw new InvalidUsage('Ambiguous command', self::AMBIGUOUS); |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 362 | $entry = $this->toc[''][$cmd] ?? null; |
| 363 | |
| 364 | if ($entry !== null) { |
| 368 | if (array_key_exists($cmd, $this->list)) { |
| 376 | throw $this->notFound($cmd); |
| 377 | } |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 357 | $parts = explode(':', $cmd, limit: 2); |
| 358 | |
| 359 | return $this->toc[$parts[0]][$parts[1]] ?? throw $this->notFound($cmd); |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 362 | $entry = $this->toc[''][$cmd] ?? null; |
| 363 | |
| 364 | if ($entry !== null) { |
| 365 | return $entry; |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 362 | $entry = $this->toc[''][$cmd] ?? null; |
| 363 | |
| 364 | if ($entry !== null) { |
| 368 | if (array_key_exists($cmd, $this->list)) { |
| 369 | if (count($this->list[$cmd]) === 1) { |
| 370 | return $this->list[$cmd][0]; |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 362 | $entry = $this->toc[''][$cmd] ?? null; |
| 363 | |
| 364 | if ($entry !== null) { |
| 368 | if (array_key_exists($cmd, $this->list)) { |
| 369 | if (count($this->list[$cmd]) === 1) { |
| 373 | throw new InvalidUsage('Ambiguous command', self::AMBIGUOUS); |
| 349 | private function getCommand(string $cmd): Entry |
| 350 | { |
| 351 | // Exact full names resolve first: an unprefixed command wins over |
| 352 | // its prefixed namesakes, and prefixed lookups are always exact. |
| 353 | // Only then a bare name serves as the alias of a unique prefixed |
| 354 | // command; shared by several, it is ambiguous. |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 355 | if (str_contains($cmd, ':')) { |
| 362 | $entry = $this->toc[''][$cmd] ?? null; |
| 363 | |
| 364 | if ($entry !== null) { |
| 368 | if (array_key_exists($cmd, $this->list)) { |
| 376 | throw $this->notFound($cmd); |
| 377 | } |
| 263 | private function message(Throwable $e): string |
| 264 | { |
| 265 | return $e->getMessage() === '' ? $e::class : $e->getMessage(); |
| 265 | return $e->getMessage() === '' ? $e::class : $e->getMessage(); |
| 265 | return $e->getMessage() === '' ? $e::class : $e->getMessage(); |
| 266 | } |
| 263 | private function message(Throwable $e): string |
| 264 | { |
| 265 | return $e->getMessage() === '' ? $e::class : $e->getMessage(); |
| 265 | return $e->getMessage() === '' ? $e::class : $e->getMessage(); |
| 265 | return $e->getMessage() === '' ? $e::class : $e->getMessage(); |
| 266 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 154 | $list = []; |
| 155 | |
| 156 | foreach ($this->toc as $commands) { |
| 156 | foreach ($this->toc as $commands) { |
| 157 | foreach ($commands as $entry) { |
| 158 | $meta = $entry->meta; |
| 159 | |
| 160 | // Full names are unique; only bare names can be shared. |
| 161 | if ($meta->prefix !== '') { |
| 162 | $list[$meta->full()] = 1; |
| 163 | } |
| 164 | |
| 165 | $list[$meta->name] = ($list[$meta->name] ?? 0) + 1; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // The built-ins resolve before a command sharing their name. |
| 170 | $list['commands'] = 1; |
| 171 | $list['help'] = 1; |
| 172 | |
| 173 | ksort($list); |
| 174 | $names = []; |
| 175 | |
| 176 | foreach ($list as $name => $count) { |
| 176 | foreach ($list as $name => $count) { |
| 177 | if ($count === 1 || array_key_exists($name, $this->toc[''] ?? [])) { |
| 178 | // PHP turns numeric keys like '2026' into integers. |
| 179 | $names[] = (string) $name; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | return $names; |
| 184 | } |
| 379 | private function notFound(string $cmd): InvalidUsage |
| 380 | { |
| 381 | $name = Suggestion::find($cmd, $this->names()); |
| 382 | |
| 383 | return new InvalidUsage($name === null ? 'Command not found' : "Command not found. Did you mean '{$name}'?"); |
| 383 | return new InvalidUsage($name === null ? 'Command not found' : "Command not found. Did you mean '{$name}'?"); |
| 383 | return new InvalidUsage($name === null ? 'Command not found' : "Command not found. Did you mean '{$name}'?"); |
| 384 | } |
| 379 | private function notFound(string $cmd): InvalidUsage |
| 380 | { |
| 381 | $name = Suggestion::find($cmd, $this->names()); |
| 382 | |
| 383 | return new InvalidUsage($name === null ? 'Command not found' : "Command not found. Did you mean '{$name}'?"); |
| 383 | return new InvalidUsage($name === null ? 'Command not found' : "Command not found. Did you mean '{$name}'?"); |
| 383 | return new InvalidUsage($name === null ? 'Command not found' : "Command not found. Did you mean '{$name}'?"); |
| 384 | } |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 201 | $arg = $argv[2] ?? null; |
| 202 | } |
| 203 | |
| 204 | try { |
| 204 | try { |
| 205 | return $arg === null |
| 206 | ? $this->showHelp($script) |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 201 | $arg = $argv[2] ?? null; |
| 202 | } |
| 203 | |
| 204 | try { |
| 204 | try { |
| 205 | return $arg === null |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 204 | try { |
| 205 | return $arg === null |
| 206 | ? $this->showHelp($script) |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 204 | try { |
| 205 | return $arg === null |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 201 | $arg = $argv[2] ?? null; |
| 202 | } |
| 203 | |
| 204 | try { |
| 204 | try { |
| 205 | return $arg === null |
| 206 | ? $this->showHelp($script) |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 201 | $arg = $argv[2] ?? null; |
| 202 | } |
| 203 | |
| 204 | try { |
| 204 | try { |
| 205 | return $arg === null |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 204 | try { |
| 205 | return $arg === null |
| 206 | ? $this->showHelp($script) |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 204 | try { |
| 205 | return $arg === null |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 201 | $arg = $argv[2] ?? null; |
| 202 | } |
| 203 | |
| 204 | try { |
| 204 | try { |
| 205 | return $arg === null |
| 206 | ? $this->showHelp($script) |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 201 | $arg = $argv[2] ?? null; |
| 202 | } |
| 203 | |
| 204 | try { |
| 204 | try { |
| 205 | return $arg === null |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 204 | try { |
| 205 | return $arg === null |
| 206 | ? $this->showHelp($script) |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 204 | try { |
| 205 | return $arg === null |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 201 | $arg = $argv[2] ?? null; |
| 202 | } |
| 203 | |
| 204 | try { |
| 204 | try { |
| 205 | return $arg === null |
| 206 | ? $this->showHelp($script) |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 201 | $arg = $argv[2] ?? null; |
| 202 | } |
| 203 | |
| 204 | try { |
| 204 | try { |
| 205 | return $arg === null |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 204 | try { |
| 205 | return $arg === null |
| 206 | ? $this->showHelp($script) |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 193 | public function run(?array $argv = null): int |
| 194 | { |
| 195 | $argv ??= $_SERVER['argv'] ?? []; |
| 196 | $script = $argv[0] ?? ''; |
| 197 | $arg = $argv[1] ?? null; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 198 | $isHelpCall = $arg === '--help' || $arg === '-h' || strtolower($arg ?? '') === 'help'; |
| 199 | |
| 200 | if ($isHelpCall) { |
| 204 | try { |
| 205 | return $arg === null |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 207 | : $this->dispatch($arg, $isHelpCall, array_slice($argv, offset: 2), $script); |
| 208 | } catch (Throwable $e) { |
| 210 | $this->io->error( |
| 211 | "Error while running command '%s':\n\n%s", |
| 212 | $arg ?? '<no command given>', |
| 213 | $this->message($e), |
| 214 | ); |
| 215 | |
| 216 | // A usage error is the input's fault; its trace shows nothing. |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 218 | $this->showTraceback($e); |
| 219 | } |
| 220 | |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 222 | } |
| 223 | } |
| 208 | } catch (Throwable $e) { |
| 210 | $this->io->error( |
| 211 | "Error while running command '%s':\n\n%s", |
| 212 | $arg ?? '<no command given>', |
| 213 | $this->message($e), |
| 214 | ); |
| 215 | |
| 216 | // A usage error is the input's fault; its trace shows nothing. |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 218 | $this->showTraceback($e); |
| 219 | } |
| 220 | |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 222 | } |
| 223 | } |
| 208 | } catch (Throwable $e) { |
| 210 | $this->io->error( |
| 211 | "Error while running command '%s':\n\n%s", |
| 212 | $arg ?? '<no command given>', |
| 213 | $this->message($e), |
| 214 | ); |
| 215 | |
| 216 | // A usage error is the input's fault; its trace shows nothing. |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 222 | } |
| 223 | } |
| 208 | } catch (Throwable $e) { |
| 210 | $this->io->error( |
| 211 | "Error while running command '%s':\n\n%s", |
| 212 | $arg ?? '<no command given>', |
| 213 | $this->message($e), |
| 214 | ); |
| 215 | |
| 216 | // A usage error is the input's fault; its trace shows nothing. |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 222 | } |
| 223 | } |
| 208 | } catch (Throwable $e) { |
| 210 | $this->io->error( |
| 211 | "Error while running command '%s':\n\n%s", |
| 212 | $arg ?? '<no command given>', |
| 213 | $this->message($e), |
| 214 | ); |
| 215 | |
| 216 | // A usage error is the input's fault; its trace shows nothing. |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 218 | $this->showTraceback($e); |
| 219 | } |
| 220 | |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 222 | } |
| 223 | } |
| 208 | } catch (Throwable $e) { |
| 210 | $this->io->error( |
| 211 | "Error while running command '%s':\n\n%s", |
| 212 | $arg ?? '<no command given>', |
| 213 | $this->message($e), |
| 214 | ); |
| 215 | |
| 216 | // A usage error is the input's fault; its trace shows nothing. |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 218 | $this->showTraceback($e); |
| 219 | } |
| 220 | |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 222 | } |
| 223 | } |
| 208 | } catch (Throwable $e) { |
| 210 | $this->io->error( |
| 211 | "Error while running command '%s':\n\n%s", |
| 212 | $arg ?? '<no command given>', |
| 213 | $this->message($e), |
| 214 | ); |
| 215 | |
| 216 | // A usage error is the input's fault; its trace shows nothing. |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 222 | } |
| 223 | } |
| 208 | } catch (Throwable $e) { |
| 210 | $this->io->error( |
| 211 | "Error while running command '%s':\n\n%s", |
| 212 | $arg ?? '<no command given>', |
| 213 | $this->message($e), |
| 214 | ); |
| 215 | |
| 216 | // A usage error is the input's fault; its trace shows nothing. |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 217 | if ($this->debug && !$e instanceof InvalidUsage) { |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 221 | return $e instanceof InvalidUsage ? 2 : 1; |
| 222 | } |
| 223 | } |
| 291 | private function runCommand(Entry $entry, array $tokens, string $script): int |
| 292 | { |
| 293 | $signature = $entry->signature(); |
| 294 | |
| 295 | if ($signature->asksForHelp($tokens)) { |
| 296 | return $this->showCommandHelp($entry, $script); |
| 291 | private function runCommand(Entry $entry, array $tokens, string $script): int |
| 292 | { |
| 293 | $signature = $entry->signature(); |
| 294 | |
| 295 | if ($signature->asksForHelp($tokens)) { |
| 299 | $values = $signature->bind($tokens, $this->io); |
| 300 | |
| 301 | // The signature checked that __invoke() exists; PHP requires it |
| 302 | // to be public. |
| 303 | /** @var callable-object $command */ |
| 304 | $command = $entry->command(); |
| 305 | |
| 306 | /** @var int Guaranteed by the declared return type under strict_types */ |
| 307 | return $command(...$signature->match($values, $command)); |
| 308 | } |
| 336 | private function showAmbiguousMessage(string $cmd): int |
| 337 | { |
| 338 | $this->io->error("Ambiguous command. Please add the group name:\n"); |
| 339 | $entries = $this->list[$cmd]; |
| 340 | usort($entries, static fn(Entry $a, Entry $b): int => strcmp($a->meta->full(), $b->meta->full())); |
| 341 | |
| 342 | foreach ($entries as $entry) { |
| 342 | foreach ($entries as $entry) { |
| 342 | foreach ($entries as $entry) { |
| 343 | $this->io->error(' <yellow>%s</yellow>:%s', $entry->meta->prefix, $entry->meta->name); |
| 342 | foreach ($entries as $entry) { |
| 342 | foreach ($entries as $entry) { |
| 343 | $this->io->error(' <yellow>%s</yellow>:%s', $entry->meta->prefix, $entry->meta->name); |
| 344 | } |
| 345 | |
| 346 | return 2; |
| 347 | } |
| 336 | private function showAmbiguousMessage(string $cmd): int |
| 337 | { |
| 338 | $this->io->error("Ambiguous command. Please add the group name:\n"); |
| 339 | $entries = $this->list[$cmd]; |
| 340 | usort($entries, static fn(Entry $a, Entry $b): int => strcmp($a->meta->full(), $b->meta->full())); |
| 341 | |
| 342 | foreach ($entries as $entry) { |
| 342 | foreach ($entries as $entry) { |
| 342 | foreach ($entries as $entry) { |
| 343 | $this->io->error(' <yellow>%s</yellow>:%s', $entry->meta->prefix, $entry->meta->name); |
| 344 | } |
| 345 | |
| 346 | return 2; |
| 347 | } |
| 336 | private function showAmbiguousMessage(string $cmd): int |
| 337 | { |
| 338 | $this->io->error("Ambiguous command. Please add the group name:\n"); |
| 339 | $entries = $this->list[$cmd]; |
| 340 | usort($entries, static fn(Entry $a, Entry $b): int => strcmp($a->meta->full(), $b->meta->full())); |
| 341 | |
| 342 | foreach ($entries as $entry) { |
| 342 | foreach ($entries as $entry) { |
| 343 | $this->io->error(' <yellow>%s</yellow>:%s', $entry->meta->prefix, $entry->meta->name); |
| 344 | } |
| 345 | |
| 346 | return 2; |
| 347 | } |
| 325 | private function showCommand(string $prefix, string $name, string $desc): void |
| 326 | { |
| 327 | $prefix = $prefix === '' ? '' : $prefix . ':'; |
| 327 | $prefix = $prefix === '' ? '' : $prefix . ':'; |
| 327 | $prefix = $prefix === '' ? '' : $prefix . ':'; |
| 328 | |
| 329 | // The longest name includes every listed one, so the gap is at |
| 330 | // least 2. |
| 331 | $pad = str_repeat(' ', $this->longestName + 2 - strlen($prefix . $name)); |
| 332 | $this->io->write(' %s<green>%s</green>%s', $prefix, $name, $pad); |
| 333 | $this->io->line($desc); |
| 334 | } |
| 325 | private function showCommand(string $prefix, string $name, string $desc): void |
| 326 | { |
| 327 | $prefix = $prefix === '' ? '' : $prefix . ':'; |
| 327 | $prefix = $prefix === '' ? '' : $prefix . ':'; |
| 327 | $prefix = $prefix === '' ? '' : $prefix . ':'; |
| 328 | |
| 329 | // The longest name includes every listed one, so the gap is at |
| 330 | // least 2. |
| 331 | $pad = str_repeat(' ', $this->longestName + 2 - strlen($prefix . $name)); |
| 332 | $this->io->write(' %s<green>%s</green>%s', $prefix, $name, $pad); |
| 333 | $this->io->line($desc); |
| 334 | } |
| 310 | private function showCommandHelp(Entry $entry, string $script): int |
| 311 | { |
| 312 | new Help($this->io, $script)->showFor($entry->class); |
| 313 | |
| 314 | return 0; |
| 315 | } |
| 134 | foreach ($this->names() as $name) { |
| 134 | foreach ($this->names() as $name) { |
| 134 | foreach ($this->names() as $name) { |
| 135 | $this->io->line('%s', $name); |
| 134 | foreach ($this->names() as $name) { |
| 134 | foreach ($this->names() as $name) { |
| 135 | $this->io->line('%s', $name); |
| 136 | } |
| 137 | |
| 138 | return 0; |
| 139 | } |
| 134 | foreach ($this->names() as $name) { |
| 134 | foreach ($this->names() as $name) { |
| 134 | foreach ($this->names() as $name) { |
| 135 | $this->io->line('%s', $name); |
| 136 | } |
| 137 | |
| 138 | return 0; |
| 139 | } |
| 134 | foreach ($this->names() as $name) { |
| 134 | foreach ($this->names() as $name) { |
| 135 | $this->io->line('%s', $name); |
| 136 | } |
| 137 | |
| 138 | return 0; |
| 139 | } |
| 317 | private function showGroup(string $title): void |
| 318 | { |
| 319 | $this->io->line("\n<yellow>%s</yellow>", $title); |
| 320 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 97 | foreach ($commands as $entry) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 97 | foreach ($commands as $entry) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 97 | foreach ($commands as $entry) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 86 | private function showHelp(string $script): int |
| 87 | { |
| 88 | $this->io->line('<yellow>Usage:</yellow>'); |
| 89 | $this->io->line(" php %s [prefix:]command [arguments]\n", $script); |
| 90 | $this->io->line("Prefixes are optional if the command is unambiguous.\n"); |
| 91 | $this->io->line('Available commands:'); |
| 92 | |
| 93 | /** @var array<array-key, array<string, Entry>> $sections */ |
| 94 | $sections = []; |
| 95 | |
| 96 | foreach ($this->toc as $commands) { |
| 96 | foreach ($this->toc as $commands) { |
| 97 | foreach ($commands as $entry) { |
| 98 | $sections[$entry->meta->title()][$entry->meta->full()] = $entry; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | $this->showGroup('General'); |
| 103 | $this->showCommand('', 'commands', 'Lists all available commands'); |
| 104 | $this->showCommand('', 'help', 'Displays this overview'); |
| 105 | $this->showSection($sections['General'] ?? []); |
| 106 | unset($sections['General']); |
| 107 | |
| 108 | // PHP turns numeric keys like '2026' into integers. |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 110 | |
| 111 | foreach ($sections as $title => $entries) { |
| 111 | foreach ($sections as $title => $entries) { |
| 112 | $this->showGroup((string) $title); |
| 113 | $this->showSection($entries); |
| 114 | } |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 120 | private function showSection(array $entries): void |
| 121 | { |
| 122 | ksort($entries, SORT_STRING); |
| 123 | |
| 124 | foreach ($entries as $entry) { |
| 124 | foreach ($entries as $entry) { |
| 124 | foreach ($entries as $entry) { |
| 125 | $this->showCommand($entry->meta->prefix, $entry->meta->name, $entry->meta->description); |
| 124 | foreach ($entries as $entry) { |
| 124 | foreach ($entries as $entry) { |
| 125 | $this->showCommand($entry->meta->prefix, $entry->meta->name, $entry->meta->description); |
| 126 | } |
| 127 | } |
| 120 | private function showSection(array $entries): void |
| 121 | { |
| 122 | ksort($entries, SORT_STRING); |
| 123 | |
| 124 | foreach ($entries as $entry) { |
| 124 | foreach ($entries as $entry) { |
| 124 | foreach ($entries as $entry) { |
| 125 | $this->showCommand($entry->meta->prefix, $entry->meta->name, $entry->meta->description); |
| 126 | } |
| 127 | } |
| 120 | private function showSection(array $entries): void |
| 121 | { |
| 122 | ksort($entries, SORT_STRING); |
| 123 | |
| 124 | foreach ($entries as $entry) { |
| 124 | foreach ($entries as $entry) { |
| 125 | $this->showCommand($entry->meta->prefix, $entry->meta->name, $entry->meta->description); |
| 126 | } |
| 127 | } |
| 272 | private function showTraceback(Throwable $e): void |
| 273 | { |
| 274 | $io = $this->io->stderr(); |
| 275 | $io->line("\n<yellow>Traceback:</yellow>"); |
| 276 | |
| 277 | while (true) { |
| 277 | while (true) { |
| 288 | } |
| 272 | private function showTraceback(Throwable $e): void |
| 273 | { |
| 274 | $io = $this->io->stderr(); |
| 275 | $io->line("\n<yellow>Traceback:</yellow>"); |
| 276 | |
| 277 | while (true) { |
| 277 | while (true) { |
| 278 | $io->line('%s in %s:%d', $e::class, $e->getFile(), $e->getLine()); |
| 279 | $io->line('%s', $e->getTraceAsString()); |
| 280 | $e = $e->getPrevious(); |
| 281 | |
| 282 | if ($e === null) { |
| 283 | return; |
| 272 | private function showTraceback(Throwable $e): void |
| 273 | { |
| 274 | $io = $this->io->stderr(); |
| 275 | $io->line("\n<yellow>Traceback:</yellow>"); |
| 276 | |
| 277 | while (true) { |
| 277 | while (true) { |
| 278 | $io->line('%s in %s:%d', $e::class, $e->getFile(), $e->getLine()); |
| 279 | $io->line('%s', $e->getTraceAsString()); |
| 280 | $e = $e->getPrevious(); |
| 281 | |
| 282 | if ($e === null) { |
| 277 | while (true) { |
| 278 | $io->line('%s in %s:%d', $e::class, $e->getFile(), $e->getLine()); |
| 279 | $io->line('%s', $e->getTraceAsString()); |
| 280 | $e = $e->getPrevious(); |
| 281 | |
| 282 | if ($e === null) { |
| 283 | return; |
| 284 | } |
| 285 | |
| 286 | $io->line("\n<yellow>Caused by:</yellow> %s", $this->message($e)); |
| 277 | while (true) { |
| 288 | } |
| 437 | private function validClass(string $class): string |
| 438 | { |
| 439 | if (!class_exists($class)) { |
| 439 | if (!class_exists($class)) { |
| 439 | if (!class_exists($class)) { |
| 440 | throw new ValueError("Unknown command class '{$class}'"); |
| 437 | private function validClass(string $class): string |
| 438 | { |
| 439 | if (!class_exists($class)) { |
| 439 | if (!class_exists($class)) { |
| 439 | if (!class_exists($class)) { |
| 443 | return $class; |
| 444 | } |
| 437 | private function validClass(string $class): string |
| 438 | { |
| 439 | if (!class_exists($class)) { |
| 439 | if (!class_exists($class)) { |
| 439 | if (!class_exists($class)) { |
| 440 | throw new ValueError("Unknown command class '{$class}'"); |
| 437 | private function validClass(string $class): string |
| 438 | { |
| 439 | if (!class_exists($class)) { |
| 439 | if (!class_exists($class)) { |
| 439 | if (!class_exists($class)) { |
| 443 | return $class; |
| 444 | } |
| 109 | uksort($sections, static fn(int|string $a, int|string $b): int => strcasecmp((string) $a, (string) $b)); |
| 340 | usort($entries, static fn(Entry $a, Entry $b): int => strcmp($a->meta->full(), $b->meta->full())); |
| 433 | : Entry::fromFactory($class, static fn(): mixed => $resolve($class)); |