* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Style; use Symfony\Component\Console\Application; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Helper\Helper; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\SymfonyQuestionHelper; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; /** * Output decorator helpers for the Symfony Style Guide. * * @author Kevin Bond */ class SymfonyStyle extends OutputStyle { const MAX_LINE_LENGTH = 120; private $input; private $questionHelper; private $progressBar; private $lineLength; /** * @param InputInterface $input * @param OutputInterface $output */ public function __construct(InputInterface $input, OutputInterface $output) { $this->input = $input; $this->lineLength = min($this->getTerminalWidth(), self::MAX_LINE_LENGTH); parent::__construct($output); } /** * Formats a message as a block of text. * * @param string|array $messages The message to write in the block * @param string|null $type The block type (added in [] on first line) * @param string|null $style The style to apply to the whole block * @param string $prefix The prefix for the block * @param bool $padding Whether to add vertical padding */ public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false) { $messages = array_values((array) $messages); $lines = array(); // add type if (null !== $type) { $messages[0] = sprintf('[%s] %s', $type, $messages[0]); } // wrap and add newlines for each element foreach ($messages as $key => $message) { $message = OutputFormatter::escape($message); $lines = array_merge($lines, explode("\n", wordwrap($message, $this->lineLength - Helper::strlen($prefix)))); if (count($messages) > 1 && $key < count($messages) - 1) { $lines[] = ''; } } if ($padding && $this->isDecorated()) { array_unshift($lines, ''); $lines[] = ''; } foreach ($lines as &$line) { $line = sprintf('%s%s', $prefix, $line); $line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line)); if ($style) { $line = sprintf('<%s>%s', $style, $line); } } $this->writeln(implode("\n", $lines)."\n"); } /** * {@inheritdoc} */ public function title($message) { $this->writeln(sprintf("\n%s\n%s\n", $message, str_repeat('=', strlen($message)))); } /** * {@inheritdoc} */ public function section($message) { $this->writeln(sprintf("%s\n%s\n", $message, str_repeat('-', strlen($message)))); } /** * {@inheritdoc} */ public function listing(array $elements) { $elements = array_map(function ($element) { return sprintf(' * %s', $element); }, $elements ); $this->writeln(implode("\n", $elements)."\n"); } /** * {@inheritdoc} */ public function text($message) { if (!is_array($message)) { $this->writeln(sprintf(' // %s', $message)); return; } foreach ($message as $element) { $this->text($element); } } /** * {@inheritdoc} */ public function success($message) { $this->block($message, 'OK', 'fg=white;bg=green', ' ', true); } /** * {@inheritdoc} */ public function error($message) { $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); } /** * {@inheritdoc} */ public function warning($message) { $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true); } /** * {@inheritdoc} */ public function note($message) { $this->block($message, 'NOTE', 'fg=yellow', ' ! '); } /** * {@inheritdoc} */ public function caution($message) { $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); } /** * {@inheritdoc} */ public function table(array $headers, array $rows) { $headers = array_map(function ($value) { return sprintf('%s', $value); }, $headers); $table = new Table($this); $table->setHeaders($headers); $table->setRows($rows); $table->setStyle('symfony-style-guide'); $table->render(); $this->newLine(); } /** * {@inheritdoc} */ public function ask($question, $default = null, $validator = null) { $question = new Question($question, $default); $question->setValidator($validator); return $this->askQuestion($question, $validator); } /** * {@inheritdoc} */ public function askHidden($question, $validator = null) { $question = new Question($question); $question->setHidden(true); return $this->askQuestion($question, $validator); } /** * {@inheritdoc} */ public function confirm($question, $default = true) { return $this->askQuestion(new ConfirmationQuestion($question, $default)); } /** * {@inheritdoc} */ public function choice($question, array $choices, $default = null) { if (null !== $default) { $values = array_flip($choices); $default = $values[$default]; } return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); } /** * {@inheritdoc} */ public function progressStart($max = 0) { $this->progressBar = $this->createProgressBar($max); $this->progressBar->start(); } /** * {@inheritdoc} */ public function progressAdvance($step = 1) { $this->getProgressBar()->advance($step); } /** * {@inheritdoc} */ public function progressFinish() { $this->getProgressBar()->finish(); $this->newLine(2); $this->progressBar = null; } /** * {@inheritdoc} */ public function createProgressBar($max = 0) { $progressBar = parent::createProgressBar($max); if ('\\' === DIRECTORY_SEPARATOR) { $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591 $progressBar->setProgressCharacter(''); $progressBar->setBarCharacter('▓'); // dark shade character \u2593 } return $progressBar; } /** * @param Question $question * * @return string */ public function askQuestion(Question $question) { if (!$this->questionHelper) { $this->questionHelper = new SymfonyQuestionHelper(); } $answer = $this->questionHelper->ask($this->input, $this, $question); $this->newLine(); return $answer; } /** * @return ProgressBar */ private function getProgressBar() { if (!$this->progressBar) { throw new \RuntimeException('The ProgressBar is not started.'); } return $this->progressBar; } private function getTerminalWidth() { $application = new Application(); $dimensions = $application->getTerminalDimensions(); return $dimensions[0] ?: self::MAX_LINE_LENGTH; } }