Commands

A Command functions similarly to a Controller in a web application.

Commands must implement Centum\Interfaces\Console\CommandInterface.

Commands only require one public method:

  • public function execute(Centum\Interfaces\Console\TerminalInterface $terminal): int

The execute() method’s return value is the exit code. 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

The CommandMetadata attribute class stores the name and description of the Command. This is separated so that this information can be validated without instantiating the Command.

By default, a Command can be as simple as this:

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;
    }
}

A Command’s name must follow a slug pattern - all lowercase, alphanumeric with dashes, and must begin and end with alphanumeric characters. To allow commands like php cli.php, empty names are allowed.

Command Arguments

Commands can take inputs in the form of arguments:

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

To access these arguments from within a Command, they need to be declared in the constructor with a type of string or bool:

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 $name,
        protected readonly bool $loud
    ) {
    }

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

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

        $terminal->writeLine($message);

        return self::SUCCESS;
    }
}

Arguments are converted to camel-case so --first-name will become $firstName in the constructor.

Injecting Services into the Application

Services from the Container can also be injected through the constructor method:

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

Commands can be added 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 that they are added to the Application but a later added Command can overwrite an older Command with the same name.

If the Command’s name is not valid, then an Centum\Console\Exception\InvalidCommandNameException will be thrown.