控制台命令
Symfony 框架通过 bin/console
脚本提供了许多命令 (例如,众所周知的 bin/console cache:clear
命令)。这些命令是使用 Console 组件 创建的。你也可以使用它来创建你自己的命令。
运行命令
每个 Symfony 应用程序都带有一组大量的命令。你可以使用 list
命令来查看应用程序中所有可用的命令
1 2 3 4 5 6 7 8 9 10 11 12 13
$ php bin/console list
...
Available commands:
about Display information about the current project
completion Dump the shell completion script
help Display help for a command
list List commands
assets
assets:install Install bundle's web assets under a public directory
cache
cache:clear Clear the cache
...
注意
list
是默认命令,所以运行 php bin/console
效果相同。
如果你找到了你需要的命令,你可以使用 --help
选项来运行它,以查看该命令的文档
1
$ php bin/console assets:install --help
注意
--help
是 Console 组件的内置全局选项之一,所有命令都可以使用,包括你可以创建的命令。要了解更多关于它们的信息,你可以阅读 本节。
APP_ENV & APP_DEBUG
控制台命令在 .env
文件的 APP_ENV
变量中定义的环境中运行,默认情况下是 dev
。它还读取 APP_DEBUG
值来开启或关闭 "debug" 模式 (默认为 1
,即开启)。
要在另一个环境或调试模式下运行命令,请编辑 APP_ENV
和 APP_DEBUG
的值。你也可以在运行命令时定义这些环境变量,例如
1 2
# clears the cache for the prod environment
$ APP_ENV=prod php bin/console cache:clear
控制台补全
如果你正在使用 Bash、Zsh 或 Fish shell,你可以安装 Symfony 的补全脚本,以便在终端中输入命令时获得自动补全。所有命令都支持名称和选项补全,有些甚至可以补全值。

首先,你必须一次性安装补全脚本。运行 bin/console completion --help
查看你的 shell 的安装说明。
注意
当使用 Bash 时,请确保你已经为你的操作系统安装并设置了 "bash completion" 包 (通常命名为 bash-completion
)。
安装并重启你的终端后,你就完成了补全的设置 (默认情况下,按下 Tab 键)。
提示
许多 PHP 工具都是使用 Symfony Console 组件构建的 (例如 Composer、PHPstan 和 Behat)。如果它们使用的是 5.4 或更高版本,你也可以安装它们的补全脚本来启用控制台补全
1 2
$ php vendor/bin/phpstan completion --help
$ composer completion --help
提示
如果你正在使用 Symfony 本地 Web 服务器,建议使用内置的补全脚本,这将确保在运行控制台补全时使用正确的 PHP 版本和配置。运行 symfony completion --help
查看你的 shell 的安装说明。Symfony CLI 将为 console
和 composer
命令提供补全。
创建命令
命令在扩展 Command 的类中定义。例如,你可能想要一个创建用户的命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// src/Command/CreateUserCommand.php
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
// the name of the command is what users type after "php bin/console"
#[AsCommand(name: 'app:create-user')]
class CreateUserCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
// ... put here the code to create the user
// this method must return an integer number with the "exit status code"
// of the command. You can also use these constants to make code more readable
// return this if there was no problem running the command
// (it's equivalent to returning int(0))
return Command::SUCCESS;
// or return this if some error happened during the execution
// (it's equivalent to returning int(1))
// return Command::FAILURE;
// or return this to indicate incorrect command usage; e.g. invalid options
// or missing arguments (it's equivalent to returning int(2))
// return Command::INVALID
}
}
配置命令
你可以选择性地定义描述、帮助信息和 输入选项和参数,通过覆盖 configure()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// src/Command/CreateUserCommand.php
// ...
class CreateUserCommand extends Command
{
// ...
protected function configure(): void
{
$this
// the command description shown when running "php bin/console list"
->setDescription('Creates a new user.')
// the command help shown when running the command with the "--help" option
->setHelp('This command allows you to create a user...')
;
}
}
提示
使用 #[AsCommand]
属性来定义描述,而不是使用 setDescription()
方法,可以让你在不实例化其类的情况下获取命令描述。这使得 php bin/console list
命令运行得更快。
如果你想让 list
命令始终快速运行,请为其添加 --short
选项 (php bin/console list --short
)。这将避免实例化命令类,但它不会显示任何使用 setDescription()
方法而不是属性来定义命令描述的命令的描述。
configure()
方法在命令构造函数的末尾自动调用。如果你的命令定义了自己的构造函数,请先设置属性,然后调用父构造函数,以使这些属性在 configure()
方法中可用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// ...
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
class CreateUserCommand extends Command
{
// ...
public function __construct(bool $requirePassword = false)
{
// best practices recommend to call the parent constructor first and
// then set your own properties. That wouldn't work in this case
// because configure() needs the properties set in this constructor
$this->requirePassword = $requirePassword;
parent::__construct();
}
protected function configure(): void
{
$this
// ...
->addArgument('password', $this->requirePassword ? InputArgument::REQUIRED : InputArgument::OPTIONAL, 'User password')
;
}
}
注册命令
你可以通过向命令添加 AsCommand
属性来注册该命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// src/Command/CreateUserCommand.php
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
#[AsCommand(
name: 'app:create-user',
description: 'Creates a new user.',
hidden: false,
aliases: ['app:add-user']
)]
class CreateUserCommand extends Command
{
// ...
}
如果你不能使用 PHP 属性,请将命令注册为服务,并使用 console.command
标签标记它。如果你正在使用默认的 services.yaml 配置,这已经为你完成了,这要归功于 自动配置。
运行命令
配置和注册命令后,你可以在终端中运行它
1
$ php bin/console app:create-user
正如你可能期望的那样,这个命令将什么都不做,因为你还没有编写任何逻辑。在 execute()
方法中添加你自己的逻辑。
控制台输出
execute()
方法可以访问输出流,以便向控制台写入消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// ...
protected function execute(InputInterface $input, OutputInterface $output): int
{
// outputs multiple lines to the console (adding "\n" at the end of each line)
$output->writeln([
'User Creator',
'============',
'',
]);
// the value returned by someMethod() can be an iterator (http://php.ac.cn/iterator)
// that generates and returns the messages with the 'yield' PHP keyword
$output->writeln($this->someMethod());
// outputs a message followed by a "\n"
$output->writeln('Whoa!');
// outputs a message without adding a "\n" at the end of the line
$output->write('You are about to ');
$output->write('create a user.');
return Command::SUCCESS;
}
现在,尝试执行该命令
1 2 3 4 5 6
$ php bin/console app:create-user
User Creator
============
Whoa!
You are about to create a user.
输出区域
常规控制台输出可以分为多个独立的区域,称为 "输出区域"。当你需要清除和覆盖输出信息时,创建这些区域中的一个或多个。
区域是使用 ConsoleOutput::section() 方法创建的,该方法返回 ConsoleSectionOutput 的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
// ...
use Symfony\Component\Console\Output\ConsoleOutputInterface;
class MyCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!$output instanceof ConsoleOutputInterface) {
throw new \LogicException('This command accepts only an instance of "ConsoleOutputInterface".');
}
$section1 = $output->section();
$section2 = $output->section();
$section1->writeln('Hello');
$section2->writeln('World!');
sleep(1);
// Output displays "Hello\nWorld!\n"
// overwrite() replaces all the existing section contents with the given content
$section1->overwrite('Goodbye');
sleep(1);
// Output now displays "Goodbye\nWorld!\n"
// clear() deletes all the section contents...
$section2->clear();
sleep(1);
// Output now displays "Goodbye\n"
// ...but you can also delete a given number of lines
// (this example deletes the last two lines of the section)
$section1->clear(2);
sleep(1);
// Output is now completely empty!
// setting the max height of a section will make new lines replace the old ones
$section1->setMaxHeight(2);
$section1->writeln('Line1');
$section1->writeln('Line2');
$section1->writeln('Line3');
return Command::SUCCESS;
}
}
注意
当在区域中显示信息时,会自动追加一个新行。
输出区域允许你以高级方式操作控制台输出,例如 显示多个独立更新的进度条 和 向已渲染的表格追加行。
警告
终端只允许覆盖可见内容,因此在尝试写入/覆盖区域内容时,必须考虑控制台的高度。
控制台输入
使用输入选项或参数将信息传递给命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
use Symfony\Component\Console\Input\InputArgument;
// ...
protected function configure(): void
{
$this
// configure an argument
->addArgument('username', InputArgument::REQUIRED, 'The username of the user.')
// ...
;
}
// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln([
'User Creator',
'============',
'',
]);
// retrieve the argument value using getArgument()
$output->writeln('Username: '.$input->getArgument('username'));
return Command::SUCCESS;
}
现在,你可以将用户名传递给命令
1 2 3 4 5
$ php bin/console app:create-user Wouter
User Creator
============
Username: Wouter
另请参阅
阅读 控制台输入 (参数和选项) 以获取有关控制台选项和参数的更多信息。
从服务容器获取服务
要实际创建一个新用户,该命令必须访问一些服务。由于你的命令已经注册为服务,你可以使用正常的依赖注入。假设你有一个 App\Service\UserManager
服务,你想访问它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// ...
use App\Service\UserManager;
use Symfony\Component\Console\Command\Command;
class CreateUserCommand extends Command
{
public function __construct(
private UserManager $userManager,
){
parent::__construct();
}
// ...
protected function execute(InputInterface $input, OutputInterface $output): int
{
// ...
$this->userManager->create($input->getArgument('username'));
$output->writeln('User successfully generated!');
return Command::SUCCESS;
}
}
命令生命周期
命令有三个生命周期方法,在运行命令时调用
- initialize() (可选)
- 此方法在
interact()
和execute()
方法之前执行。其主要目的是初始化命令方法的其余部分中使用的变量。 - interact() (可选)
- 此方法在
initialize()
之后和execute()
之前执行。其目的是检查是否缺少某些选项/参数,并以交互方式向用户询问这些值。这是你可以询问缺少的必需选项/参数的最后一个位置。此方法在验证输入之前调用。请注意,当命令在没有交互的情况下运行时 (例如,当传递--no-interaction
全局选项标志时),将不会调用它。 - execute() (必需)
- 此方法在
interact()
和initialize()
之后执行。它包含你希望命令执行的逻辑,并且必须返回一个整数,该整数将用作命令退出状态。
测试命令
Symfony 提供了几个工具来帮助你测试你的命令。最有用的一个是 CommandTester 类。它使用特殊的输入和输出类来简化在没有真实控制台的情况下的测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
// tests/Command/CreateUserCommandTest.php
namespace App\Tests\Command;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Tester\CommandTester;
class CreateUserCommandTest extends KernelTestCase
{
public function testExecute(): void
{
self::bootKernel();
$application = new Application(self::$kernel);
$command = $application->find('app:create-user');
$commandTester = new CommandTester($command);
$commandTester->execute([
// pass arguments to the helper
'username' => 'Wouter',
// prefix the key with two dashes when passing options,
// e.g: '--some-option' => 'option_value',
// use brackets for testing array value,
// e.g: '--some-option' => ['option_value'],
]);
$commandTester->assertCommandIsSuccessful();
// the output of the command in the console
$output = $commandTester->getDisplay();
$this->assertStringContainsString('Username: Wouter', $output);
// ...
}
}
如果你正在使用单命令应用程序,请在其上调用 setAutoExit(false)
以在 CommandTester
中获取命令结果。
提示
你还可以通过使用 ApplicationTester 来测试整个控制台应用程序。
警告
当使用 CommandTester
类测试命令时,不会分派控制台事件。如果你需要测试这些事件,请改用 ApplicationTester。
警告
当使用 ApplicationTester 类测试命令时,不要忘记禁用自动退出标志
1 2 3 4
$application = new Application();
$application->setAutoExit(false);
$tester = new ApplicationTester($application);
警告
当测试 InputOption::VALUE_NONE
命令选项时,你必须将 true
传递给它们
1 2
$commandTester = new CommandTester($command);
$commandTester->execute(['--some-option' => true]);
注意
当在独立项目中使用 Console 组件时,使用 Application 并扩展正常的 \PHPUnit\Framework\TestCase
。
当测试你的命令时,了解你的命令如何对不同的设置 (如终端的宽度和高度,甚至正在使用的颜色模式) 做出反应可能很有用。你可以通过 Terminal 类访问此类信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use Symfony\Component\Console\Terminal;
$terminal = new Terminal();
// gets the number of lines available
$height = $terminal->getHeight();
// gets the number of columns available
$width = $terminal->getWidth();
// gets the color mode
$colorMode = $terminal->getColorMode();
// changes the color mode
$colorMode = $terminal->setColorMode(AnsiColorMode::Ansi24);
记录命令错误
每当在运行命令时抛出异常时,Symfony 都会为其添加日志消息,包括整个失败的命令。此外,Symfony 注册了一个 事件订阅器 来监听 ConsoleEvents::TERMINATE 事件,并在命令未以 0
退出状态结束时添加日志消息。
分析命令性能
Symfony 允许分析任何命令 (包括你自己的命令) 的执行情况。首先,确保启用 调试模式 和 性能分析器。然后,在运行命令时添加 --profile
选项
1
$ php bin/console --profile app:my-command
Symfony 现在将收集有关命令执行的数据,这有助于调试错误或检查其他问题。当命令执行完毕后,可以通过性能分析器的网页访问该分析。
提示
如果你在详细模式下运行命令 (添加 -v
选项),Symfony 将在输出中显示一个指向命令分析的可点击链接 (如果你的终端支持链接)。如果你在调试详细程度 (-vvv
) 下运行它,你还将看到命令消耗的时间和内存。
警告
当分析来自 Messenger 组件的 messenger:consume
命令时,向命令添加 --no-reset
选项,否则你将不会获得任何分析。此外,考虑使用 --limit
选项仅处理少量消息,以使分析在性能分析器中更易于阅读。
了解更多
控制台组件还包含一组 "助手" - 不同的小型工具,能够帮助你完成不同的任务
- Question Helper:交互式地向用户询问信息
- Formatter Helper:自定义输出颜色
- Progress Bar:显示进度条
- Progress Indicator:显示进度指示器
- Table:将表格数据以表格形式显示
- 调试格式化助手:提供在运行外部程序时输出调试信息的功能
- 进程助手:允许使用
DebugFormatterHelper
运行进程 - 光标助手:允许在终端中操作光标