adhocore / cli
PHP的命令行界面库
Requires
- php: >=8.0
Requires (Dev)
- phpunit/phpunit: ^9.0
- dev-main
- v1.7.2
- v1.7.1
- v1.7.0
- v1.6.2
- v1.6.1
- v1.6.0
- v1.5.0
- v1.4.0
- v1.3.1
- v1.3.0
- v1.2.0
- v1.0.1
- v1.0.0
- 0.9.1
- 0.9.0
- 0.8.4
- 0.8.3
- 0.8.2
- 0.8.1
- 0.8.0
- 0.7.3
- 0.7.2
- 0.7.1
- 0.7.0
- 0.6.2
- 0.6.1
- 0.6.0
- 0.5.0
- 0.4.0
- 0.3.3
- 0.3.2
- 0.3.1
- 0.3.0
- 0.2.1
- 0.2.0
- 0.1.1
- 0.1.0
- 0.0.9
- 0.0.8
- 0.0.7
- 0.0.6
- 0.0.5
- 0.0.4
- 0.0.3
- 0.0.2
- 0.0.1
- dev-analysis-Zn6jLn
This package is auto-updated.
Last update: 2024-09-06 05:04:42 UTC
README
PHP的框架无关命令行界面工具和助手。轻松构建控制台应用程序,享受乐趣和爱。
- 命令行应用程序制作简单
- 受nodejs commander (感谢tj) 启发
- 无依赖。
- 适用于PHP7,PHP8,并且非常好
包含内容
IO: 着色器 · 光标操纵器 · 进度条 · 流写入器 · 流读取器
其他: 自动完成
安装
# PHP8.0 and above v1.0.0 composer require adhocore/cli:^v1.0.0 # PHP 7.x composer require adhocore/cli:^v0.9.0
使用方法
Argv解析器
$command = new Ahc\Cli\Input\Command('rmdir', 'Remove dirs'); $command ->version('0.0.1-dev') // Arguments are separated by space // Format: `<name>` for required, `[name]` for optional // `[name:default]` for default value, `[name...]` for variadic (last argument) ->arguments('<dir> [dirs...]') // `-h --help`, `-V --version`, `-v --verbosity` options are already added by default. // Format: `<name>` for required, `[name]` for optional ->option('-s --with-subdir', 'Also delete subdirs (`with` means false by default)') ->option('-e,--no-empty', 'Delete empty (`no` means true by default)') // Specify santitizer/callback as 3rd param, default value as 4th param ->option('-d|--depth [nestlevel]', 'How deep to process subdirs', 'intval', 5) ->parse(['thisfile.php', '-sev', 'dir', 'dir1', 'dir2', '-vv']) // `$_SERVER['argv']` ; // Print all values: print_r($command->values()); /*Array ( [help] => [version] => 0.0.1 [verbosity] => 3 [dir] => dir [dirs] => Array ( [0] => dir1 [1] => dir2 ) [subdir] => true [empty] => false [depth] => 5 )*/ // To get values for options except the default ones (help, version, verbosity) print_r($command->values(false)); // Pick a value by name $command->dir; // dir $command->dirs; // [dir1, dir2] $command->depth; // 5
命令帮助
可以通过手动调用$command->showHelp()
来触发,或者当将-h
或--help
选项传递给$command->parse()
时自动触发。
命令版本
可以通过手动调用$command->showVersion()
来触发,或者当将-V
或--version
选项传递给$command->parse()
时自动触发。
对于上面的示例,输出将是
0.0.1-dev
控制台应用程序
一定要查看adhocore/phint - 使用adhocore/cli
制作的实际控制台应用程序。
这里我们模拟了一个具有有限功能(如add
和checkout
)的git
应用程序。您将看到构建控制台应用程序是多么直观、流畅和有趣!
Git应用程序
$app = new Ahc\Cli\Application('git', '0.0.1'); $app // Register `add` command ->command('add', 'Stage changed files', 'a') // alias a // Set options and arguments for this command ->arguments('<path> [paths...]') ->option('-f --force', 'Force add ignored file', 'boolval', false) ->option('-N --intent-to-add', 'Add content later but index now', 'boolval', false) // Handler for this command: param names should match but order can be anything :) ->action(function ($path, $paths, $force, $intentToAdd) { array_unshift($paths, $path); echo ($intentToAdd ? 'Intent to add ' : 'Add ') . implode(', ', $paths) . ($force ? ' with force' : ''); // If you return integer from here, that will be taken as exit error code }) // Done setting up this command for now, tap() to retreat back so we can add another command ->tap() ->command('checkout', 'Switch branches', 'co') // alias co ->arguments('<branch>') ->option('-b --new-branch', 'Create a new branch and switch to it', false) ->option('-f --force', 'Checkout even if index differs', 'boolval', false) ->action(function ($branch, $newBranch, $force) { echo 'Checkout to ' . ($newBranch ? 'new ' . $branch : $branch) . ($force ? ' with force' : ''); }) ; // Parse only parses input but doesnt invoke action $app->parse(['git', 'add', 'path1', 'path2', 'path3', '-f']); // Handle will do both parse and invoke action. $app->handle(['git', 'add', 'path1', 'path2', 'path3', '-f']); // Will produce: Add path1, path2, path3 with force $app->handle(['git', 'co', '-b', 'master-2', '-f']); // Will produce: Checkout to new master-2 with force
有组织的应用程序
而不是内联命令/操作,我们定义并添加自己的命令(具有interact()
和execute()
)到应用程序
class InitCommand extends Ahc\Cli\Input\Command { public function __construct() { parent::__construct('init', 'Init something'); $this ->argument('<arrg>', 'The Arrg') ->argument('[arg2]', 'The Arg2') ->option('-a --apple', 'The Apple') ->option('-b --ball', 'The ball') // Usage examples: ->usage( // append details or explanation of given example with ` ## ` so they will be uniformly aligned when shown '<bold> init</end> <comment>--apple applet --ball ballon <arggg></end> ## details 1<eol/>' . // $0 will be interpolated to actual command name '<bold> $0</end> <comment>-a applet -b ballon <arggg> [arg2]</end> ## details 2<eol/>' ); } // This method is auto called before `self::execute()` and receives `Interactor $io` instance public function interact(Ahc\Cli\IO\Interactor $io) : void { // Collect missing opts/args if (!$this->apple) { $this->set('apple', $io->prompt('Enter apple')); } if (!$this->ball) { $this->set('ball', $io->prompt('Enter ball')); } // ... } // When app->handle() locates `init` command it automatically calls `execute()` // with correct $ball and $apple values public function execute($ball, $apple) { $io = $this->app()->io(); $io->write('Apple ' . $apple, true); $io->write('Ball ' . $ball, true); // more codes ... // If you return integer from here, that will be taken as exit error code } } class OtherCommand extends Ahc\Cli\Input\Command { public function __construct() { parent::__construct('other', 'Other something'); } public function execute() { $io = $this->app()->io(); $io->write('Other command'); // more codes ... // If you return integer from here, that will be taken as exit error code } } // Init App with name and version $app = new Ahc\Cli\Application('App', 'v0.0.1'); // Add commands with optional aliases` $app->add(new InitCommand, 'i'); $app->add(new OtherCommand, 'o'); // Set logo $app->logo('Ascii art logo of your app'); $app->handle($_SERVER['argv']); // if argv[1] is `i` or `init` it executes InitCommand
分组命令
分组命令在命令列表中一起列出。显式分组命令是可选的。默认情况下,如果命令名称有冒号:
,则其前面的部分被视为组,否则使用*
作为组。
示例:命令名称
app:env
具有默认组app
,命令名称appenv
具有组*
。
// Add grouped commands: $app->group('Configuration', function ($app) { $app->add(new ConfigSetCommand); $app->add(new ConfigListCommand); }); // Alternatively, set group one by one in each commands: $app->add((new ConfigSetCommand)->inGroup('Config')); $app->add((new ConfigListCommand)->inGroup('Config')); ...
异常处理器
设置自定义异常处理程序作为回调。回调接收异常和退出代码。回调可以重新抛出异常或退出程序,或者仅记录异常并执行其他操作。
$app = new Ahc\Cli\Application('App', 'v0.0.1'); $app->add(...); $app->onException(function (Throwable $e, int $exitCode) { // send to sentry // write to logs // optionally, exit with exit code: exit($exitCode); // or optionally rethrow, a rethrown exception is propagated to top layer caller. throw $e; })->handle($argv);
应用程序帮助
可以通过手动调用$app->showHelp()
来触发,或者当将-h
或--help
选项传递给$app->parse()
时自动触发。**注意**:如果您将类似['app', 'cmd', '-h']
的内容传递给$app->parse()
,它将自动立即显示该cmd
的帮助,而不是$app
。
应用程序版本
相同的版本号传递给所有附加命令。因此,您可以在任何命令上触发版本。
Shell
一个非常薄的shell包装器,提供围绕proc_open()
的便利方法。
基本用法
$shell = new Ahc\Cli\Helper\Shell($command = 'php -v', $rawInput = null); // Waits until proc finishes $shell->execute($async = false); // default false echo $shell->getOutput(); // PHP version string (often with zend/opcache info)
高级用法
$shell = new Ahc\Cli\Helper\Shell('php /some/long/running/scipt.php'); // With async flag, doesnt wait for proc to finish! $shell->setOptions($workDir = '/home', $envVars = []) ->execute($async = true) ->isRunning(); // true // Force stop anytime (please check php.net/proc_close) $shell->stop(); // also closes pipes // Force kill anytime (please check php.net/proc_terminate) $shell->kill();
超时
$shell = new Ahc\Cli\Helper\Shell('php /some/long/running/scipt.php'); // Wait for at most 10.5 seconds for proc to finish! // If it doesnt complete by then, throws exception $shell->setOptions($workDir, $envVars, $timeout = 10.5)->execute(); // And if it completes within timeout, you can access the stdout/stderr echo $shell->getOutput(); echo $shell->getErrorOutput();
Cli交互
您可以使用提供的Ahc\Cli\IO\Interactor
执行用户交互,例如打印彩色输出、以编程方式读取用户输入并移动光标。
$interactor = new Ahc\Cli\IO\Interactor; // For mocking io: $interactor = new Ahc\Cli\IO\Interactor($inputPath, $outputPath);
确认
$confirm = $interactor->confirm('Are you happy?', 'n'); // Default: n (no) $confirm // is a boolean ? $interactor->greenBold('You are happy :)', true) // Output green bold text : $interactor->redBold('You are sad :(', true); // Output red bold text
单选
$fruits = ['a' => 'apple', 'b' => 'banana']; $choice = $interactor->choice('Select a fruit', $fruits, 'b'); $interactor->greenBold("You selected: {$fruits[$choice]}", true);
多选
$fruits = ['a' => 'apple', 'b' => 'banana', 'c' => 'cherry']; $choices = $interactor->choices('Select fruit(s)', $fruits, ['b', 'c']); $choices = \array_map(function ($c) use ($fruits) { return $fruits[$c]; }, $choices); $interactor->greenBold('You selected: ' . implode(', ', $choices), true);
无提示输入
$any = $interactor->prompt('Anything', rand(1, 100)); // Random default $interactor->greenBold("Anything is: $any", true);
带验证的提示
$nameValidator = function ($value) { if (\strlen($value) < 5) { throw new \InvalidArgumentException('Name should be atleast 5 chars'); } return $value; }; // No default, Retry 5 more times $name = $interactor->prompt('Name', null, $nameValidator, 5); $interactor->greenBold("The name is: $name", true);
隐藏提示
在Windows平台上,它可能会更改字体样式,这可以通过此方法修复。
$passValidator = function ($pass) { if (\strlen($pass) < 6) { throw new \InvalidArgumentException('Password too short'); } return $pass; }; $pass = $interactor->promptHidden('Password', $passValidator, 2);
IO组件
交互器由Ahc\Cli\Input\Reader
和Ahc\Cli\Output\Writer
组成,而Writer
本身由Ahc\Cli\Output\Color
组成。所有这些组件都可以独立使用。
颜色
颜色看起来很酷!
$color = new Ahc\Cli\Output\Color;
简单用法
echo $color->warn('This is warning'); echo $color->info('This is info'); echo $color->error('This is error'); echo $color->comment('This is comment'); echo $color->ok('This is ok msg');
自定义样式
Ahc\Cli\Output\Color::style('mystyle', [ 'bg' => Ahc\Cli\Output\Color::CYAN, 'fg' => Ahc\Cli\Output\Color::WHITE, 'bold' => 1, // You can experiment with 0, 1, 2, 3 ... as well ]); echo $color->mystyle('My text');
光标
移动光标,删除上一行或下一行,清除屏幕。
$cursor = new Ahc\Cli\Output\Cursor; echo $cursor->up(1) . $cursor->down(2) . $cursor->right(3) . $cursor->left(4) . $cursor->next(0) . $cursor->prev(2); . $cursor->eraseLine() . $cursor->clear() . $cursor->clearUp() . $cursor->clearDown() . $cursor->moveTo(5, 8); // x, y
进度条
轻松将进度条添加到您的输出中
$progress = new Ahc\Cli\Output\ProgressBar(100); for ($i = 0; $i <= 100; $i++) { $progress->current($i); // Simulate something happening usleep(80000); }
您也可以手动前进进度条
$progress = new Ahc\Cli\Output\ProgressBar(100); // Do something $progress->advance(); // Adds 1 to the current progress // Do something $progress->advance(10); // Adds 10 to the current progress // Do something $progress->advance(5, 'Still going.'); // Adds 5, displays a label
您可以覆盖进度条选项以自定义它
$progress = new Ahc\Cli\Output\ProgressBar(100); $progress->option('pointer', '>>'); $progress->option('loader', '▩'); // You can set the progress fluently $progress->option('pointer', '>>')->option('loader', '▩'); // You can also use an associative array to set many options in one time $progress->option([ 'pointer' => '>>', 'loader' => '▩' ]); // Available options +---------------+------------------------------------------------------+---------------+ | Option | Description | Default value | +===============+======================================================+===============+ | pointer | The progress bar head symbol | > | | loader | The loader symbol | = | | color | The color of progress bar | white | | labelColor | The text color of the label | white | | labelPosition | The position of the label (top, bottom, left, right) | bottom | +---------------+------------------------------------------------------+---------------+
Writer
以风格写任何东西。
$writer = new Ahc\Cli\Output\Writer; // All writes are forwarded to STDOUT // But if you specify error, then to STDERR $writer->errorBold('This is error');
输出格式化
您可以调用任何组合的方法:'<colorName>', 'bold', 'bg', 'fg', 'warn', 'info', 'error', 'ok', 'comment'
... 以任何顺序(例如:bgRedFgBlaock
、boldRed
、greenBold
、commentBgPurple
等 ...)
$writer->bold->green->write('It is bold green'); $writer->boldGreen('It is bold green'); // Same as above $writer->comment('This is grayish comment', true); // True indicates append EOL character. $writer->bgPurpleBold('This is white on purple background');
自由风格
一个调用中包含许多颜色:用标签<method>
和</end>
包裹文本。对于NL/EOL,请使用<eol>
或</eol>
或<eol/>
。
非常适合编写长彩色文本,例如命令用法信息。
$writer->colors('<red>This is red</end><eol><bgGreen>This has bg Green</end>');
原始输出
$writer->raw('Enter name: ');
表格
只需传递关联数组的数组。第一个数组的键将被用作标题。标题将自动转换为人类可读的大写单词(ucwords)。
$writer->table([ ['a' => 'apple', 'b-c' => 'ball', 'c_d' => 'cat'], ['a' => 'applet', 'b-c' => 'bee', 'c_d' => 'cute'], ]);
给出类似的东西
+--------+------+------+
| A | B C | C D |
+--------+------+------+
| apple | ball | cat |
| applet | bee | cute |
+--------+------+------+
设计表格外观和感觉
只需传递第二个参数$styles
$writer->table([ ['a' => 'apple', 'b-c' => 'ball', 'c_d' => 'cat'], ['a' => 'applet', 'b-c' => 'bee', 'c_d' => 'cute'], ], [ // for => styleName (anything that you would call in $writer instance) 'head' => 'boldGreen', // For the table heading 'odd' => 'bold', // For the odd rows (1st row is odd, then 3, 5 etc) 'even' => 'comment', // For the even rows (2nd row is even, then 4, 6 etc) ]); // 'head', 'odd', 'even' are all the styles for now // In future we may support styling a column by its name!
内容对齐(显示设置)
如果您想以类似Laravel的方式(通过php artisan about
命令)显示某些配置(例如从您的.env文件),则可以使用justify
方法。
$writer->justify('Environment'); $writer->justify('PHP Version', PHP_VERSION); $writer->justify('App Version', '1.0.0'); $writer->justify('Locale', 'en');
给出类似的东西
Environment ........................................
PHP Version .................................. 8.1.4
App Version .................................. 1.0.0
Locale .......................................... en
您可以使用sep
参数来定义要使用的分隔符。
$writer->justify('Environment', '', ['sep' => '-']); $writer->justify('PHP Version', PHP_VERSION);
给出类似的东西
Environment ----------------------------------------
PHP Version .................................. 8.1.4
此外,您还可以通过此方法的第三个参数定义文本的颜色、背景颜色以及两种文本的厚度。
$writer->justify('Cache Enable', 'true', [ 'first' => ['fg' => Ahc\Cli\Output\Color::CYAN], // style of the key 'second' => ['fg' => Ahc\Cli\Output\Color::GREEN], // style of the value ]); $writer->justify('Debug Mode', 'false', [ 'first' => ['fg' => Ahc\Cli\Output\Color::CYAN], // style of the key 'second' => ['fg' => Ahc\Cli\Output\Color::RED], // style of the value ]);
有关不同颜色选项的更多详细信息,请参阅自定义样式
Reader
读取和预处理用户输入。
$reader = new Ahc\Cli\Input\Reader; // No default, callback fn `ucwords()` $reader->read(null, 'ucwords'); // Default 'abc', callback `trim()` $reader->read('abc', 'trim'); // Read at most first 5 chars // (if ENTER is pressed before 5 chars then further read is aborted) $reader->read('', 'trim', 5); // Read but dont echo back the input $reader->readHidden($default, $callback); // Read from piped stream (or STDIN) if available without waiting $reader->readPiped(); // Pass in a callback for if STDIN is empty // The callback recieves $reader instance and MUST return string $reader->readPiped(function ($reader) { // Wait to read a line! return $reader->read(); // Wait to read multi lines (until Ctrl+D pressed) return $reader->readAll(); });
异常
每当捕获到异常时,Application::handle()
将显示漂亮的堆栈跟踪并退出状态码非0。
自动完成
任何基于adhocore/cli
构建的命令行应用程序都可以在zsh shell中使用oh-my-zsh进行命令和选项的自动完成。
您只需在~/.oh-my-zsh/custom/plugins/ahccli/ahccli.plugin.zsh
的末尾添加一行即可。
compdef _ahccli <appname>
示例:对于phint,使用compdef _ahccli phint
。
手动执行此操作很麻烦,以下是一个您可以复制/粘贴/运行的完整命令
一次性设置
mkdir -p ~/.oh-my-zsh/custom/plugins/ahccli && cd ~/.oh-my-zsh/custom/plugins/ahccli [ -f ./ahccli.plugin.zsh ] || curl -sSLo ./ahccli.plugin.zsh https://raw.githubusercontent.com/adhocore/php-cli/master/ahccli.plugin.zsh chmod 760 ./ahccli.plugin.zsh && cd -
加载ahccli插件
这也是一次性设置。
# Open .zshrc nano ~/.zshrc # locate plugins=(... ...) and add ahccli plugins=(git ... ... ahccli) # ... then save it (Ctrl + O)
注册应用程序
# replace appname with real name eg: phint echo compdef _ahccli appname >> ~/.oh-my-zsh/custom/plugins/ahccli/ahccli.plugin.zsh
当然,您可以添加多个应用程序,只需更改上述命令中的appname即可。
然后要么重新启动shell,要么像这样source插件
source ~/.oh-my-zsh/custom/plugins/ahccli/ahccli.plugin.zsh
触发自动完成
appname <tab> # autocompletes commands (phint <tab>) appname subcommand <tab> # autocompletes options for subcommand (phint init <tab>)
相关
- adhocore/phalcon-ext · 使用
adhocore/cli
的 Phalcon 扩展 - adhocore/phint · 使用
adhocore/cli
的 PHP 项目脚手架应用 - adhocore/type-hinter · 使用
adhocore/cli
的自动 PHP7 类型提示工具
贡献者
许可证
© 2017-2020, Jitendra Adhikari | MIT
感谢
该项目由 please 管理发布。