github地址:GitHub - symfony/console: Eases the creation of beautiful and testable command line interfaces
文档地址:The Console Component (Symfony 5.4 Docs)
默认命令list,可以用register注册一个command命令,之后可以设置其他内容,或者设置命令类再加到框架中。
#Symfony\Component\Console\Application
public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN'){$this->name = $name;$this->version = $version;$this->terminal = new Terminal();$this->defaultCommand = 'list';if (\defined('SIGINT') && SignalRegistry::isSupported()) {$this->signalRegistry = new SignalRegistry();$this->signalsToDispatchEvent = [\SIGINT, \SIGTERM, \SIGUSR1, \SIGUSR2];}}
public function register(string $name){return $this->add(new Command($name));}
public function add(Command $command){……$this->commands[$command->getName()] = $command;……}#Symfony\Component\Console\Command\Command
//设置执行代码
public function setCode(callable $code){if ($code instanceof \Closure) {$r = new \ReflectionFunction($code);if (null === $r->getClosureThis()) {set_error_handler(static function () {});try {if ($c = \Closure::bind($code, $this)) {$code = $c;}} finally {restore_error_handler();}}}$this->code = $code;return $this;}
//设置命令名
public function setName(string $name){$this->validateName($name);$this->name = $name;return $this;}
//设置别名public function setAliases(iterable $aliases){$list = [];foreach ($aliases as $alias) {$this->validateName($alias);$list[] = $alias;}$this->aliases = \is_array($aliases) ? $aliases : $list;return $this;}
//设置帮助内容
public function setHelp(string $help){$this->help = $help;return $this;}
//设置是否隐藏
public function setHidden(bool $hidden /* = true */){$this->hidden = $hidden;return $this;}
//设置描述
public function setDescription(string $description){$this->description = $description;return $this;}
#运行文件
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/Test1Command.php';use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;$application = new Application();
$application->register('test')->addArgument('username', InputArgument::REQUIRED, '用户名')->addOption('pwd', "P", InputArgument::REQUIRED, '密码')->setDescription("test command")->setCode(function (InputInterface $input, OutputInterface $output) {return Command::SUCCESS;});
$test1command = new Test1Command();
$application->add($test1command);
$application->run();#Test1Command
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;class Test1Command extends Command
{protected function configure(){$this->setName("test1");$this->setHelp('This command allows you to create a user...');$this->addArgument('test', InputArgument::REQUIRED, 'test');$this->setDescription("test1 command");}protected function execute(InputInterface $input, OutputInterface $output): int{var_dump($input->getArguments());//$listcommand = $this->getApplication()->get('list');//$listcommand->run($input, $output);$input = new ArrayInput(['command' => 'list']);$listcommand = $this->getApplication()->get('list');$listcommand->run($input, $output);$output->writeln(['Test1 Command','============','',]);return Command::SUCCESS;}
}
InputInterface实体类为Symfony\Component\Console\Input\ArgvInput,该类继承Symfony\Component\Console\Input\Input,会校验参数和用于获取参数。
#Symfony\Component\Console\Application
public function run(InputInterface $input = null, OutputInterface $output = null){if (\function_exists('putenv')) {@putenv('LINES=' . $this->terminal->getHeight());@putenv('COLUMNS=' . $this->terminal->getWidth());}if (null === $input) {//默认input$input = new ArgvInput();}if (null === $output) {//默认输出$output = new ConsoleOutput();}……$exitCode = $this->doRun($input, $output);……}
public function doRun(InputInterface $input, OutputInterface $output){$input->bind($this->getDefinition());……$name = $this->getCommandName($input);……if (!$name) {$name = $this->defaultCommand;$definition = $this->getDefinition();$definition->setArguments(array_merge($definition->getArguments(),['command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),]));}……try {$this->runningCommand = null;// the command name MUST be the first element of the input$command = $this->find($name);} catch (\Throwable $e) {……$alternatives = $e->getAlternatives();……$alternative = $alternatives[0];……$command = $this->find($alternative);}if ($command instanceof LazyCommand) {$command = $command->getCommand();}$this->runningCommand = $command;$exitCode = $this->doRunCommand($command, $input, $output);$this->runningCommand = null;return $exitCode;}#Symfony\Component\Console\Exception\CommandNotFoundException
class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface
{private $alternatives;/*** @param string $message Exception message to throw* @param string[] $alternatives List of similar defined names* @param int $code Exception code* @param \Throwable|null $previous Previous exception used for the exception chaining*/public function __construct(string $message, array $alternatives = [], int $code = 0, \Throwable $previous = null){parent::__construct($message, $code, $previous);$this->alternatives = $alternatives;}/*** @return string[]*/public function getAlternatives(){return $this->alternatives;}
}#Symfony\Component\Console\Input\Input
public function __construct(InputDefinition $definition = null){if (null === $definition) {$this->definition = new InputDefinition();} else {//校验选项$this->bind($definition);//校验参数$this->validate();}}
//校验参数
public function validate(){$definition = $this->definition;$givenArguments = $this->arguments;$missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) {return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();});if (\count($missingArguments) > 0) {throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments)));}}
//获取所有参数
public function getArguments(){return array_merge($this->definition->getArgumentDefaults(), $this->arguments);}
//根据变量名获取参数
public function getArgument(string $name){if (!$this->definition->hasArgument($name)) {throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));}return $this->arguments[$name] ?? $this->definition->getArgument($name)->getDefault();}
//设置参数值public function setArgument(string $name, $value){if (!$this->definition->hasArgument($name)) {throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));}$this->arguments[$name] = $value;}
//判断是否有参数
public function hasArgument(string $name){return $this->definition->hasArgument($name);}
//获取全部选项public function getOptions(){return array_merge($this->definition->getOptionDefaults(), $this->options);}
//根据名称获取选项值
public function getOption(string $name){if ($this->definition->hasNegation($name)) {if (null === $value = $this->getOption($this->definition->negationToName($name))) {return $value;}return !$value;}if (!$this->definition->hasOption($name)) {throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));}return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();}
//设置选项值
public function setOption(string $name, $value){if ($this->definition->hasNegation($name)) {$this->options[$this->definition->negationToName($name)] = !$value;return;} elseif (!$this->definition->hasOption($name)) {throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));}$this->options[$name] = $value;}
//判断是否有选项值
public function hasOption(string $name){return $this->definition->hasOption($name) || $this->definition->hasNegation($name);}#Symfony\Component\Console\Input\ArgvInput
protected function parse(){$parseOptions = true;$this->parsed = $this->tokens;while (null !== $token = array_shift($this->parsed)) {$parseOptions = $this->parseToken($token, $parseOptions);}}
protected function parseToken(string $token, bool $parseOptions): bool{if ($parseOptions && '' == $token) {$this->parseArgument($token);} elseif ($parseOptions && '--' == $token) {return false;} elseif ($parseOptions && str_starts_with($token, '--')) {$this->parseLongOption($token);} elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {$this->parseShortOption($token);} else {$this->parseArgument($token);}return $parseOptions;}
private function parseShortOption(string $token){$name = substr($token, 1);if (\strlen($name) > 1) {if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {// an option with a value (with no space)$this->addShortOption($name[0], substr($name, 1));} else {$this->parseShortOptionSet($name);}} else {$this->addShortOption($name, null);}}private function addShortOption(string $shortcut, $value){if (!$this->definition->hasShortcut($shortcut)) {throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));}$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);}
运行
php test.php test 123
php test.php test --helpDescription:test commandUsage:test [options] [--] <username>Arguments:username 用户名Options:-P, --pwd 密码-h, --help Display help for the given command. When no command is given display help for the list command-q, --quiet Do not output any message-V, --version Display this application version--ansi|--no-ansi Force (or disable --no-ansi) ANSI output-n, --no-interaction Do not ask any interactive question-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debugHelp:123
php test.php test1 --helpDescription:test1 commandUsage:test1 <test>Arguments:test testOptions:-h, --help Display help for the given command. When no command is given display help for the list command-q, --quiet Do not output any message-V, --version Display this application version--ansi|--no-ansi Force (or disable --no-ansi) ANSI output-n, --no-interaction Do not ask any interactive question-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debugHelp:This command allows you to create a user...
php test.php test1 111array(2) {'command' =>string(5) "test1"'test' =>string(3) "111"
}
Console ToolUsage:command [options] [arguments]Options:-h, --help Display help for the given command. When no command is given display help for the list command-q, --quiet Do not output any message-V, --version Display this application version--ansi|--no-ansi Force (or disable --no-ansi) ANSI output-n, --no-interaction Do not ask any interactive question-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debugAvailable commands:completion Dump the shell completion scripthelp Display help for a commandlist List commandstest test commandtest1 test1 command
Test1 Command
============