diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md index 9c5741b5e7..c741f156f7 100644 --- a/src/Symfony/Component/Console/CHANGELOG.md +++ b/src/Symfony/Component/Console/CHANGELOG.md @@ -5,11 +5,13 @@ CHANGELOG ----- * deprecated the dialog helper (use the question helper instead) + * added a Process helper * deprecated TableHelper in favor of Table * deprecated ProgressHelper in favor of ProgressBar * added a question helper * added a way to set the process name of a command * added a way to set a default command instead of `ListCommand` + * added a way to set the process title of a command 2.4.0 ----- diff --git a/src/Symfony/Component/Console/Helper/DebugFormatterHelper.php b/src/Symfony/Component/Console/Helper/DebugFormatterHelper.php new file mode 100644 index 0000000000..c7616cf898 --- /dev/null +++ b/src/Symfony/Component/Console/Helper/DebugFormatterHelper.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Helper\Helper; + +/** + * Helps outputting debug information when running an external program from a command. + * + * An external program can be a Process, an HTTP request, or anything else. + * + * @author Fabien Potencier + */ +class DebugFormatterHelper extends Helper +{ + private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'); + private $started = array(); + private $count = -1; + + public function start($id, $message, $prefix = 'RUN') + { + $this->started[$id] = array('border' => ++$this->count % count($this->colors)); + + return sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); + } + + public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR') + { + $message = ''; + + if ($error) { + if (!isset($this->started[$id]['err'])) { + $message = sprintf("%s %s ", $this->getBorder($id), $errorPrefix); + $this->started[$id]['err'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $errorPrefix), $buffer); + } else { + if (!isset($this->started[$id]['out'])) { + $message = sprintf("%s %s ", $this->getBorder($id), $prefix); + $this->started[$id]['out'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $prefix), $buffer); + } + + return $message; + } + + public function stop($id, $message, $successful, $prefix = 'RES') + { + $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : ''; + + if ($successful) { + return sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + } + + return sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + } + + private function getBorder($id) + { + return sprintf(' ', $this->colors[$this->started[$id]['border']]); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'debug_formatter'; + } +} diff --git a/src/Symfony/Component/Console/Helper/ProcessHelper.php b/src/Symfony/Component/Console/Helper/ProcessHelper.php new file mode 100644 index 0000000000..30c38c1ece --- /dev/null +++ b/src/Symfony/Component/Console/Helper/ProcessHelper.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Process; + +/** + * The Process class provides helpers to run external processes. + * + * @author Fabien Potencier + */ +class ProcessHelper extends Helper +{ + /** + * Runs an external process. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|Process $cmd An instance of Process or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callback|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return Process The process that ran + */ + public function run(OutputInterface $output, $cmd, $error = null, $callback = null) + { + $verbose = $output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE; + $debug = $output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG; + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + $process = $cmd instanceof Process ? $cmd : new Process($cmd); + + if ($verbose) { + $output->write($formatter->start(spl_object_hash($process), $process->getCommandLine())); + } + + if ($debug) { + $callback = $this->wrapCallback($output, $process, $callback); + } + + $process->run($callback); + + if ($verbose) { + $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run sucessfully', $process->getExitCode()); + $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); + } + + if (!$process->isSuccessful() && null !== $error) { + $output->writeln(sprintf('%s'), $error); + } + + return $process; + } + + /** + * Wraps a Process callback to add debugging output. + * + * @param OutputInterface $output An OutputInterface interface + * @param callable|null $callback A PHP callable + */ + public function wrapCallback(OutputInterface $output, Process $process, $callback = null) + { + $formatter = $this->getHelperSet()->get('debug_formatter'); + + return function ($type, $buffer) use ($output, $process, $callback, $formatter) { + $output->write($formatter->progress(spl_object_hash($process), $buffer, 'err' === $type)); + + if (null !== $callback) { + $callback($type, $buffer); + } + }; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'process'; + } +} diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json index 40131af463..ec597c558c 100644 --- a/src/Symfony/Component/Console/composer.json +++ b/src/Symfony/Component/Console/composer.json @@ -24,6 +24,7 @@ }, "suggest": { "symfony/event-dispatcher": "", + "symfony/process": "", "psr/log": "For using the console logger" }, "autoload": {