Middleware
tempest/console
has support for middleware, a well known concept within the context of web applications, which also makes building a lot of console features easier.
Console middleware can be applied both globally (to all console commands), or on a per-command basis.
The global middleware stack is defined within ConsoleConfig
. Note that the default stack is provided out of the box, you only need to add a custom config file if you want to change this stack.
// app/Config/console.config.php use Tempest\Console\ConsoleConfig; return new ConsoleConfig( // … middleware: [ OverviewMiddleware::class, ResolveOrRescueMiddleware::class, ConsoleExceptionMiddleware::class, HelpMiddleware::class, ], );
Individual middleware can be added on top of this stack by passing it into the #[ConsoleCommand]
attribute:
// app/ForceCommand.php use Tempest\Console\HasConsole; use Tempest\Console\ConsoleCommand; use Tempest\Console\Middleware\ForceMiddleware; final readonly class ForceCommand { use HasConsole; #[ConsoleCommand( middleware: [ForceMiddleware::class] )] public function __invoke(): void { /* … */ } }
Let's take a look at the built-in middleware that Tempest provides.
OverviewMiddleware
Renders the console command overview when no specific command is provided.
./tempest Tempest General install [--force=false] - Interactively install Tempest in your project routes - List all registered routes serve [host='localhost:8000'] [publicDir='public/'] - Start a PHP development server deploy /* … */
ResolveOrRescueMiddleware
Shows a list of similar commands when you mistyped a command:
./tempest migrate Command migrate not found Did you mean to run one of these? [x] migrate:down [ ] migrate:up Press enter to confirm, ctrl+c to cancel
ConsoleExceptionMiddleware
Handles exceptions that while running a console command.
./tempest fail Exception A message from the exception default 18 } 19 20 function failingFunction(string $string) 21 { 22 throw new Exception("A message from the exception {$string}"); < 23 } /Users/brent/Dev/tempest-console/tests/Fixtures/FailCommand.php:22
HelpMiddleware
Adds the global --help
and -h
flags to all commands.
./tempest serve --help Usage serve [host='localhost:8000'] [publicDir='public/'] - Start a PHP development server
ForceMiddleware
Adds the global --force
and -f
flags to all commands. Using these flags will cause tempest to skip all $console->confirm()
calls.
use Tempest\Console\Middleware\ForceMiddleware; #[ConsoleCommand( middleware: [ForceMiddleware::class] )] public function __invoke(): void { // This part will be skipped when the `-f` flag is applied if (! $this->console->confirm('continue?')) { return; } $this->console->writeln('continued'); }
CautionMiddleware
Adds a warning before running the command in production or staging.
use Tempest\Console\Middleware\CautionMiddleware; #[ConsoleCommand( middleware: [CautionMiddleware::class] )] public function __invoke(): void { $this->console->error('something cautionous'); }
Caution! Do you wish to continue? [yes/no] something cautionous
Building your own middleware
You can create your own middleware by implementing the ConsoleMiddleware
interface:
// app/HelloWorldMiddleware.php use Tempest\Console\HasConsole; use Tempest\Console\ConsoleMiddleware; use Tempest\Console\ConsoleMiddlewareCallable; final readonly class HelloWorldMiddleware implements ConsoleMiddleware { use HasConsole; public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next): ExitCode|int { if ($invocation->argumentBag->get('hello')) { $this->writeln('Hello world!') } return $next($invocation); } }
Middleware classes will be autowired by the container, so you can use the constructor to inject any dependency you'd like. The Invocation
object contains everything you need about the context for the current console command invocation:
-
$invocation->argumentBag
contains the argument bag with all the input provided by the user. -
$invocation->consoleCommand
an instance of the#[ConsoleCommand]
attribute for the matched console command. This property will benull
if you're not usingResolveOrRescueMiddleware
or if your middleware runs before it.