celema / console
Celema console command runner
Requires
- php: ^8.5
- ext-mbstring: *
Requires (Dev)
- carthage-software/mago: 1.50.0
- ernst/coverlyzer: ^0.3
- infection/infection: 0.35.6
- phpunit/phpunit: ^13.0
- vimeo/psalm: 6.19.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A command line interface helper.
Features
- Commands are plain classes marked with a
#[Command]attribute — no base class, free constructors - Arguments and options are
__invoke()parameters marked#[Arg]or#[Opt], converted to their declared types —string,int,float,bool,array, or a backed enum — with defaults from the signature - Option groups: classes bundling
#[Opt]constructor parameters, shared by several commands - Automatic help generation from the
#[Command]attribute and the__invoke()signature - Strict by default: the parameters are a command's complete interface — an unknown or malformed option (with a "Did you mean" suggestion), a value of the wrong type, a missing required argument, or an undeclared positional aborts with exit code 2 before the command runs; an
arrayargument takes open-ended input - Raw access to the parsed options and positionals via an injected
Argsobject - Lazy command construction: factories or an optional class resolver run only for the invoked command
- Anonymous classes as lightweight one-off commands — attributes work inline
- Built-in color support with per-stream terminal detection and
NO_COLOR/FORCE_COLORhandling - Command help with
php run help <command> - Built-in
commandscommand for shell autocomplete --key=valueoptions (repeatable) and boolean--flag/-hflags;--ends option parsing- Output from markup templates and data arguments:
$io->error('Cannot read <strong>%s</strong>', $path)needs no escaping;line(),write(),success(),warn(), anderror()(warnings and errors go to STDERR) - Inline markup for styled output:
<strong>,<em>,<dim>,<u>, the ANSI colors —<green>,<bright-red>,<bg-blue>, ... — and truecolor hex tags:<#ff7313>,<bg-#ff7313> - Interactive prompts:
ask(),secret()for hidden input,confirm(), andchoice() Buffer, a terminal in memory for testing commands without output buffering or escape-code stripping- Text formatting helpers:
indent()wraps,pad()aligns,rule()separates — all on the visible width, markup and multibyte aware Tablefor minimal scc-style column output — no borders, no cell wrapping- Debug mode for detailed error traces
Installation
composer require celema/console
Quick Start
A command is a plain invokable class with a #[Command] attribute:
use Celema\Console\{Arg, Command, Opt, Io}; #[Command('grp:mycommand', 'This is my command')] class MyCommand { public function __invoke( Io $io, #[Arg('Who to greet')] string $name = 'world', #[Opt('Rows per batch', short: '-b')] int $batch = 500, #[Opt('Skip the safety net')] bool $force = false, ): int { $io->line('Running my command for %s in batches of %d', $name, $batch); $io->success('Command completed!'); return 0; } }
__invoke() must declare the return type int (the exit code). An Io parameter is injected; #[Arg] parameters take the positional arguments in order and #[Opt] parameters the options, named after the parameter in kebab-case. Options use --key=value (a flag like --force has no value), and every option needs a default.
Create a runner script and pass its exit code to exit():
<?php require __DIR__ . '/vendor/autoload.php'; use Celema\Console\Runner; $runner = new Runner(); $runner->add(new MyCommand()); exit($runner->run());
Run your command:
$ php run mycommand alice -b=100 Running my command for alice in batches of 100 Command completed!
Resolving Commands
Register class names with an optional resolver to construct commands through your container or application runtime:
$runner = new Runner( [MyCommand::class], resolve: $container->get(...), );
The resolver receives the registered class name and must return an instance of that class or a subclass. Commands are resolved only when invoked, then cached per registration; instances and explicit factories bypass the resolver. Without a resolver, class names use a zero-argument constructor. Console has no container dependency.
Commands can also receive Io through their constructors. Configure the resolver to supply the same Io instance you pass to the runner as its output; Console does not register services in your container. See Registering Commands for details.
Mutation testing
Mutation testing with Infection is not part of composer ci, but the CI workflow runs it after the coverage step and enforces the minimum mutation score from infection.json5.dist. Pushes only mutate the changed lines; a weekly scheduled run covers the whole codebase. Run it locally with:
composer mutation
Reports are written to .infection/.
License
This project is licensed under the MIT license.