Process组件在子进程(sub-processes)中执行命令。
超高难内容
译注:本文档并非孤立,建议与Console组件一起学习。本文需要较为高深的computer学科背景知识,请学者注意。
安装 ¶
你可以通过下述两种方式安装:
然后,包容vendor/autoload.php文件,以开启Composer提供的自动加载机制。否则,你的程序将无法找到这个Symfony组件的类。
用法 ¶
1
2
3
4
5
6
7
8
9
10
11
12use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
$process = new Process('ls -lsa');
$process->run();
// executes after the command finishes / 命令完成后执行
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
echo $process->getOutput();
本组件负责处理在执行命令行时“不同平台之间的微妙区别”。
getOutput() 方法始终返回“命令的标准输出(standard output)”的全部内容,而 getErrorOutput() 返回的是error output的内容。可以选择
3.1
对一个进程的流化输出(streaming the output)支持,是从Symofny3.1开始引入的。
你也可以使用
1
2
3
4
5
6
7
8
9
10$process = new Process('ls -lsa');
$process->start();
foreach ($process as $type => $data) {
if ($process::OUT === $type) {
echo "\nRead from stdout: ".$data;
} else { // $process::ERR === $type
echo "\nRead from stderr: ".$data;
}
}
mustRun() 方法等同于 run(),除了它将抛出一个
1
2
3
4
5
6
7
8
9
10
11
12use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;
$process = new Process('ls -lsa');
try {
$process->mustRun();
echo $process->getOutput();
} catch (ProcessFailedException $e) {
echo $e->getMessage();
}
获取实时Process输出 ¶
当执行一个长时间运行的命令时(比如同步文件到远程服务器),你可以把反馈信息实时地返给末级用户,这时要传递一个匿名函数给
1
2
3
4
5
6
7
8
9
10use Symfony\Component\Process\Process;
$process = new Process('ls -lsa');
$process->run(function ($type, $buffer) {
if (Process::ERR === $type) {
echo 'ERR > '.$buffer;
} else {
echo 'OUT > '.$buffer;
}
});
运行异步进程 ¶
你也可以启用子进程,并让它异步运行,在你需要的时候,取出output和主进程状态。使用
1
2
3
4
5
6
7
8$process = new Process('ls -lsa');
$process->start();
while ($process->isRunning()) {
// waiting for process to finish / 等待进程完成
}
echo $process->getOutput();
异步启动时,你也可以等待进程结束,然后再做些事情:
1
2
3
4
5
6
7
8
9$process = new Process('ls -lsa');
$process->start();
// ... do other things / 做一些事
$process->wait();
// ... do things after the process has finished
// .. 在进程完成后做一些事
1
2
3
4
5
6
7
8
9
10$process = new Process('ls -lsa');
$process->start();
$process->wait(function ($type, $buffer) {
if (Process::ERR === $type) {
echo 'ERR > '.$buffer;
} else {
echo 'OUT > '.$buffer;
}
});
---流化Process的标准Input---
3.1
对一个进程的input进行流化,是从Symfony 3.1开始支持的。
在进程启动之前,你可以使用
1
2
3$process = new Process('cat');
$process->setInput('foobar');
$process->run();
当input被完全写到子进程的standard input时,对应的pipe将关闭。
为了能对一个“正在运行”的子进程编写standard input,本组件提供了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17$input = new InputStream();
$input->write('foo');
$process = new Process('cat');
$process->setInput($input);
$process->start();
// ... read process output or do other things
// ... 读取进程的output,或者做其他事
$input->write('bar');
$input->close();
$process->wait();
// will echo: foobar / 将执行echo: foobar
echo $process->getOutput();
中止一个进程 ¶
任何一个异步进程都可以通过 SIGKILL。请参考 下文的signal文档 来了解Process组件中关于“信号处理”(signal handling)的更多内容:
1
2
3
4
5
6$process = new Process('ls -lsa');
$process->start();
// ... do other things / 做其他事
$process->stop(3, SIGINT);
孤立执行PHP代码 ¶
如果你希望在孤立状态下执行一些PHP代码,使用 PhpProcess 来替代:
1
2
3
4
5
6
7use Symfony\Component\Process\PhpProcess;
$process = new PhpProcess(<<
EOF
);
$process->run();
要确保代码能更好地运行于全部平台之上,你可能需要使用
1
2
3
4use Symfony\Component\Process\ProcessBuilder;
$builder = new ProcessBuilder(array('ls', '-lsa'));
$builder->getProcess()->run();
如果你在构建一个二进制驱动(binary driver),可以使用
下例将为一个tar binary adapter生成两个进程命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16use Symfony\Component\Process\ProcessBuilder;
$builder = new ProcessBuilder();
$builder->setPrefix('/usr/bin/tar');
// '/usr/bin/tar' '--list' '--file=archive.tar.gz'
echo $builder
->setArguments(array('--list', '--file=archive.tar.gz'))
->getProcess()
->getCommandLine();
// '/usr/bin/tar' '-xzf' 'archive.tar.gz'
echo $builder
->setArguments(array('-xzf', 'archive.tar.gz'))
->getProcess()
->getCommandLine();
Process超时 ¶
你可以限制进程在完成过程中所耗费的时间数,设置一个timeout(以秒计)即可:
1
2
3
4
5use Symfony\Component\Process\Process;
$process = new Process('ls -lsa');
$process->setTimeout(3600);
$process->run();
对于长耗时命令,你的职责就是对超时进行例行检查:
1
2
3
4
5
6
7
8
9
10
11
12$process->setTimeout(3600);
$process->start();
while ($condition) {
// ...
// check if the timeout is reached
// 检查是否到达时限
$process->checkTimeout();
usleep(200000);
}
进程空闲超时 ¶
相对于前面的超时,空间超时(idle timeout)只计算从“上一次output被进程输出”开始的时间:
1
2
3
4
5
6use Symfony\Component\Process\Process;
$process = new Process('something-with-variable-runtime');
$process->setTimeout(3600);
$process->setIdleTimeout(60);
$process->run();
上例如,一个进程将在总体运行时间超过3600秒,或是60秒内进程没有产生任何output时,被认定超时。
进程的信号 ¶
当异步运行一个程序时,你可以发送通过
1
2
3
4
5
6
7
8use Symfony\Component\Process\Process;
$process = new Process('find / -name "rabbit"');
$process->start();
// will send a SIGKILL to the process
// 将发送一个SIGKILL到进程
$process->signal(SIGKILL);
Caution
由于PHP的某些限制,如果你在Process组件中使用signals,你可能不得不对命令施以 exec 前缀。参考 Symfony Issue#5759 和 PHP Bug#39992 以了解为何会如此。
POSIX signals在Windows平台不可用,参考 PHP文档 了解可用的信号。
进程的Pid ¶
你可以访问运行中的进程的 pid,通过
1
2
3
4
5
6use Symfony\Component\Process\Process;
$process = new Process('/usr/bin/php worker.php');
$process->start();
$pid = $process->getPid();
由于PHP的某些限制,如果你要取得一个symfony进程的pid,你可能不得不对自己的命令添加 exec 前缀。参考 Symfony Issue#5759 来理解为何会发生这样的事。
关闭Output ¶
由于standard output和error output总是从背后的进程(underlying process)中被取出,在某些需要节约内存的场景下,关闭output可能更方便。使用
1
2
3
4
5use Symfony\Component\Process\Process;
$process = new Process('/usr/bin/php worker.php');
$process->disableOutput();
$process->run();
进程在运行时,你可以开启或关闭输出。
如果你关闭output,将不能使用 getOutput、getIncrementalOutput、getErrorOutput、getIncrementalErrorOutput 或是 setIdleTimeout。
然而,传入一个callback给 start、run 或 mustRun ,以streaming方式来处理进程的output,是可以的。
3.1
在output被关闭时,“向这些方法传入callback”的能力,从Symofny3.1开始被添加进来。