0

Command base class — $signature, handle(), output helpers

Intermediate5 min read·fw-10-002
laravel-src

Concept

Command base class provides the structure all framework commands inherit from. It defines $signature, handle(), and output helpers. The base class parses the signature, integrates with input/output objects, and provides the developer-facing API.

$signature parsing: The signature string defines the command name, arguments, and options in one concise string: 'make:model {name} {--migration}'. The base class parses this to register the command with the CLI framework and to provide $this->argument() and $this->option().

handle(): int: The main method. Returns an exit code: 0 = success (use self::SUCCESS), 1 = failure (self::FAILURE), 2 = invalid input (self::INVALID).

Output helpers (wrapping an Output object):

  • $this->info(string $message): Green text.
  • $this->error(string $message): Red text to stderr.
  • $this->line(string $message): Default color.
  • $this->warn(string $message): Yellow text.
  • $this->table(array $headers, array $rows).

Input helpers:

  • $this->argument(string $name): Get an argument value.
  • $this->option(string $name): Get an option value.
  • $this->ask(string $question): Prompt user for input.
  • $this->confirm(string $question): Yes/No prompt.
  • $this->choice(string $question, array $choices): Selection.

Dependency injection via constructor: Commands resolved from the container can declare constructor dependencies.

Code Example

php
<?php
namespace Framework\Console;

abstract class Command
{
    public const SUCCESS = 0;
    public const FAILURE = 1;
    public const INVALID = 2;

    protected string $signature    = '';
    protected string $description  = '';
    protected string $name         = '';

    protected InputInterface  $input;
    protected OutputInterface $output;

    abstract public function handle(): int;

    // Called by Application when running the command
    public function run(InputInterface $input, OutputInterface $output): int
    {
        $this->input  = $input;
        $this->output = $output;
        return $this->handle();
    }

    // --- Input helpers ---
    protected function argument(string $name): mixed
    {
        return $this->input->getArgument($name);
    }

    protected function option(string $name): mixed
    {
        return $this->input->getOption($name);
    }

    protected function ask(string $question, ?string $default = null): string
    {
        return $this->output->ask($question, $default) ?? $default ?? '';
    }

    protected function confirm(string $question, bool $default = false): bool
    {
        return $this->output->confirm($question, $default);
    }

    protected function choice(string $question, array $choices, mixed $default = null): string
    {
        return $this->output->choice($question, $choices, $default);
    }

    // --- Output helpers ---
    protected function info(string $message): void    { $this->output->info($message); }
    protected function error(string $message): void   { $this->output->error($message); }
    protected function warn(string $message): void    { $this->output->warning($message); }
    protected function line(string $message): void    { $this->output->writeln($message); }
    protected function newLine(int $count = 1): void  { $this->output->newLine($count); }

    protected function table(array $headers, array $rows): void
    {
        $this->output->table($headers, $rows);
    }

    public function getName(): string        { return $this->name ?: $this->parseNameFromSignature(); }
    public function getDescription(): string { return $this->description; }
    public function getSignature(): string   { return $this->signature; }

    private function parseNameFromSignature(): string
    {
        // Extract 'make:model' from 'make:model {name} {--migration}'
        return trim(explode('{', $this->signature)[0]);
    }
}

// Example command using the base class
class ListUsersCommand extends Command
{
    protected string $signature   = 'users:list {--role= : Filter by role} {--limit=20 : Max results}';
    protected string $description = 'List all users';

    public function handle(): int
    {
        $role  = $this->option('role');
        $limit = (int) $this->option('limit');

        $query = User::query()->limit($limit);
        if ($role) $query->where('role', '=', $role);
        $users = $query->get();

        $this->table(
            ['ID', 'Name', 'Email', 'Role'],
            array_map(fn($u) => [$u->id, $u->name, $u->email, $u->role], $users)
        );
        $this->info("Total: {$users->count()} users");
        return self::SUCCESS;
    }
}