Commands

A Command in Centum functions similarly to a Controller in a web application, providing an entry point for CLI actions.

Commands must implement Centum\Interfaces\Console\CommandInterface.

Command Structure

Commands require one public method:

  • public function execute(Centum\Interfaces\Console\TerminalInterface $terminal): int The return value is the exit code.

Exit Codes

Three constants exist to clearly describe an exit code:

  • Centum\Interfaces\Console\CommandInterface::SUCCESS = 0
  • Centum\Interfaces\Console\CommandInterface::FAILURE = 1
  • Centum\Interfaces\Console\CommandInterface::INVALID = 2

Within a Command, you can use the self keyword (for example: return self::SUCCESS;).

Metadata

Use the CommandMetadata attribute to define the command’s name and description. This is separated so that this information can be validated without instantiating the Command.

Example: Basic Command

namespace App\Console\Commands;

use Centum\Console\CommandMetadata;
use Centum\Interfaces\Console\CommandInterface;
use Centum\Interfaces\Console\TerminalInterface;

#[CommandMetadata("this:is:your:name")]
class IndexCommand implements CommandInterface
{
    public function execute(TerminalInterface $terminal): int
    {
        $terminal->writeLine("hello");

        return self::SUCCESS;
    }
}

Command names must follow a slug pattern: all lowercase, alphanumeric with dashes, and must begin and end with alphanumeric characters. Empty names are allowed for default commands (e.g., php cli.php).

Command Arguments

Commands can accept arguments via the CLI:

php cli.php hello --first-name Sid --loud

Declare arguments in the constructor with string or bool types. Arguments are converted to camel-case, so --first-name becomes $firstName.

namespace App\Console\Commands;

use Centum\Console\CommandMetadata;
use Centum\Interfaces\Console\CommandInterface;
use Centum\Interfaces\Console\TerminalInterface;

#[CommandMetadata("hello")]
class HelloCommand implements CommandInterface
{
    public function __construct(
        protected readonly string $firstName,
        protected readonly bool $loud
    ) {
    }

    public function execute(TerminalInterface $terminal): int
    {
        $message = "Hello {$this->firstName}!";

        if ($this->loud) {
            $message = mb_strtoupper($message);
        }

        $terminal->writeLine($message);

        return self::SUCCESS;
    }
}

Injecting Services

You can inject services from the Container into your command’s constructor:

namespace App\Console\Commands;

use Centum\Console\CommandMetadata;
use Centum\Interfaces\Clock\ClockInterface;
use Centum\Interfaces\Console\CommandInterface;
use Centum\Interfaces\Console\TerminalInterface;

#[CommandMetadata("hello")]
class HelloCommand implements CommandInterface
{
    public function __construct(
        protected readonly string $name,
        protected readonly ClockInterface $clock
    ) {
    }

    public function execute(TerminalInterface $terminal): int
    {
        $terminal->writeLine("Hello {$this->name}!");

        $now = $this->clock->now();

        $terminal->writeLine(
            "The time is now {$now->format("H:i:s")} in {$now->format("e")}."
        );

        return self::SUCCESS;
    }
}

Adding Commands to the Application

Add Commands to the Application using the addCommand() method:

use App\Console\Commands\IndexCommand;
use Centum\Interfaces\Console\ApplicationInterface;

/** @var ApplicationInterface $application */

$application->addCommand(IndexCommand::class);

The Application will be able to determine Command’s name from the CommandMetadata attribute. Commands are processed in the order they are added. A later Command with the same name will overwrite an earlier one.

If the Command’s name is invalid, InvalidCommandNameException will be thrown.