Symfony Console 与输入处理
Symfony Console 组件提供了强大的命令行输入处理能力,以下是主要方式和最佳实践:

基本命令结构
// src/Command/YourCommand.php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class YourCommand extends Command
{
protected static $defaultName = 'app:your-command';
protected function configure(): void
{
$this
->setDescription('命令描述')
->setHelp('命令帮助信息');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
// 处理逻辑
return Command::SUCCESS;
}
}
输入参数定义
1 参数 (Arguments) - 位置参数
protected function configure(): void
{
$this
->addArgument('name', InputArgument::REQUIRED, '用户名称')
->addArgument('age', InputArgument::OPTIONAL, '用户年龄', 18)
->addArgument('tags', InputArgument::IS_ARRAY, '标签列表');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
// 获取参数值
$name = $input->getArgument('name'); // 必须参数
$age = $input->getArgument('age'); // 可选参数,默认18
$tags = $input->getArgument('tags'); // 数组参数
$output->writeln("Name: $name, Age: $age");
$output->writeln('Tags: ' . implode(', ', $tags));
return Command::SUCCESS;
}
使用示例:
php bin/console app:your-command John 25 tag1 tag2 tag3
2 选项 (Options) - 命名参数
protected function configure(): void
{
$this
->addOption('format', 'f', InputOption::VALUE_REQUIRED, '输出格式', 'json')
->addOption('verbose', 'v', InputOption::VALUE_NONE, '详细输出')
->addOption('config', 'c', InputOption::VALUE_OPTIONAL, '配置文件路径')
->addOption('exclude', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, '排除项');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$format = $input->getOption('format'); // 有值选项
$verbose = $input->getOption('verbose'); // 布尔选项
$config = $input->getOption('config'); // 可选值选项
$exclude = $input->getOption('exclude'); // 数组选项
if ($verbose) {
$output->writeln("Format: $format");
}
return Command::SUCCESS;
}
使用示例:
# 基本用法 php bin/console app:your-command --format=yaml -v # 可选值选项(可以没有值) php bin/console app:your-command --config php bin/console app:your-command --config=config.yml # 数组选项(多次使用) php bin/console app:your-command --exclude=foo --exclude=bar
输入验证
1 自定义验证
protected function execute(InputInterface $input, OutputInterface $output): int
{
$email = $input->getArgument('email');
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$output->writeln('<error>无效的邮箱地址</error>');
return Command::FAILURE;
}
return Command::SUCCESS;
}
2 使用选择器验证
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Helper\QuestionHelper;
protected function interact(InputInterface $input, OutputInterface $output): void
{
$helper = $this->getHelper('question');
// 选择题
$question = new ChoiceQuestion(
'选择颜色:',
['red', 'blue', 'green'],
0
);
$question->setErrorMessage('无效选择 %s');
$color = $helper->ask($input, $output, $question);
// 自定义验证
$question = new Question('请输入年龄:', 18);
$question->setValidator(function ($answer) {
if (!is_numeric($answer) || $answer < 0 || $answer > 150) {
throw new \RuntimeException('年龄必须在0-150之间');
}
return (int) $answer;
});
$age = $helper->ask($input, $output, $question);
}
交互式输入
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Helper\QuestionHelper;
protected function execute(InputInterface $input, OutputInterface $output): int
{
$helper = $this->getHelper('question');
// 确认提示
$question = new ConfirmationQuestion('继续操作吗?(y/n) ', false);
if (!$helper->ask($input, $output, $question)) {
return Command::SUCCESS;
}
// 隐藏输入(密码等)
$question = new Question('请输入密码:');
$question->setHidden(true);
$question->setHiddenFallback(false); // 如果不支持隐藏则抛出异常
$password = $helper->ask($input, $output, $question);
// 自动补全
$question = new Question('请输入命令:');
$question->setAutocompleterValues(['list', 'help', 'debug']);
$command = $helper->ask($input, $output, $question);
return Command::SUCCESS;
}
高级输入处理
1 表格输入
use Symfony\Component\Console\Helper\Table;
protected function execute(InputInterface $input, OutputInterface $output): int
{
// 从标准输入读取表格数据
$data = [];
while ($line = fgets(STDIN)) {
$data[] = json_decode(trim($line), true);
}
$table = new Table($output);
$table
->setHeaders(['ID', 'Name', 'Email'])
->setRows($data);
$table->render();
return Command::SUCCESS;
}
2 CSV文件输入
protected function execute(InputInterface $input, OutputInterface $output): int
{
$csvFile = $input->getArgument('csv-file');
if (!file_exists($csvFile)) {
$output->writeln('<error>文件不存在</error>');
return Command::FAILURE;
}
$handle = fopen($csvFile, 'r');
$headers = fgetcsv($handle);
while (($row = fgetcsv($handle)) !== false) {
$data = array_combine($headers, $row);
// 处理数据...
}
fclose($handle);
return Command::SUCCESS;
}
输入样式和格式化
protected function execute(InputInterface $input, OutputInterface $output): int
{
// 格式化输出
$output->writeln('<info>成功信息</info>');
$output->writeln('<error>错误信息</error>');
$output->writeln('<comment>注释信息</comment>');
$output->writeln('<question>问题信息</question>');
// 进度条
$progressBar = new ProgressBar($output, 50);
$progressBar->start();
for ($i = 0; $i < 50; $i++) {
usleep(10000); // 模拟工作
$progressBar->advance();
}
$progressBar->finish();
return Command::SUCCESS;
}
完整的命令示例
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Style\SymfonyStyle;
class UserImportCommand extends Command
{
protected static $defaultName = 'app:user:import';
protected function configure(): void
{
$this
->setDescription('批量导入用户')
->setHelp('从CSV文件导入用户数据')
->addArgument('file', InputArgument::REQUIRED, 'CSV文件路径')
->addOption('dry-run', null, InputOption::VALUE_NONE, '试运行,不实际导入')
->addOption('format', 'f', InputOption::VALUE_REQUIRED, '输出格式', 'table')
->addOption('batch-size', null, InputOption::VALUE_REQUIRED, '批处理大小', 100);
}
protected function interact(InputInterface $input, OutputInterface $output): void
{
$io = new SymfonyStyle($input, $output);
if (!$input->getArgument('file')) {
$file = $io->ask('请输入CSV文件路径');
$input->setArgument('file', $file);
}
if (!$io->confirm('确认导入?', false)) {
throw new \RuntimeException('导入已取消');
}
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$file = $input->getArgument('file');
if (!file_exists($file)) {
$io->error("文件 $file 不存在");
return Command::FAILURE;
}
// 文件处理逻辑
$io->section('开始导入');
$io->progressStart(100);
for ($i = 0; $i < 100; $i++) {
usleep(10000);
$io->progressAdvance();
}
$io->progressFinish();
$io->success('导入完成');
return Command::SUCCESS;
}
}
最佳实践
- 参数验证:始终验证用户输入
- 错误处理:使用适当的返回值(
SUCCESS/FAILURE/INVALID) - 用户反馈:提供清晰的进度指示和结果反馈
- 交互提示:使用问答组件处理复杂输入
- 默认值:为可选参数提供合理的默认值
- 文档:提供详细的帮助文本和示例
Symfony Console 的输入处理非常灵活,可以根据项目需求选择合适的处理方式。