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 = [];
13 protected array $additionalProgramDirectories = [];
15 public function __construct(
16 protected string $program,
17 protected string $defaultPath
21 public function withTimeout(int $timeoutSeconds): static
23 $this->timeout = $timeoutSeconds;
27 public function withIdleTimeout(int $idleTimeoutSeconds): static
29 $this->idleTimeout = $idleTimeoutSeconds;
33 public function withEnvironment(array $environment): static
35 $this->environment = $environment;
39 public function withAdditionalPathLocation(string $directoryPath): static
41 $this->additionalProgramDirectories[] = $directoryPath;
45 public function runCapturingAllOutput(array $args): string
48 $callable = function ($data) use (&$output) {
49 $output .= $data . "\n";
52 $this->runWithoutOutputCallbacks($args, $callable, $callable);
56 public function runCapturingStdErr(array $args): string
59 $this->runWithoutOutputCallbacks($args, fn() => '', function ($data) use (&$err) {
65 public function runWithoutOutputCallbacks(array $args, callable $stdOutCallback = null, callable $stdErrCallback = null): int
67 $process = $this->startProcess($args);
68 foreach ($process as $type => $data) {
69 if ($type === $process::ERR) {
70 if ($stdErrCallback) {
71 $stdErrCallback($data);
74 if ($stdOutCallback) {
75 $stdOutCallback($data);
80 return $process->getExitCode() ?? 1;
86 public function ensureFound(): void
88 $this->resolveProgramPath();
91 public function isFound(): bool
96 } catch (\Exception $exception) {
101 protected function startProcess(array $args): Process
103 $programPath = $this->resolveProgramPath();
104 $process = new Process([$programPath, ...$args], null, $this->environment);
105 $process->setTimeout($this->timeout);
106 $process->setIdleTimeout($this->idleTimeout);
111 protected function resolveProgramPath(): string
113 $executableFinder = new ExecutableFinder();
114 $path = $executableFinder->find($this->program, $this->defaultPath, $this->additionalProgramDirectories);
116 if (is_null($path) || !is_file($path)) {
117 throw new \Exception("Could not locate \"{$this->program}\" program.");