命令行工具 #

一、Console概述 #

1.1 Console组件 #

Symfony Console组件提供强大的命令行工具支持:

text
┌─────────────────────────────────────────────────────┐
│                  Console功能                         │
├─────────────────────────────────────────────────────┤
│  • 自定义命令                                        │
│  • 参数和选项                                        │
│  • 输入输出格式化                                    │
│  • 表格和进度条                                      │
│  • 命令测试                                          │
│  • 定时任务集成                                      │
└─────────────────────────────────────────────────────┘

1.2 内置命令 #

bash
# 查看所有命令
php bin/console list

# 常用命令
php bin/console cache:clear
php bin/console debug:router
php bin/console make:entity
php bin/console doctrine:migrations:migrate

二、创建命令 #

2.1 使用Maker创建 #

bash
php bin/console make:command SendEmailsCommand

# 输出
created: src/Command/SendEmailsCommand.php

2.2 命令定义 #

php
<?php

namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
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\Style\SymfonyStyle;

#[AsCommand(
    name: 'app:send-emails',
    description: '发送邮件',
)]
class SendEmailsCommand extends Command
{
    protected function configure(): void
    {
        $this
            ->addArgument('recipient', InputArgument::REQUIRED, '收件人邮箱')
            ->addArgument('subject', InputArgument::OPTIONAL, '邮件主题', 'No Subject')
            ->addOption('dry-run', 'd', InputOption::VALUE_NONE, '试运行,不实际发送')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        
        $recipient = $input->getArgument('recipient');
        $subject = $input->getArgument('subject');
        $dryRun = $input->getOption('dry-run');

        $io->title('发送邮件');
        
        if ($dryRun) {
            $io->note('试运行模式');
        }

        $io->text([
            "收件人: $recipient",
            "主题: $subject",
        ]);

        if (!$dryRun) {
            // 实际发送邮件
            $io->success('邮件发送成功');
        }

        return Command::SUCCESS;
    }
}

三、参数和选项 #

3.1 参数类型 #

php
<?php

protected function configure(): void
{
    $this
        // 必需参数
        ->addArgument('name', InputArgument::REQUIRED, '用户名')
        
        // 可选参数
        ->addArgument('email', InputArgument::OPTIONAL, '邮箱')
        
        // 数组参数
        ->addArgument('tags', InputArgument::IS_ARRAY, '标签')
        
        // 默认值
        ->addArgument('count', InputArgument::OPTIONAL, '数量', 10)
    ;
}

3.2 选项类型 #

php
<?php

protected function configure(): void
{
    $this
        // 无值选项
        ->addOption('verbose', 'v', InputOption::VALUE_NONE, '详细输出')
        
        // 必需值选项
        ->addOption('format', 'f', InputOption::VALUE_REQUIRED, '输出格式', 'json')
        
        // 可选值选项
        ->addOption('limit', 'l', InputOption::VALUE_OPTIONAL, '限制数量', 100)
        
        // 数组选项
        ->addOption('exclude', 'e', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, '排除项')
        
        // 否定选项
        ->addOption('no-cache', null, InputOption::VALUE_NEGATABLE, '禁用缓存')
    ;
}

3.3 获取输入 #

php
<?php

protected function execute(InputInterface $input, OutputInterface $output): int
{
    // 获取参数
    $name = $input->getArgument('name');
    $tags = $input->getArgument('tags');
    
    // 获取选项
    $verbose = $input->getOption('verbose');
    $format = $input->getOption('format');
    $exclude = $input->getOption('exclude');
    
    return Command::SUCCESS;
}

四、输出格式化 #

4.1 SymfonyStyle #

php
<?php

use Symfony\Component\Console\Style\SymfonyStyle;

protected function execute(InputInterface $input, OutputInterface $output): int
{
    $io = new SymfonyStyle($input, $output);

    // 标题
    $io->title('命令标题');

    // 章节
    $io->section('章节标题');

    // 文本
    $io->text('普通文本');
    $io->text(['文本1', '文本2', '文本3']);

    // 列表
    $io->listing(['项目1', '项目2', '项目3']);

    // 成功消息
    $io->success('操作成功');
    $io->success(['成功1', '成功2']);

    // 错误消息
    $io->error('操作失败');

    // 警告消息
    $io->warning('警告信息');

    // 注意消息
    $io->note('注意信息');

    // 提示消息
    $io->caution('谨慎操作');

    // 定义列表
    $io->definitionList(
        '标题',
        ['键1' => '值1'],
        ['键2' => '值2']
    );

    return Command::SUCCESS;
}

4.2 表格输出 #

php
<?php

protected function execute(InputInterface $input, OutputInterface $output): int
{
    $io = new SymfonyStyle($input, $output);

    $io->table(
        ['ID', '名称', '邮箱'],
        [
            [1, '张三', 'zhangsan@example.com'],
            [2, '李四', 'lisi@example.com'],
            [3, '王五', 'wangwu@example.com'],
        ]
    );

    return Command::SUCCESS;
}

4.3 进度条 #

php
<?php

protected function execute(InputInterface $input, OutputInterface $output): int
{
    $io = new SymfonyStyle($input, $output);

    $items = range(1, 100);

    $io->progressStart(count($items));

    foreach ($items as $item) {
        // 处理项目
        usleep(10000);
        
        $io->progressAdvance();
    }

    $io->progressFinish();

    return Command::SUCCESS;
}

五、交互输入 #

5.1 询问输入 #

php
<?php

protected function execute(InputInterface $input, OutputInterface $output): int
{
    $io = new SymfonyStyle($input, $output);

    // 简单询问
    $name = $io->ask('请输入您的姓名', '默认姓名');

    // 密码询问
    $password = $io->askHidden('请输入密码');

    // 确认询问
    if (!$io->confirm('确定要继续吗?', false)) {
        return Command::FAILURE;
    }

    // 选择询问
    $choice = $io->choice(
        '请选择颜色',
        ['red', 'green', 'blue'],
        'red'
    );

    // 多选询问
    $selected = $io->choice(
        '请选择多个选项',
        ['option1', 'option2', 'option3'],
        '0,1'
    );

    return Command::SUCCESS;
}

六、服务注入 #

6.1 构造函数注入 #

php
<?php

namespace App\Command;

use App\Service\UserService;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
    name: 'app:user:report',
    description: '生成用户报告',
)]
class UserReportCommand extends Command
{
    public function __construct(
        private UserService $userService
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $users = $this->userService->getAllUsers();
        
        // 生成报告
        
        return Command::SUCCESS;
    }
}

七、定时任务 #

7.1 创建定时任务命令 #

php
<?php

namespace App\Command;

use App\Service\CleanupService;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
    name: 'app:cleanup',
    description: '清理过期数据',
)]
class CleanupCommand extends Command
{
    public function __construct(
        private CleanupService $cleanupService
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->cleanupService->cleanupExpiredData();
        
        return Command::SUCCESS;
    }
}

7.2 配置Cron #

bash
# crontab -e

# 每天凌晨2点清理
0 2 * * * cd /var/www/project && php bin/console app:cleanup >> /var/log/cleanup.log 2>&1

# 每小时发送邮件
0 * * * * cd /var/www/project && php bin/console app:send-emails

# 每5分钟检查队列
*/5 * * * * cd /var/www/project && php bin/console app:queue:process

八、命令测试 #

8.1 测试命令 #

php
<?php

namespace App\Tests\Command;

use App\Command\SendEmailsCommand;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Tester\CommandTester;

class SendEmailsCommandTest extends KernelTestCase
{
    public function testExecute(): void
    {
        $kernel = self::bootKernel();
        $application = new Application($kernel);

        $command = $application->find('app:send-emails');
        $commandTester = new CommandTester($command);
        
        $commandTester->execute([
            'recipient' => 'test@example.com',
            'subject' => 'Test Subject',
            '--dry-run' => true,
        ]);

        $commandTester->assertCommandIsSuccessful();

        $output = $commandTester->getDisplay();
        $this->assertStringContainsString('试运行模式', $output);
        $this->assertStringContainsString('test@example.com', $output);
    }
}

九、命令最佳实践 #

9.1 命名规范 #

text
命令命名规范:
├── 使用命名空间:app:, doctrine:, cache:
├── 使用动词开头:send:, create:, delete:
├── 使用连字符分隔:user:report, cache:clear
└── 示例:app:user:report, app:email:send

9.2 设计原则 #

text
命令设计原则:
├── 单一职责:每个命令只做一件事
├── 幂等性:多次执行结果相同
├── 可测试:易于编写测试
├── 详细输出:提供足够的执行信息
└── 错误处理:优雅处理异常情况

十、总结 #

本章学习了:

  • Console组件概述
  • 创建自定义命令
  • 参数和选项
  • 输出格式化
  • 交互输入
  • 服务注入
  • 定时任务
  • 命令测试

下一章将学习 API开发

最后更新:2026-03-28