6 use Symfony\Component\Process\Process;
10 * @package Robo\Common
17 protected $background = false;
22 protected $timeout = null;
27 protected $idleTimeout = null;
32 protected $env = null;
40 * @var resource|string
47 protected $interactive = null;
52 protected $isPrinted = true;
57 protected $isMetadataPrinted = true;
62 protected $workingDirectory;
67 abstract public function getCommandDescription();
69 /** Typically provided by Timer trait via ProgressIndicatorAwareTrait. */
70 abstract public function startTimer();
71 abstract public function stopTimer();
72 abstract public function getExecutionTime();
75 * Typically provided by TaskIO Trait.
77 abstract public function hideTaskProgress();
78 abstract public function showTaskProgress($inProgress);
79 abstract public function printTaskInfo($text, $context = null);
82 * Typically provided by VerbosityThresholdTrait.
84 abstract public function verbosityMeetsThreshold();
85 abstract public function writeMessage($message);
88 * Sets $this->interactive() based on posix_isatty().
92 public function detectInteractive()
94 // If the caller did not explicity set the 'interactive' mode,
95 // and output should be produced by this task (verbosityMeetsThreshold),
96 // then we will automatically set interactive mode based on whether
97 // or not output was redirected when robo was executed.
98 if (!isset($this->interactive) && function_exists('posix_isatty') && $this->verbosityMeetsThreshold()) {
99 $this->interactive = posix_isatty(STDOUT);
106 * Executes command in background mode (asynchronously)
110 public function background($arg = true)
112 $this->background = $arg;
117 * Stop command if it runs longer then $timeout in seconds
119 * @param int $timeout
123 public function timeout($timeout)
125 $this->timeout = $timeout;
130 * Stops command if it does not output something for a while
132 * @param int $timeout
136 public function idleTimeout($timeout)
138 $this->idleTimeout = $timeout;
143 * Set a single environment variable, or multiple.
145 public function env($env, $value = null)
147 if (!is_array($env)) {
148 $env = [$env => ($value ? $value : true)];
150 return $this->envVars($env);
154 * Sets the environment variables for the command
160 public function envVars(array $env)
167 * Pass an input to the process. Can be resource created with fopen() or string
169 * @param resource|string $input
173 public function setInput($input)
175 $this->input = $input;
180 * Attach tty to process for interactive input
182 * @param $interactive bool
186 public function interactive($interactive = true)
188 $this->interactive = $interactive;
194 * Is command printing its output to screen
198 public function getPrinted()
200 return $this->isPrinted;
204 * Changes working directory of command
210 public function dir($dir)
212 $this->workingDirectory = $dir;
217 * Shortcut for setting isPrinted() and isMetadataPrinted() to false.
223 public function silent($arg)
226 $this->isPrinted = !$arg;
227 $this->isMetadataPrinted = !$arg;
233 * Should command output be printed
241 public function printed($arg)
243 $this->logger->warning("printed() is deprecated. Please use printOutput().");
244 return $this->printOutput($arg);
248 * Should command output be printed
254 public function printOutput($arg)
257 $this->isPrinted = $arg;
263 * Should command metadata be printed. I,e., command and timer.
269 public function printMetadata($arg)
272 $this->isMetadataPrinted = $arg;
278 * @param Process $process
279 * @param callable $output_callback
281 * @return \Robo\ResultData
283 protected function execute($process, $output_callback = null)
285 $this->process = $process;
287 if (!$output_callback) {
288 $output_callback = function ($type, $buffer) {
289 $progressWasVisible = $this->hideTaskProgress();
290 $this->writeMessage($buffer);
291 $this->showTaskProgress($progressWasVisible);
295 $this->detectInteractive();
297 if ($this->isMetadataPrinted) {
298 $this->printAction();
300 $this->process->setTimeout($this->timeout);
301 $this->process->setIdleTimeout($this->idleTimeout);
302 if ($this->workingDirectory) {
303 $this->process->setWorkingDirectory($this->workingDirectory);
306 $this->process->setInput($this->input);
309 if ($this->interactive && $this->isPrinted) {
310 $this->process->setTty(true);
313 if (isset($this->env)) {
314 $this->process->setEnv($this->env);
317 if (!$this->background && !$this->isPrinted) {
319 $this->process->run();
321 $output = rtrim($this->process->getOutput());
322 return new ResultData(
323 $this->process->getExitCode(),
325 $this->getResultData()
329 if (!$this->background && $this->isPrinted) {
331 $this->process->run($output_callback);
333 return new ResultData(
334 $this->process->getExitCode(),
335 $this->process->getOutput(),
336 $this->getResultData()
341 $this->process->start();
342 } catch (\Exception $e) {
343 return new ResultData(
344 $this->process->getExitCode(),
346 $this->getResultData()
349 return new ResultData($this->process->getExitCode());
355 protected function stop()
357 if ($this->background && isset($this->process) && $this->process->isRunning()) {
358 $this->process->stop();
359 $this->printTaskInfo(
361 ['command' => $this->getCommandDescription()]
367 * @param array $context
369 protected function printAction($context = [])
371 $command = $this->getCommandDescription();
372 $formatted_command = $this->formatCommandDisplay($command);
374 $dir = $this->workingDirectory ? " in {dir}" : "";
375 $this->printTaskInfo("Running {command}$dir", [
376 'command' => $formatted_command,
377 'dir' => $this->workingDirectory
386 protected function formatCommandDisplay($command)
388 $formatted_command = str_replace("&&", "&&\n", $command);
389 $formatted_command = str_replace("||", "||\n", $formatted_command);
391 return $formatted_command;
395 * Gets the data array to be passed to Result().
398 * The data array passed to Result().
400 protected function getResultData()
402 if ($this->isMetadataPrinted) {
403 return ['time' => $this->getExecutionTime()];