*/ private array $commands; public function __construct(ConsoleCommand ...$commands) { $commandMap = []; foreach ($commands as $command) { if (isset($commandMap[$command->name])) { throw FrameworkException::create( ErrorCode::CON_INVALID_COMMAND_STRUCTURE, "Duplicate command name '{$command->name}'" )->withData(['command_name' => $command->name]); } $commandMap[$command->name] = $command; } $this->commands = $commandMap; } public static function empty(): self { return new self(); } public function add(ConsoleCommand $command): self { if ($this->has($command->name)) { throw FrameworkException::create( ErrorCode::CON_INVALID_COMMAND_STRUCTURE, "Command '{$command->name}' already exists" )->withData(['command_name' => $command->name]); } $allCommands = array_values($this->commands); $allCommands[] = $command; return new self(...$allCommands); } public function has(string $name): bool { return isset($this->commands[$name]); } public function get(string $name): ConsoleCommand { if (! $this->has($name)) { throw FrameworkException::create( ErrorCode::CON_COMMAND_NOT_FOUND, "Command '{$name}' not found" )->withData(['command_name' => $name]); } return $this->commands[$name]; } public function getNames(): array { return array_keys($this->commands); } public function findSimilar(string $name, int $maxDistance = 3): array { $suggestions = []; foreach ($this->getNames() as $commandName) { $distance = levenshtein($name, $commandName); if ($distance <= $maxDistance && $distance > 0) { $suggestions[] = $commandName; } } return $suggestions; } public function count(): int { return count($this->commands); } public function getIterator(): Traversable { return new ArrayIterator($this->commands); } public function toArray(): array { return $this->commands; } public function isEmpty(): bool { return empty($this->commands); } }