3 namespace Cli\Services;
5 use Symfony\Component\Process\ExecutableFinder;
6 use Symfony\Component\Process\Process;
10 protected int $timeout = 240;
11 protected int $idleTimeout = 15;
12 protected array $environment = [];
14 public function __construct(
15 protected string $program,
16 protected string $defaultPath
20 public function withTimeout(int $timeoutSeconds): static
22 $this->timeout = $timeoutSeconds;
26 public function withIdleTimeout(int $idleTimeoutSeconds): static
28 $this->idleTimeout = $idleTimeoutSeconds;
32 public function withEnvironment(array $environment): static
34 $this->environment = $environment;
38 public function runCapturingAllOutput(array $args): string
41 $callable = function ($data) use (&$output) {
42 $output .= $data . "\n";
45 $this->runWithoutOutputCallbacks($args, $callable, $callable);
49 public function runCapturingStdErr(array $args): string
52 $this->runWithoutOutputCallbacks($args, fn() => '', function ($data) use (&$err) {
58 public function runWithoutOutputCallbacks(array $args, callable $stdOutCallback, callable $stdErrCallback): void
60 $process = $this->startProcess($args);
61 foreach ($process as $type => $data) {
62 if ($type === $process::ERR) {
63 $stdErrCallback($data);
65 $stdOutCallback($data);
70 protected function startProcess(array $args): Process
72 $programPath = $this->resolveProgramPath();
73 $process = new Process([$programPath, ...$args], null, $this->environment);
74 $process->setTimeout($this->timeout);
75 $process->setIdleTimeout($this->idleTimeout);
80 protected function resolveProgramPath(): string
82 $executableFinder = new ExecutableFinder();
83 $path = $executableFinder->find($this->program, $this->defaultPath);
85 if (is_null($path) || !is_file($path)) {
86 throw new \Exception("Could not locate \"{$this->program}\" program.");