ptlis / shell-command
壳命令执行的基本包装器。
Requires
- php: >=7.2.0
- ext-pcntl: *
- psr/log: ^1.0
- react/event-loop: ^0.4.3
- react/promise: ^2.5
Requires (Dev)
- phpunit/phpunit: ^8.0
- dev-master
- 1.3.0
- 1.2.0
- 1.1.0
- 1.0.0
- 0.17.3
- 0.17.2
- 0.17.1
- 0.17.0
- 0.16.6
- 0.16.4
- 0.16.3
- 0.16.2
- 0.16.1
- 0.16.0
- 0.15.0
- 0.14.0
- 0.13.1
- v0.13.0
- v0.12.1
- v0.12.0
- v0.11.5
- v0.11.4
- v0.11.3
- v0.11.2
- v0.11.1
- v0.11.0
- v0.10.2
- v0.10.1
- v0.10.0
- v0.9.0
- v0.8.0
- v0.7.0
- v0.6.0
- v0.5.0
- v0.4.0
- v0.3.0
- v0.2.0
- v0.1.0
- dev-feature/modernize
- dev-feature/deferred-factory
This package is auto-updated.
Last update: 2024-09-12 22:32:35 UTC
README
一个友好的开发者壳命令执行包装器。
以下目标激发了该包的创建
- 使用 命令模式 封装执行壳命令所需的数据,允许命令在以后传递和执行。
- 维护一个无状态的对象图,允许(例如)从单个命令启动多个运行进程。
- 提供同步和异步使用的干净 API。
- 可以将运行进程包装在承诺中,以允许轻松组合。
安装
从终端
$ composer require ptlis/shell-command
使用方法
构建器
该软件包包含一个命令构建器,提供了一种简单、安全的方法来构建命令。
use ptlis\ShellCommand\CommandBuilder; $builder = new CommandBuilder();
构建器将在构造时尝试确定您的环境,您可以通过指定环境作为第一个参数来覆盖此操作
use ptlis\ShellCommand\CommandBuilder; use ptlis\ShellCommand\UnixEnvironment; $builder = new CommandBuilder(new UnixEnvironment());
注意:此构建器是不可变的 - 方法调用必须链接,并以对 buildCommand
的调用结束,如下所示
$command = $builder ->setCommand('foo') ->addArgument('--bar=baz') ->buildCommand()
设置命令
首先,我们必须提供要执行的命令
$builder->setCommand('git') // Executable in $PATH $builder->setCommand('./local/bin/git') // Relative to current working directory $builder->setCommand('/usr/bin/git') // Fully qualified path $build->setCommand('~/.script.sh') // Path relative to $HOME
如果命令不可定位,则抛出 RuntimeException
。
设置进程超时
超时(以微秒为单位)设置库在终止进程之前将等待多长时间。默认为 -1,表示永远不会强制终止。
$builder ->setTimeout(30 * 1000 * 1000) // Wait 30 seconds
如果进程执行时间超过此值,则发送 SIGTERM;如果进程在此之后进一步等待 1 秒后仍未终止,则发送 SIGKILL。
设置轮询超时
设置轮询进程状态之间的等待时间(以微秒为单位)。默认为 1,000,000(1 秒)。
$builder ->setPollTimeout(30 * 1000 * 1000) // Wait 30 seconds
设置工作目录
您可以设置命令的工作目录
$builder ->setCwd('/path/to/working/directory/')
添加参数
添加参数以调用命令(所有参数均被转义)
$builder ->addArgument('--foo=bar')
有条件地添加,取决于表达式的结果
$builder ->addArgument('--foo=bar', $myVar === 5)
添加多个参数
$builder ->addArguments([ '--foo=bar', '-xzcf', 'if=/dev/sda of=/dev/sdb' ])
有条件地添加,取决于表达式的结果
$builder ->addArguments([ '--foo=bar', '-xzcf', 'if=/dev/sda of=/dev/sdb' ], $myVar === 5)
注意:转义和原始参数按添加到构建器的顺序添加到命令中。这适用于对参数顺序敏感的命令。
添加原始参数
警告:不要将这些方法传递用户提供的数据!恶意用户可以轻松执行任意壳命令。
参数也可以在不转义的情况下应用
$builder ->addRawArgument("--foo='bar'")
有条件地,根据表达式的结果
$builder ->addRawArgument('--foo=bar', $myVar === 5)
添加多个原始参数
$builder ->addRawArguments([ "--foo='bar'", '-xzcf', ])
有条件地,根据表达式的结果
$builder ->addRawArguments([ '--foo=bar', '-xzcf', 'if=/dev/sda of=/dev/sdb' ], $myVar === 5)
注意:转义和原始参数按添加到构建器的顺序添加到命令中。这适用于对参数顺序敏感的命令。
添加环境变量
在运行命令时可以设置环境变量
$builder ->addEnvironmentVariable('TEST_VARIABLE', '123')
有条件地,根据表达式的结果
$builder ->addEnvironmentVariable('TEST_VARIABLE', '123', $myVar === 5)
添加多个环境变量
$builder ->addEnvironmentVariables([ 'TEST_VARIABLE' => '123', 'FOO' => 'bar' ])
有条件地,根据表达式的结果
$builder ->addEnvironmentVariables([ 'TEST_VARIABLE' => '123', 'FOO' => 'bar' ], $foo === 5)
添加进程观察者
可以将观察者附加到启动的进程中。在这种情况下,我们添加了一个简单的记录器
$builder ->addProcessObserver( new AllLogger( new DiskLogger(), LogLevel::DEBUG ) )
构建命令
构建器配置完成后,可以检索命令以执行
$command = $builder // ... ->buildCommand();
同步执行
要同步运行命令,请使用 runSynchronous
方法。这将返回实现 CommandResultInterface
的对象,编码了命令的结果。
$result = $command ->runSynchronous();
当您需要多次运行相同的命令时,可以简单地重复调用runSynchronous
;每次调用都会运行命令并将结果返回到您的应用程序。
命令的退出码和输出可以通过此对象上的方法获得
$result->getExitCode(); // 0 for success, anything else conventionally indicates an error $result->getStdOut(); // The contents of stdout (as a string) $result->getStdOutLines(); // The contents of stdout (as an array of lines) $result->getStdErr(); // The contents of stderr (as a string) $result->getStdErrLines(); // The contents of stderr (as an array of lines) $result->getExecutedCommand(); // Get the executed command as a string, including environment variables $result->getWorkingDirectory(); // Get the directory the command was executed in
异步执行
命令也可以异步执行,允许程序在等待结果的同时继续执行。
Command::runAsynchronous
runAsynchronous
方法返回一个实现了ProcessInterface
的对象,该对象提供了监控进程状态的方法。
$process = $command->runAsynchronous();
与同步API一样,当您需要多次运行相同的命令时,可以简单地重复调用runAsynchronous
;每次调用都会运行命令并将表示进程的对象返回到您的应用程序。
进程 API
ProcessInterface
提供了监控和操作进程状态和生命周期所需的方法。
检查进程是否已完成
if (!$process->isRunning()) { echo 'done' . PHP_EOL; }
强制进程停止
$process->stop();
等待进程停止(这将阻塞脚本的执行,实际上使此操作同步)
$process->wait();
获取进程ID(如果进程已结束,则抛出\RuntimeException
)
$process->getPid();
从流中读取输出
$stdOut = $process->readStream(ProcessInterface::STDOUT);
提供输入(例如通过STDIN)
$process->writeInput('Data to pass to the running process via STDIN');
获取退出码(如果进程仍在运行,则抛出\RuntimeException
)
$exitCode = $process->getExitCode();
向进程发送信号(SIGTERM或SIGKILL)
$process->sendSignal(ProcessInterface::SIGTERM);
获取运行中的命令的字符串表示
$commandString = $process->getCommand();
Process::getPromise
可以将shell命令执行的监控封装在ReactPHP Promise中。这为我们提供了一个灵活的执行模型,允许使用Promise::then进行链式调用,以及使用Promise::all、Promise::some、Promise::race及其相关方法进行聚合。
通过从Process
实例调用getPromise
方法来构建执行命令的promise。这将返回一个\React\Promise\Promise
实例。
$eventLoop = \React\EventLoop\Factory::create(); $promise = $command->runAsynchonous()->getPromise($eventLoop);
ReactPHP EventLoop组件用于定期轮询正在运行的过程,以查看它是否已终止;一旦它已经终止,根据执行命令的退出码,promise将被解析或拒绝。
这种实现的效果是,一旦创建了您的promises、chains和aggregates,就必须调用EventLoop::run
$eventLoop->run();
这将阻塞进一步的执行,直到promises被解析/拒绝。
模拟
提供了命令和Builder接口的模拟实现,以帮助测试。
通过针对接口进行类型提示,而不是具体的实现,这些模拟可以注入并用于返回预配置的结果对象。
贡献
您可以通过向问题跟踪器提交问题、改进文档或提交拉取请求来做出贡献。对于拉取请求,我更希望保持代码风格和测试覆盖率,但我很乐意解决可能出现的任何小问题,以便请求可以合并。
已知限制
- 仅支持UNIX环境。