false, 'echo_errors' => null, 'live_callback' => null, ], $options); if ($options['echo_errors'] === null) { $options['echo_errors'] = $options['echo']; } if ($options['live_callback'] && !is_callable($options['live_callback'])) { throw new InvalidArgumentException('live_callback option must be a valid callback'); } $descriptorSpec = [ 1 => ['pipe', 'w'], // stdout 2 => ['pipe', 'w'], // stderr ]; $process = proc_open($cmd, $descriptorSpec, $pipes); if (!is_resource($process)) { throw new RuntimeException('proc_open failed'); } stream_set_blocking($pipes[1], false); stream_set_blocking($pipes[2], false); $output = ''; $err = ''; while (!feof($pipes[1]) || !feof($pipes[2])) { foreach ($pipes as $key => $pipe) { $data = fread($pipe, 1024); if (!$data) { continue; } if (1 == $key) { $output .= $data; if ($options['echo']) { fprintf(STDOUT, "%s", $data); } if ($options['live_callback']) { call_user_func($options['live_callback'], $data, false); } } else { $err .= $data; if ($options['echo_errors']) { fprintf(STDERR, "%s", $data); } if ($options['live_callback']) { call_user_func($options['live_callback'], $data, true); } } } usleep(100000); } fclose($pipes[1]); fclose($pipes[2]); $exitCode = proc_close($process); return [$exitCode, $output, $err]; } /** * Convenience method to build a command to execute. Will escape everything as necessary. * * @param string $executable The path to the executable * @param array $arguments An array of arguments * @param array $options An associative array of flags in the form flagName => flagValue. * Short and long flags are supported. * If flagValue === true, the flag have no value. * If flagValue === false, the flag will be skipped. * * @return string An executable command */ public static function buildCmd($executable, array $arguments = [], array $options = []) { $shellArgs = []; foreach ($options as $key => $value) { if ($value === false) { continue; } if (strlen($key) === 1) { $shellArgs[] = '-'.$key; if ($value !== true) { $shellArgs[] = $value; } } else { $shellArgs[] = '--' . $key . ($value !== true ? '=' . $value : ''); } } $shellArgs = array_merge($shellArgs, array_values($arguments)); return $executable . ' ' . join(' ', array_map('escapeshellarg', $shellArgs)); } }