annotate vendor/consolidation/site-process/src/ProcessBase.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents
children
rev   line source
Chris@5 1 <?php
Chris@5 2
Chris@5 3 namespace Consolidation\SiteProcess;
Chris@5 4
Chris@5 5 use Psr\Log\LoggerInterface;
Chris@5 6 use Symfony\Component\Console\Style\OutputStyle;
Chris@5 7 use Symfony\Component\Process\Process;
Chris@5 8 use Consolidation\SiteProcess\Util\RealtimeOutputHandler;
Chris@5 9 use Consolidation\SiteProcess\Util\Escape;
Chris@5 10 use Symfony\Component\Console\Output\OutputInterface;
Chris@5 11 use Symfony\Component\Console\Output\ConsoleOutputInterface;
Chris@5 12
Chris@5 13 /**
Chris@5 14 * A wrapper around Symfony Process.
Chris@5 15 *
Chris@5 16 * - Supports simulated mode. Typically enabled via a --simulate option.
Chris@5 17 * - Supports verbose mode - logs all runs.
Chris@5 18 * - Can convert output json data into php array (convenience method)
Chris@5 19 * - Provides a "realtime output" helper
Chris@5 20 */
Chris@5 21 class ProcessBase extends Process
Chris@5 22 {
Chris@5 23 /**
Chris@5 24 * @var OutputStyle
Chris@5 25 */
Chris@5 26 protected $output;
Chris@5 27
Chris@5 28 /**
Chris@5 29 * @var OutputInterface
Chris@5 30 */
Chris@5 31 protected $stderr;
Chris@5 32
Chris@5 33 private $simulated = false;
Chris@5 34
Chris@5 35 private $verbose = false;
Chris@5 36
Chris@5 37 /**
Chris@5 38 * @var LoggerInterface
Chris@5 39 */
Chris@5 40 private $logger;
Chris@5 41
Chris@5 42 /**
Chris@5 43 * Symfony 4 style constructor for creating Process instances from strings.
Chris@5 44 * @param string $command The commandline string to run
Chris@5 45 * @param string|null $cwd The working directory or null to use the working dir of the current PHP process
Chris@5 46 * @param array|null $env The environment variables or null to use the same environment as the current PHP process
Chris@5 47 * @param mixed|null $input The input as stream resource, scalar or \Traversable, or null for no input
Chris@5 48 * @param int|float|null $timeout The timeout in seconds or null to disable
Chris@5 49 * @return Process
Chris@5 50 */
Chris@5 51 public static function fromShellCommandline($command, $cwd = null, array $env = null, $input = null, $timeout = 60)
Chris@5 52 {
Chris@5 53 if (method_exists('\Symfony\Component\Process\Process', 'fromShellCommandline')) {
Chris@5 54 return Process::fromShellCommandline($command, $cwd, $env, $input, $timeout);
Chris@5 55 }
Chris@5 56 return new self($command, $cwd, $env, $input, $timeout);
Chris@5 57 }
Chris@5 58
Chris@5 59 /**
Chris@5 60 * realtimeStdout returns the output stream that realtime output
Chris@5 61 * should be sent to (if applicable)
Chris@5 62 *
Chris@5 63 * @return OutputStyle $output
Chris@5 64 */
Chris@5 65 public function realtimeStdout()
Chris@5 66 {
Chris@5 67 return $this->output;
Chris@5 68 }
Chris@5 69
Chris@5 70 protected function realtimeStderr()
Chris@5 71 {
Chris@5 72 if ($this->stderr) {
Chris@5 73 return $this->stderr;
Chris@5 74 }
Chris@5 75 if (method_exists($this->output, 'getErrorStyle')) {
Chris@5 76 return $this->output->getErrorStyle();
Chris@5 77 }
Chris@5 78
Chris@5 79 return $this->realtimeStdout();
Chris@5 80 }
Chris@5 81
Chris@5 82 /**
Chris@5 83 * setRealtimeOutput allows the caller to inject an OutputStyle object
Chris@5 84 * that will be used to stream realtime output if applicable.
Chris@5 85 *
Chris@5 86 * @param OutputStyle $output
Chris@5 87 */
Chris@5 88 public function setRealtimeOutput(OutputInterface $output, $stderr = null)
Chris@5 89 {
Chris@5 90 $this->output = $output;
Chris@5 91 $this->stderr = $stderr instanceof ConsoleOutputInterface ? $stderr->getErrorOutput() : $stderr;
Chris@5 92 }
Chris@5 93
Chris@5 94 /**
Chris@5 95 * @return bool
Chris@5 96 */
Chris@5 97 public function isVerbose()
Chris@5 98 {
Chris@5 99 return $this->verbose;
Chris@5 100 }
Chris@5 101
Chris@5 102 /**
Chris@5 103 * @param bool $verbose
Chris@5 104 */
Chris@5 105 public function setVerbose($verbose)
Chris@5 106 {
Chris@5 107 $this->verbose = $verbose;
Chris@5 108 }
Chris@5 109
Chris@5 110 /**
Chris@5 111 * @return bool
Chris@5 112 */
Chris@5 113 public function isSimulated()
Chris@5 114 {
Chris@5 115 return $this->simulated;
Chris@5 116 }
Chris@5 117
Chris@5 118 /**
Chris@5 119 * @param bool $simulated
Chris@5 120 */
Chris@5 121 public function setSimulated($simulated)
Chris@5 122 {
Chris@5 123 $this->simulated = $simulated;
Chris@5 124 }
Chris@5 125
Chris@5 126 /**
Chris@5 127 * @return LoggerInterface
Chris@5 128 */
Chris@5 129 public function getLogger()
Chris@5 130 {
Chris@5 131 return $this->logger;
Chris@5 132 }
Chris@5 133
Chris@5 134 /**
Chris@5 135 * @param LoggerInterface $logger
Chris@5 136 */
Chris@5 137 public function setLogger($logger)
Chris@5 138 {
Chris@5 139 $this->logger = $logger;
Chris@5 140 }
Chris@5 141
Chris@5 142 /**
Chris@5 143 * @inheritDoc
Chris@5 144 */
Chris@5 145 public function start(callable $callback = null, $env = array())
Chris@5 146 {
Chris@5 147 $cmd = $this->getCommandLine();
Chris@5 148 if ($this->isSimulated()) {
Chris@5 149 $this->getLogger()->notice('Simulating: ' . $cmd);
Chris@5 150 // Run a command that always succeeds (on Linux and Windows).
Chris@5 151 $this->setCommandLine('true');
Chris@5 152 } elseif ($this->isVerbose()) {
Chris@5 153 $this->getLogger()->info('Executing: ' . $cmd);
Chris@5 154 }
Chris@5 155 parent::start($callback, $env);
Chris@5 156 // Set command back to original value in case anyone asks.
Chris@5 157 if ($this->isSimulated()) {
Chris@5 158 $this->setCommandLine($cmd);
Chris@5 159 }
Chris@5 160 }
Chris@5 161
Chris@5 162 /**
Chris@5 163 * Get Process output and decode its JSON.
Chris@5 164 *
Chris@5 165 * @return array
Chris@5 166 * An associative array.
Chris@5 167 */
Chris@5 168 public function getOutputAsJson()
Chris@5 169 {
Chris@5 170 $output = trim($this->getOutput());
Chris@5 171 if (empty($output)) {
Chris@5 172 throw new \InvalidArgumentException('Output is empty.');
Chris@5 173 }
Chris@5 174 if (Escape::isWindows()) {
Chris@5 175 // Doubled double quotes were converted to \\".
Chris@5 176 // Revert to double quote.
Chris@5 177 $output = str_replace('\\"', '"', $output);
Chris@5 178 // Revert of doubled backslashes.
Chris@5 179 $output = preg_replace('#\\\\{2}#', '\\', $output);
Chris@5 180 }
Chris@5 181 $output = $this->removeNonJsonJunk($output);
Chris@5 182 $json = json_decode($output, true);
Chris@5 183 if (!isset($json)) {
Chris@5 184 throw new \InvalidArgumentException('Unable to decode output into JSON.');
Chris@5 185 }
Chris@5 186 return $json;
Chris@5 187 }
Chris@5 188
Chris@5 189 /**
Chris@5 190 * Allow for a certain amount of resiliancy in the output received when
Chris@5 191 * json is expected.
Chris@5 192 *
Chris@5 193 * @param string $data
Chris@5 194 * @return string
Chris@5 195 */
Chris@5 196 protected function removeNonJsonJunk($data)
Chris@5 197 {
Chris@5 198 // Exit early if we have no output.
Chris@5 199 $data = trim($data);
Chris@5 200 if (empty($data)) {
Chris@5 201 return $data;
Chris@5 202 }
Chris@5 203 // If the data is a simple quoted string, or an array, then exit.
Chris@5 204 if ((($data[0] == '"') && ($data[strlen($data) - 1] == '"')) ||
Chris@5 205 (($data[0] == "[") && ($data[strlen($data) - 1] == "]"))
Chris@5 206 ) {
Chris@5 207 return $data;
Chris@5 208 }
Chris@5 209 // If the json is not a simple string or a simple array, then is must
Chris@5 210 // be an associative array. We will remove non-json garbage characters
Chris@5 211 // before and after the enclosing curley-braces.
Chris@5 212 $start = strpos($data, '{');
Chris@5 213 $end = strrpos($data, '}') + 1;
Chris@5 214 $data = substr($data, $start, $end - $start);
Chris@5 215 return $data;
Chris@5 216 }
Chris@5 217
Chris@5 218 /**
Chris@5 219 * Return a realTime output object.
Chris@5 220 *
Chris@5 221 * @return callable
Chris@5 222 */
Chris@5 223 public function showRealtime()
Chris@5 224 {
Chris@5 225 $realTimeOutput = new RealtimeOutputHandler($this->realtimeStdout(), $this->realtimeStderr());
Chris@5 226 $realTimeOutput->configure($this);
Chris@5 227 return $realTimeOutput;
Chris@5 228 }
Chris@5 229 }