pmjones/auto-shell
AutoShell maps CLI command names to PHP command classes in a namespace, reflecting on a main method to parse args/options (scalars or arrays). Add a class in the command directory and it becomes available automatically—no dependencies, minimal setup.
composer require pmjones/auto-shell
bin/console.php):
<?php
use AutoShell\Console;
require dirname(__DIR__) . '/vendor/autoload.php';
$console = Console::new(
namespace: 'App\\Cli\\Command',
directory: __DIR__ . '/../src/Cli/Command',
);
exit($console($_SERVER['argv']));
src/Cli/Command/Hello.php):
<?php
namespace App\Cli\Command;
class Hello
{
public function __invoke(string $name): int
{
echo "Hello, {$name}!\n";
return 0;
}
}
php bin/console.php hello world
Output: Hello, world!__invoke() and instantly use it via CLI.string, int, bool, array) for automatic parsing.#[Help] attributes to auto-generate --help documentation.CreateUser → create-user).__invoke() (default) or customize via method in Console::new().
public function __invoke(
string $name,
int $retryCount = 3,
array $tags = []
): int { ... }
Options interface with #[Option] attributes.
class UserOptions implements Options {
public function __construct(
#[Option('v,verbose')]
public readonly bool $verbose = false,
) {}
}
Options classes to __invoke() for shared/global options.
public function __invoke(GlobalOptions $global, UserOptions $user): int { ... }
$console = Console::new(
namespace: 'App\\Cli\\Command',
directory: __DIR__ . '/../src/Cli/Command',
factory: fn(string $class) => $container->get($class),
);
#[Help] on classes and parameters.
#[Help("Deploys the application.")]
class Deploy {
public function __invoke(
#[Help("The environment to deploy to (e.g., staging).")]
string $env
): int { ... }
}
*bold*, _underline_).0 for success, non-zero for failures (e.g., 1 for general errors).#[Option(mode: Option::VALUE_REQUIRED)] for mandatory options.php bin/console.php <command>.// app/Console/Kernel.php
protected $commands = [
\AutoShell\Console::class,
];
#[Option] with mode: Option::VALUE_REQUIRED for mandatory config flags.php bin/console.php deploy --env=production --dry-run.Hello = hello), but class names must match PSR-4.namespace and directory in Console::new() are correct.-v) in multiple Options classes throws OptionAlreadyDefined.-global-verbose).bool options default to true if the flag is present (e.g., -v → true).default: false if you want explicit flags only.array type hints use str_getcsv(), which splits on commas. For custom delimiters, pre-process arguments.stdout/stderr callbacks bypass default help formatting.AutoShell\Console to debug mode (not built-in, but you can extend it):
$console = Console::new(..., debug: true);
error_log() in the Console class to trace parsing.namespace and directory paths are correct.composer dump-autoload).php bin/console.php --help to validate help text.$_SERVER['argv'] directly in a test script.AutoShell\Option to support additional modes (e.g., VALUE_CONDITIONAL).AutoShell\TypeCaster to add custom type handling (e.g., DateTime).HelpRenderer.factory callback to wrap commands in middleware (e.g., logging, auth).
factory: fn(string $class) => new CommandWrapper(new $class()),
make).app:deploy → App\Cli\Command\Deploy).AutoShell\Console to the container if using dependency injection:
$this->app->singleton(\AutoShell\Console::class, fn($app) => Console::new(
namespace: 'App\\Cli\\Command',
directory: __DIR__ . '/../src/Cli/Command',
factory: $app->get('container'),
));
namespace/directory in config/console.php for easy updates:
return [
'namespace' => env('CLI_NAMESPACE', 'App\\Cli\\Command'),
'directory' => __DIR__ . '/../src/Cli/Command',
];
Exec objects or use a compiled class map.Shell class that pre-loads commands.Options constructor if possible.$_SERVER['argv'] and test Console directly:
$_SERVER['argv'] = ['console', 'hello', 'world'];
$exitCode = $console($_SERVER['argv']);
$this->assertEquals(0, $exitCode);
symfony/process to simulate CLI calls:
$process = new Process(['php', 'bin/console.php', 'hello', 'world']);
$process->run();
$this->assertStringContainsString('Hello, world!', $process->getOutput());
How can I help you explore Laravel packages today?