
Symfony Process 组件是 Symfony 生态系统中最基础、最常用的子进程执行组件。
Symfony Process 组件是一个强大的 PHP 库,用于在 PHP 中执行命令行进程,提供简单易用的 API,可以用于执行系统命令,文件操作和进程管理等任务。无论你是需要运行简单的系统命令还是复杂的进程控制,这个组件都能满足你的需求。
你有没有遇到过这样的情况,在终端里直接运行 composer、rsync 或 Symfony 命令,屏幕上漂亮的彩色进度条一闪而过,但当你通过 PHP 脚本运行同样的命令时,所有颜色和动画瞬间消失?
这正是 Symfony Process 组件的经典问题。要想彻底搞清楚原因(以及如何解决),我们必须回到 Linux 是如何处理流和终端的。别担心,这其实比你想象的简单多了。
在 Linux 下,每个进程默认都有三个标准流,由文件描述符(file descriptors)标识:
这些流就像简单的管道(pipe)。管道另一端连接的东西决定了程序的行为。通常有三种常见情况:
ls > file.txtls | grep php,或者在 PHP 中以编程方式启动子进程默认情况下,Symfony Process 组件启动命令时,使用的是管道(上面第三种情况)。
被执行的程序(以 Composer 为例)非常聪明:它会检查标准输出(STDOUT)另一端连接的是什么。如果检测到是管道而不是终端,它就会自动判断“这是被机器或脚本执行的”,从而关闭彩色输出和进度条,避免污染日志文件(ANSI 转义字符)和让进度条变得无法阅读。
这就是颜色消失的原因。
setTty(true):直接连接终端Process 组件提供了一个解决方案:setTty(true)。
激活它后,你就把子进程的流直接连接到你运行 PHP 脚本的真正终端上。
结果:颜色和动画恢复正常,命令看起来就像你自己手动敲进去一样。
缺点:因为流直接指向你的屏幕,PHP 脚本就无法再捕获输出了。getOutput() 没法用了(自动同步导致立即显示)。
setPty(true) 的魔法:完美的幻觉这就是 Pseudo-Terminal (PTY) 登场的地方。
使用 setPty(true) 时,系统会为你创建一个完全模拟的终端。PTY 由两个部分组成:
/dev/pts/X)对子进程来说,感觉完全是一个真正的终端。它会检测到终端,正常显示颜色和交互式进度条。 而对 PHP 端来说,情况正好相反:不再是自动输出到屏幕(而是缓冲区)。你必须自己读取虚拟终端流。这样,你就同时拥有了两个世界的最佳体验:强制程序保持丰富输出,同时保留在 PHP 代码中直接截取和操作流的能力。
先准备一个支持彩色输出的 Symfony 单命令应用:
console.php 文件
#!/usr/bin/env php
<?php
require__DIR__.'/vendor/autoload.php';
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SingleCommandApplication;
(new SingleCommandApplication())
->setCode(function (OutputInterface $output): int {
$output->writeln('<info>Hello World!</info>');
$output->writeln('<info>开源技术小栈!</info>');
$output->writeln('<comment>This is a single command application.</comment>');
$output->writeln('<error>Goodbye!</error>');
return0;
})->run();
然后用 Process 组件运行它,并通过参数控制 TTY 或 PTY:
run.php 文件
<?php
/**
* @desc run.php 描述信息
* @author Tinywan(ShaoBo Wan)
*/
declare(strict_types=1);
require__DIR__ . '/vendor/autoload.php';
$process = new Symfony\Component\Process\Process(['php', __DIR__ . '/console.php']);
$process->setTty(($argv[1] ?? '') === 'tty');
$process->setPty(($argv[1] ?? '') === 'pty');
$process->mustRun();
echo"\nOutput captured:\n";
dump($process->getOutput());
执行效果请自行验证(颜色、动画是否正常,输出是否可捕获)。

如果你在 PHP 中构建命令行工具或异步 Worker: