asika / simple-console
One file console framework to help you write build scripts.
Installs: 119 260
Dependents: 0
Suggesters: 0
Security: 0
Stars: 24
Watchers: 3
Forks: 1
Open Issues: 0
Requires
- php: >=8.4
Requires (Dev)
- phpunit/phpunit: ^12.1
- symfony/console: ^7.3@beta
- symfony/process: ^7.3@beta
- symfony/var-dumper: ^7.3@beta
- windwalker/test: ^4.1
- windwalker/utilities: ^4.1
This package is auto-updated.
Last update: 2025-05-23 14:02:53 UTC
README
Single file console framework to help you write scripts quickly, v2.0 requires PHP 8.4 or later.
This package is highly inspired by Symfony Console and Python argparse.
- PHP Simple Console V2.0
Installation
Use composer:
composer require asika/simple-console
Download single file: Download Here
CLI quick download:
# WGET wget https://raw.githubusercontent.com/asika32764/php-simple-console/master/src/Console.php chmod +x Console.php # CURL curl https://raw.githubusercontent.com/asika32764/php-simple-console/master/src/Console.php -o Console.php chmod +x Console.php
Getting Started
Run Console by Closure
Use closure to create a simple console script.
#!/bin/sh php <?php // Include single file include_once __DIR__ . '/Console.php'; // Or use composer include_once __DIR__ . '/vendor/autolod.php'; $app = new \Asika\SimpleConsole\Console(); $app->execute( $argv, function () use ($app) { $this->writeln('Hello'); // OR $app->writeln('Hello'); return $app::SUCCESS; // OR `0` as success } );
The closure can receive a Console
object as parameter, so you can use $this
or $app
to access the console object.
$app->execute( $argv, function (Console $app) { $app->writeln('Hello'); // ... } );
The $argv
can be omit, Console will get it from $_SERVER['argv']
$app->execute( main: function (Console $app) { $app->writeln('Hello'); // ... } );
Run Console by Custom Class
You can also use class mode, extends Asika\SimpleConsole\Console
class to create a console application.
$app = new class () extends \Asika\SimpleConsole\Console { protected function doExecute(): int|bool { $this->writeln('Hello'); return static::SUCCESS; } } $app->execute(); // OR $app->execute($argv);
The Return Value
You can return true
or 0
as success, false
or any int larger than 0
as failure. Please refer to
GNU/Linux Exit Codes.
Simple Console provides constants for success and failure:
retrun $app::SUCCESS; // 0 retrun $app::FAILURE; // 255
Parameter Parser
Simple Console supports arguments and options pre-defined. Below is a parser object to help use define parameters and
parse argv
variables. You can pass the parsed data to any function or entry point.
use Asika\SimpleConsole\Console; function main(array $options) { // Run your code... } $parser = \Asika\SimpleConsole\Console::createArgvParser(); // Arguments $parser->addParameter('name', type: Console::STRING, description: 'Your name', required: true); $parser->addParameter('age', type: Console::INT, description: 'Your age'); // Name starts with `-` or `--` will be treated as option $parser->addParameter('--height|-h', type: Console::FLOAT, description: 'Your height', required: true); $parser->addParameter('--location|-l', type: Console::STRING, description: 'Live location', required: true); $parser->addParameter('--muted|-m', type: Console::BOOLEAN, description: 'Is muted'); main($parser->parse($argv));
Same as:
use Asika\SimpleConsole\ArgvParser; use Asika\SimpleConsole\Console; $params = Console::parseArgv( function (ArgvParser $parser) { // Arguments $parser->addParameter('name', type: Console::STRING, description: 'Your name', required: true); $parser->addParameter('age', type: Console::INT, description: 'Your age'); // Name starts with `-` or `--` will be treated as option $parser->addParameter('--height|-h', type: Console::FLOAT, description: 'Your height', required: true); $parser->addParameter('--location|-l', type: Console::STRING, description: 'Live location', required: true); $parser->addParameter('--muted|-m', type: Console::BOOLEAN, description: 'Is muted'); }, // pass $argv or leave empty ); main($params);
Parameter Definitions
After upgraded to 2.0, the parameter defining is required for Console
app, if a provided argument or option is not
exists
in pre-defined parameters, it will raise an error.
// me.php $app = new Console(); // Arguments $app->addParameter('name', type: $app::STRING, description: 'Your name', required: true); $app->addParameter('age', type: $app::INT, description: 'Your age'); // Name starts with `-` or `--` will be treated as option $app->addParameter('--height', type: $app::FLOAT, description: 'Your height', required: true); $app->addParameter('--location|-l', type: $app::STRING, description: 'Live location', required: true); $app->addParameter('--muted|-m', type: $app::BOOLEAN, description: 'Is muted'); $app->execute( argv: $argv, main: function () use ($app) { $app->writeln('Hello'); $app->writeln('Name: ' . $app->get('name')); $app->writeln('Age: ' . $app->get('age')); $app->writeln('Height: ' . $app->get('height')); $app->writeln('Location: ' . $app->get('location')); $app->writeln('Muted: ' . $app->get('muted') ? 'Y' : 'N'); return $app::SUCCESS; } );
Also same as:
// me.php $app = new class () extends Console { protected function configure(): void { // Arguments $this->addParameter('name', type: $this::STRING, description: 'Your name', required: true); $this->addParameter('age', type: $this::INT, description: 'Your age'); // Name starts with `-` or `--` will be treated as option $this->addParameter('--height', type: $this::FLOAT, description: 'Your height', required: true); $this->addParameter('--location|-l', type: $this::STRING, description: 'Live location', required: true); $this->addParameter('--muted|-m', type: $this::BOOLEAN, description: 'Is muted'); } protected function doExecute(): int|bool { $this->writeln('Hello'); $this->writeln('Name: ' . $this->get('name')); $this->writeln('Age: ' . $this->get('age')); $this->writeln('Height: ' . $this->get('height')); $this->writeln('Location: ' . $this->get('location')); $this->writeln('Muted: ' . ($this->get('muted') ? 'Y' : 'N')); return $this::SUCCESS; } }; $app->execute();
Now if we enter
php me.php --name="John Doe " --age=18 --height=1.8 --location=America --muted
It shows:
Hello
Name: John Doe
Age: 25
Height: 1.8
Location: America
Muted: Y
Then, if we enter wrong parameters, Simple Console will throw errors:
php me.php # [Warning] Required argument "name" is missing. php me.php Simon eighteen # [Warning] Invalid value type for "age". Expected INT. php me.php Simon 18 foo bar # [Warning] Unknown argument "foo". php me.php Simon 18 --nonexists # [Warning] The "-nonexists" option does not exist. php me.php Simon 18 --location # [Warning] Required value for "location" is missing. php me.php Simon 18 --muted=ON # [Warning] Option "muted" does not accept value. php me.php Simon 18 --height one-point-eight # [Warning] Invalid value type for "height". Expected FLOAT.
Show Help
Simple Console supports to describe arguments/options information which follows docopt standards.
Add --help
or -h
to Console App:
php me.php --help
Will print the help information:
Usage:
me.php [options] [--] <name> [<age>]
Arguments:
name Your name
age Your age
Options:
--height=HEIGHT Your height
-l, --location=LOCATION Live location
-m, --muted Is muted
-h, --help Show description of all parameters
-v, --verbosity The verbosity level of the output
Add your heading/epilog and command name:
// Use constructor $app = new \Asika\SimpleConsole\Console( heading: <<<HEADER [Console] SHOW ME - v1.0 This command can show personal information. HEADER, epilog: <<<EPILOG $ show-me.php John 18 $ show-me.php John 18 --location=Europe --height 1.75 ...more please see https://show-me.example EPILOG, commandName: 'show-me.php' ); // Or set properties $app->heading = <<<HEADER [Console] SHOW ME - v1.0 This command can show personal information. HEADER; $app->commandName = 'show-me.php'; // If not provided, will auto use script file name $app->epilog = <<<EPILOG $ show-me.php John 18 $ show-me.php John 18 --location=Europe --height 1.75 ...more please see https://show-me.example EPILOG; $app->execute();
The result:
[Console] SHOW ME - v1.0
This command can show personal information.
Usage:
show-me.php [options] [--] <name> [<age>]
Arguments:
name Your name
age Your age
Options:
--height=HEIGHT Your height
-l, --location=LOCATION Live location
-m, --muted Is muted
-h, --help Show description of all parameters
-v, --verbosity The verbosity level of the output
Help:
$ show-me.php John 18
$ show-me.php John 18 --location=Europe --height 1.75
...more please see https://show-me.example
Override Help Information
If your are using class extending, you may override showHelp()
method to add your own help information.
$app = new class () extends Console { public function showHelp(): void { $this->writeln( <<<HELP My script v1.0 Options: -h, --help Show this help message -q, --quiet Suppress output -l, --location Your location HELP ); }
Parameter Configurations
To define parameters, you can use addParameter()
method.
The parameter name without -
and --
will be treated as argument.
// Function style $app->addParameter('name', type: $app::STRING, description: 'Your name', required: true); // Chaining style $app->addParameter('name', type: $app::STRING) ->description('Your name') ->required(true) ->default('John Doe');
The parameter name starts with -
and --
will be treated as options, you can use |
to separate primary name
and shortcuts.
$app->addParameter('--foo', type: $app::STRING, description: 'Foo description'); $app->addParameter('--muted|-m', type: $app::BOOLEAN, description: 'Muted description');
Arguments' name cannot same as options' name, otherwise it will throw an error.
Get Parameters Value
To get parameters' value, you can use get()
method, all values will cast to the type which you defined.
$name = $app->get('name'); // String $height = $app->get('height'); // Int $muted = $app->get('muted'); // Bool
Array access also works:
$name = $app['name']; $height = $app['height'];
If a parameter is not provided, it will return FALSE
, and if a parameter provided but has no value, it will
return as NULL
.
php console.php # `dir` is FALSE php console.php --dir # `dir` is NULL php console.php --dir /path/to/dir # `dir` is `/path/to/dir`
So you can easily detect the parameter existence and give a default value.
if (($dir = $app['dir']) !== false) { $dir ??= '/path/to/default'; }
Parameters Type
You can define the type of parameters, it will auto convert to the type you defined.
Type | Argument | Option | Description |
---|---|---|---|
STRING | String type | String type | |
INT | Integer type | Integer type | |
FLOAT | Float type | Flot type | Can be int or float, will all converts to float |
NUMERIC | Int or Float | Int or Float | Can be int or float, will all converts to float |
BOOLEAN | (X) | Add --opt as TRUE |
Use negatable to supports --opt as TRUE and --no-opt as FALSE |
ARRAY | Array, must be last argument | Array | Can provide multiple as string[] |
LEVEL | (X) | Int type | Can provide multiple times and convert the times to int |
All parameter values parsed from argv
is default as string
type, and convert to the type you defined.
ARRAY
type
The ARRAY
can be use to arguments and options.
If you set an argument as ARRAY
type, it must be last argument, and you can add more tailing arguments.
$app->addParameter('name', $app::STRING); $app->addParameter('tag', $app::ARRAY); // Run: console.php foo a b c d e $app->get('tag'); // [a, b, c ,d, e]
Use --
to escape all following options, all will be treated as arguments, it is useful if you are
writing a proxy script.
php listen.php --timeout 500 --wait 100 -- php flower.php hello --name=sakura --location Japan --muted // The last argument values will be: // ['php', 'flower.php', 'hello', '--name=sakura', '--location', 'Japan', '--muted']
If you set an option as ARRAY
type, it can be used as --tag a --tag b --tag c
.
$app->addParameter('--tag|-t', $app::ARRAY); $app->get('tag'); // [a, b, c]
LEVEL
type
The LEVEL
type is a special type, it will convert the times to int. For example, a verbosity level of -vvv
will be
converted to 3
,
and -v
will be converted to 1
. You can use this type to define the verbosity level of your argv parser.
$parser->addParameter('--verbosity|-v', type: $app::LEVEL, description: 'The verbosity level of the output');
If you are using Console
class, the verbosity is built-in option, you don't need to define it again.
Parameters Options
description
- Argument: Description for the argument, will be shown in help information.
- Option: Description for the option, will be shown in help information.
required
- Argument: Required argument must be provided, otherwise it will throw an error.
- You should not set a required argument after an optional argument.
- Option: All options are
optional
.- If you set an option as
required
, it means this option requires a value, only--option
without value is not allowed. boolean
option should not be required.
- If you set an option as
default
- Argument: Default value for the argument.
- If not provided, it will be
null
,false
for boolean type, or[]
for array type.
- If not provided, it will be
- Option: Default value for the option.
- If not provided, it will be
null
,false
for boolean type, or[]
for array type.
- If not provided, it will be
negatable
- Argument: Argument cannot use this option.
- Option: Negatable option. Should work with
boolean
type.- If set to
true
, it will support--xxx|--no-xxx
2 styles to settrue|false
. - If you want to set a boolean option's default as
TRUE
and use--no-xxx
to set it asFALSE
, you can do this:
$app->addParameter('--muted|-m', $app::BOOLEAN, default: true, negatable: true);
- If set to
Parameters Parsing
Simple Console follows docopt style to parse parameters.
- Long options starts with
--
- Option shortcut starts with
-
- If an option
-a
requires value,-abc
will parse as$a = bc
- If option
-a
dose not require value,-abc
will parse as$a = true, $b = true, $c = true
- Long options supports
=
while shortcuts are not. The--foo=bar
is valid and-f=bar
is invalid, you should use-f bar
. - Add
--
to escape all following options, all will be treated as arguments.
Error Handling
Just throw Exception in doExecute()
, Console will auto catch error.
throw new \RuntimeException('An error occurred');
If Console app receive an Throwable or Exception, it will render an [ERROR]
message:
[Error] An error occurred.
Add -v
to show backtrace if error.
[Error] An error occurred.
[Backtrace]:
#0 /path/to/Console.php(145): Asika\SimpleConsole\Console@anonymous->doExecute()
#1 /path/to/test.php(36): Asika\SimpleConsole\Console->execute()
#2 {main}
Wrong Parameters
If you provide wrong parameters, Console will render a [WARNING]
message with synopsis:
[Warning] Invalid value type for "age". Expected INT.
test.php [-h|--help] [-v|--verbosity] [--height HEIGHT] [-l|--location LOCATION] [-m|--muted] [--] <name> [<age>]
You can manually raise this [WARNING]
by throwing InvalidParameterException
:
if ($app->get('age') < 18) { throw new \Asika\SimpleConsole\InvalidParameterException('Age must greater than 18.'); }
Verbosity
You can set verbosity by option -v
php console.php # verbosity: 0 php console.php -v # verbosity: 1 php console.php -vv # verbosity: 2 php console.php -vvv # verbosity: 3
or ser it manually in PHP:
$app->verbosity = 3;
If verbosity
is larger than 0
, it will show backtrace in exception output.
You can show your own debug information on different verbosity:
if ($app->verbosity > 2) { $app->writeln($debugInfo); }
The Built-In Options
The --help|-h
and --verbosity|-v
options are built-in options if you use Console
app.
$app = new \Asika\SimpleConsole\Console(); // add parameters... $app->execute(); // You can use built-in `-h` and `-v` options
If you parse argv
by ArgvParser
, you must add it manually. To avoid the required parameters error,
you can set validate
to false
when parsing. Then validate and cast parameters after parsing and help
content display.
$parser->addParameter( '--help|-h', static::BOOLEAN, 'Show description of all parameters', default: false ); $parser->addParameter( '--verbosity|-v', static::LEVEL, 'The verbosity level of the output', ); // Add other parameters... /** @var \Asika\SimpleConsole\ArgvParser $parser */ $params = $parser->parse($argv, validate: false); if ($params['help'] !== false) { echo \Asika\SimpleConsole\ParameterDescriptor::describe($parser, 'command.php'); exit(0); } // Now we can validate and cast params $params = $parser->validateAndCastParams($params); main($params);
Disable Built-In Options for Console App
If you don't want to use built-in options for Console App, you can set disableDefaultParameters
to true
:
$app = new \Asika\SimpleConsole\Console(); $app->disableDefaultParameters = true; // Add it manually $app->addParameter('--help|-h', $app::BOOLEAN, default: false); // Set verbosity $app->verbosity = (int) env('DEBUG_LEVEL'); $app->execute( main: function (\Asika\SimpleConsole\Console $app) { if ($app->get('help')) { $this->showHelp(); return 0; } // ... } );
Input/Output
STDIN/STDOUT/STDERR
Simple Console supports to read from STDIN and write to STDOUT/STDERR. The default stream can be set at constructor.
new Console( stdout: STDOUT, stderr: STDERR, stdin: STDIN );
If you want to catch the output, you can set stdout
to a file pointer or a stream.
$fp = fopen('php://memory', 'r+'); $app = new Console(stdout: $fp); $app->execute(); rewind($fp); echo stream_get_contents($fp);
Output Methods
To output messages, you can use these methods:
write(string $message, bool $err = false)
: Write to STDOUT or STDERRwriteln(string $message, bool $err = false)
: Write to STDOUT or STDERR with a new linenewLine(int $lines, bool $err = false)
: Write empty new lines to STDOUT or STDERR
Input and Asking Questions
To input data, you can use in()
methods:
// This will wait user enter text... $app->write('Please enter something: '); $ans = $app->in();
Use ask()
to ask a question, if return empty string, the default value will instead.
$ans = $app->ask('What is your name: ', [$default]);
Use askConfirm()
to ask a question with Y/n
:
$ans = $app->askConfirm('Are you sure you want to do this? [y/N]: '); // Return BOOL // Set default as Yes $ans = $app->askConfirm('Are you sure you want to do this? [Y/n]: ', 'y');
- The
'n', 'no', 'false', 0, '0'
will beFALSE
. - The
'y', 'yes', 'true', 1, '1'
will beTRUE
.
To add your boolean mapping, set values to boolMapping
$app->boolMapping[0] = [...]; // Falsy values $app->boolMapping[1] = [...]; // Truly values
Run Sub-Process
Use exec()
to run a sub-process, it will instantly print the output and return the result code of the command.
$app->exec('ls'); $app->exec('git status'); $app->exec('git commit ...'); $result = $app->exec('git push'); // All output will instantly print to STDOUT if ($result->code !== 0) { // Failure } $result->code; // 0 is success $result->success; // BOOL
Use mustExec()
to make sure a sub-process should run success, otherwise it will throw an exception.
try { $this->mustExec('...'); } catch (\RuntimeException $e) { // }
Hide Command Name
Bt default, exec()
and mustExec()
will show the command name before executing, for example.
>> git show
...
>> git commit -am ""
...
>> git push
...
if you want to hide it, set the arg: showCmd
to false
.
$app->exec('cmd...', showCmd: false);
Custom Output
Simple Console use proc_open()
to run sub-process, so you can set your own output stream by callback.
$log = ''; $app->exec( 'cmd ...', output: function (string $data, bool $err) use ($app, &$log) { $app->write($data, $err); $log .= $data; } );
Disable the Output
Use false
to disable the output, you can get full output from result object after sub-process finished.
Note, the output will only write to result object if output
set to false
. If you set output
as closure or
keep default NULL
, the output will be empty in result object.
$result = $app->exec('cmd ...', output: false); $result->output; // StdOutput of sub-process $result->errOutput; // StdErr Output of sub-process // Below will not write to the result object $result = $app->exec('cmd ...'); // OR $result = $app->exec('cmd ...', output: function () { ... }); $result->output; // Empty $result->errOutput; // Empty
Override exec()
By now, running sub-process by prop_open()
is in BETA, if prop_open()
not work for your environment, simply override
exec()
to use PHP system()
instead.
public function exec(string $cmd, \Closure|null $output = null, bool $showCmd = true): ExecResult { !$showCmd || $this->writeln('>> ' . $cmd); $returnLine = system($cmd, $code); return new \Asika\SimpleConsole\ExecResult($code, $returnLine, $returnLine); }
Delegating Multiple Tasks
If your script has multiple tasks, for example, the build script contains configure|make|clear
etc...
Here is an example to show how to delegate multiple tasks and pass the necessary params to method interface.
$app = new class () extends Console { protected function configure(): void { $this->addParameter('task', type: $this::STRING, description: 'Task (configure|build|make|move|clear)', required: true); $this->addParameter('--lib-path', type: $this::STRING); $this->addParameter('--temp-path', type: $this::STRING); $this->addParameter('--nested', type: $this::STRING); $this->addParameter('--all', type: $this::STRING); } protected function doExecute(): int|bool { $params = []; foreach ($this->params as $k => $v) { // Use any camel case convert library $params[Str::toCamelCase($k)] = $v; } return $this->{$this['task']}(...$params); } // `...$args` is required, otherwise the redundant params will make method calling error protected function build(string $libPath, string $tempPath, ...$args): int { $this->writeln("Building: $libPath | $tempPath"); return 0; } protected function clear(string $nested, string $dir, ...$args): int { $this->writeln("Clearing: $nested | $dir"); return 0; } }; $app->execute();
Now run:
php make.php build --lib-path foo --temp-path bar
# Building foo | bar
Contributing and PR is Welcome
I'm apologize that I'm too busy to fix or handle all issues and reports, but pull-request is very welcome and will speed up the fixing process.