1<?php 2 3class ProcessResult { 4 public $stdout; 5 public $stderr; 6} 7 8function runCommand(array $args, ?string $cwd = null): ProcessResult { 9 $cmd = implode(' ', array_map('escapeshellarg', $args)); 10 $pipes = null; 11 $result = new ProcessResult(); 12 $descriptorSpec = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; 13 fwrite(STDOUT, "> $cmd\n"); 14 $processHandle = proc_open($cmd, $descriptorSpec, $pipes, $cwd ?? getcwd(), null); 15 16 $stdin = $pipes[0]; 17 $stdout = $pipes[1]; 18 $stderr = $pipes[2]; 19 20 fclose($stdin); 21 22 stream_set_blocking($stdout, false); 23 stream_set_blocking($stderr, false); 24 25 $stdoutEof = false; 26 $stderrEof = false; 27 28 do { 29 $read = [$stdout, $stderr]; 30 $write = null; 31 $except = null; 32 33 stream_select($read, $write, $except, 1, 0); 34 35 foreach ($read as $stream) { 36 $chunk = fgets($stream); 37 if ($stream === $stdout) { 38 $result->stdout .= $chunk; 39 } elseif ($stream === $stderr) { 40 $result->stderr .= $chunk; 41 } 42 } 43 44 $stdoutEof = $stdoutEof || feof($stdout); 45 $stderrEof = $stderrEof || feof($stderr); 46 } while(!$stdoutEof || !$stderrEof); 47 48 fclose($stdout); 49 fclose($stderr); 50 51 $statusCode = proc_close($processHandle); 52 if ($statusCode !== 0) { 53 fwrite(STDOUT, $result->stdout); 54 fwrite(STDERR, $result->stderr); 55 fwrite(STDERR, 'Exited with status code ' . $statusCode . "\n"); 56 exit($statusCode); 57 } 58 59 return $result; 60} 61 62function cloneRepo(string $path, string $url) { 63 if (is_dir($path)) { 64 return; 65 } 66 $dir = dirname($path); 67 $repo = basename($path); 68 if (!is_dir($dir)) { 69 mkdir($dir, 0755, true); 70 } 71 runCommand(['git', 'clone', '-q', '--end-of-options', $url, $repo], dirname($path)); 72} 73