]> BookStack Code Mirror - system-cli/blob - scripts/Services/ProgramRunner.php
Extracted program running to its own class
[system-cli] / scripts / Services / ProgramRunner.php
1 <?php
2
3 namespace Cli\Services;
4
5 use Symfony\Component\Process\ExecutableFinder;
6 use Symfony\Component\Process\Process;
7
8 class ProgramRunner
9 {
10     protected int $timeout = 240;
11     protected int $idleTimeout = 15;
12     protected array $environment = [];
13
14     public function __construct(
15         protected string $program,
16         protected string $defaultPath
17     ) {
18     }
19
20     public function withTimeout(int $timeoutSeconds): static
21     {
22         $this->timeout = $timeoutSeconds;
23         return $this;
24     }
25
26     public function withIdleTimeout(int $idleTimeoutSeconds): static
27     {
28         $this->idleTimeout = $idleTimeoutSeconds;
29         return $this;
30     }
31
32     public function withEnvironment(array $environment): static
33     {
34         $this->environment = $environment;
35         return $this;
36     }
37
38     public function runCapturingAllOutput(array $args): string
39     {
40         $output = '';
41         $callable = function ($data) use (&$output) {
42             $output .= $data . "\n";
43         };
44
45         $this->runWithoutOutputCallbacks($args, $callable, $callable);
46         return $output;
47     }
48
49     public function runCapturingStdErr(array $args): string
50     {
51         $err = '';
52         $this->runWithoutOutputCallbacks($args, fn() => '', function ($data) use (&$err) {
53             $err .= $data . "\n";
54         });
55         return $err;
56     }
57
58     public function runWithoutOutputCallbacks(array $args, callable $stdOutCallback, callable $stdErrCallback): void
59     {
60         $process = $this->startProcess($args);
61         foreach ($process as $type => $data) {
62             if ($type === $process::ERR) {
63                 $stdErrCallback($data);
64             } else {
65                 $stdOutCallback($data);
66             }
67         }
68     }
69
70     protected function startProcess(array $args): Process
71     {
72         $programPath = $this->resolveProgramPath();
73         $process = new Process([$programPath, ...$args], null, $this->environment);
74         $process->setTimeout($this->timeout);
75         $process->setIdleTimeout($this->idleTimeout);
76         $process->start();
77         return $process;
78     }
79
80     protected function resolveProgramPath(): string
81     {
82         $executableFinder = new ExecutableFinder();
83         $path = $executableFinder->find($this->program, $this->defaultPath);
84
85         if (is_null($path) || !is_file($path)) {
86             throw new \Exception("Could not locate \"{$this->program}\" program.");
87         }
88
89         return $path;
90     }
91 }