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 --help
Description:
test command
Usage:
test [options] [--]
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 debug
Help:
123
php test.php test1 --help
Description:
test1 command
Usage:
test1
Arguments:
test test
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 debug
Help:
This command allows you to create a user...
php test.php test1 111
array(2) {
'command' =>
string(5) "test1"
'test' =>
string(3) "111"
}
Console Tool
Usage:
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 debug
Available commands:
completion Dump the shell completion script
help Display help for a command
list List commands
test test command
test1 test1 command
Test1 Command
============