PHP项目Symfony console与输入

wen PHP项目 3

Symfony Console 与输入处理

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

PHP项目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;
    }
}

最佳实践

  1. 参数验证:始终验证用户输入
  2. 错误处理:使用适当的返回值(SUCCESS/FAILURE/INVALID
  3. 用户反馈:提供清晰的进度指示和结果反馈
  4. 交互提示:使用问答组件处理复杂输入
  5. 默认值:为可选参数提供合理的默认值
  6. 文档:提供详细的帮助文本和示例

Symfony Console 的输入处理非常灵活,可以根据项目需求选择合适的处理方式。

抱歉,评论功能暂时关闭!