/** * {@inheritdoc} */ public function generate(ReportSummary $reportSummary) { $dom = new \DOMDocument('1.0', 'UTF-8'); // new nodes should be added to this or existing children $root = $dom->createElement('report'); $dom->appendChild($root); $filesXML = $dom->createElement('files'); $root->appendChild($filesXML); $i = 1; foreach ($reportSummary->getChanged() as $file => $fixResult) { $fileXML = $dom->createElement('file'); $fileXML->setAttribute('id', $i++); $fileXML->setAttribute('name', $file); $filesXML->appendChild($fileXML); if ($reportSummary->shouldAddAppliedFixers()) { $fileXML->appendChild($this->createAppliedFixersElement($dom, $fixResult)); } if (!empty($fixResult['diff'])) { $fileXML->appendChild($this->createDiffElement($dom, $fixResult)); } } if (null !== $reportSummary->getTime()) { $root->appendChild($this->createTimeElement($reportSummary->getTime(), $dom)); } if (null !== $reportSummary->getTime()) { $root->appendChild($this->createMemoryElement($reportSummary->getMemory(), $dom)); } $dom->formatOutput = true; return $reportSummary->isDecoratedOutput() ? OutputFormatter::escape($dom->saveXML()) : $dom->saveXML(); }
/** * {@inheritdoc} */ protected function writePrompt(OutputInterface $output, Question $question) { $text = OutputFormatter::escape($question->getQuestion()); $default = $question->getDefault(); switch (true) { case null === $default: $text = sprintf(' <info>%s</info>:', $text); break; case $question instanceof ConfirmationQuestion: $text = sprintf(' <info>%s (yes/no)</info> [<comment>%s</comment>]:', $text, $default ? 'yes' : 'no'); break; case $question instanceof ChoiceQuestion && $question->isMultiselect(): $choices = $question->getChoices(); $default = explode(',', $default); foreach ($default as $key => $value) { $default[$key] = $choices[trim($value)]; } $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape(implode(', ', $default))); break; case $question instanceof ChoiceQuestion: $choices = $question->getChoices(); $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($choices[$default])); break; default: $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($default)); } $output->writeln($text); if ($question instanceof ChoiceQuestion) { $width = max(array_map('strlen', array_keys($question->getChoices()))); foreach ($question->getChoices() as $key => $value) { $output->writeln(sprintf(" [<comment>%-{$width}s</comment>] %s", $key, $value)); } } $output->write(' > '); }
/** * Get a backtrace for an exception. * * Optionally limit the number of rows to include with $count, and exclude * Psy from the trace. * * @param \Exception $e The exception with a backtrace. * @param int $count (default: PHP_INT_MAX) * @param bool $includePsy (default: true) * * @return array Formatted stacktrace lines. */ protected function getBacktrace(\Exception $e, $count = null, $includePsy = true) { if ($cwd = getcwd()) { $cwd = rtrim($cwd, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; } if ($count === null) { $count = PHP_INT_MAX; } $lines = array(); $trace = $e->getTrace(); array_unshift($trace, array('function' => '', 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', 'args' => array())); if (!$includePsy) { for ($i = count($trace) - 1; $i >= 0; $i--) { $thing = isset($trace[$i]['class']) ? $trace[$i]['class'] : $trace[$i]['function']; if (preg_match('/\\\\?Psy\\\\/', $thing)) { $trace = array_slice($trace, $i + 1); break; } } } for ($i = 0, $count = min($count, count($trace)); $i < $count; $i++) { $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; $function = $trace[$i]['function']; $file = isset($trace[$i]['file']) ? $this->replaceCwd($cwd, $trace[$i]['file']) : 'n/a'; $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; $lines[] = sprintf(' <class>%s</class>%s%s() at <info>%s:%s</info>', OutputFormatter::escape($class), OutputFormatter::escape($type), OutputFormatter::escape($function), OutputFormatter::escape($file), OutputFormatter::escape($line)); } return $lines; }
/** * Print escaped values of all parameters * Strings like 'Namespace\\' has to be escaped * * @link https://github.com/symfony/symfony/issues/17908 * * @return void */ public function complete() { $this->io->write('<info>Summary :</info>'); foreach ($this->getConfigKey('Placeholders') as $placeholder) { $this->io->write(sprintf(_('%1$s: <info>%2$s</info>'), Formatter\OutputFormatter::escape($placeholder['name']), Formatter\OutputFormatter::escape($placeholder['value']))); } }
/** * 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) { $this->autoPrependBlock(); $messages = is_array($messages) ? array_values($messages) : 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(PHP_EOL, wordwrap($message, $this->lineLength - Helper::strlen($prefix), PHP_EOL, true))); 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($lines); $this->newLine(); }
public function testFormattedEscapedOutput() { $output = new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, true, null); $output->writeln('<info>' . OutputFormatter::escape('<error>some error</error>') . '</info>'); rewind($output->getStream()); $this->assertSame("[32m<error>some error</error>[39m" . PHP_EOL, stream_get_contents($output->getStream())); }
public function testLGCharEscaping() { $formatter = new OutputFormatter(true); $this->assertEquals("foo<bar", $formatter->format('foo\\<bar')); $this->assertEquals("<info>some info</info>", $formatter->format('\\<info>some info\\</info>')); $this->assertEquals("\\<info>some info\\</info>", OutputFormatter::escape('<info>some info</info>')); $this->assertEquals("[33mSymfony\\Component\\Console does work very well![0m", $formatter->format('<comment>Symfony\\Component\\Console does work very well!</comment>')); }
/** * Present a reference to the value. * * @param mixed $value * * @return string */ public function presentRef($value, $color = false) { $type = get_resource_type($value); if ($type === 'stream') { $meta = stream_get_meta_data($value); $type = sprintf('%s stream', $meta['stream_type']); } $id = str_replace('Resource id #', '', (string) $value); $format = $color ? self::COLOR_FMT : self::FMT; return sprintf($format, OutputFormatter::escape('<'), $type, $id); }
/** * @param string $diff * @param string $lineTemplate * * @return string */ public function format($diff, $lineTemplate = '%s') { $isDecorated = $this->isDecoratedOutput; $template = $isDecorated ? $this->template : preg_replace('/<[^<>]+>/', '', $this->template); return sprintf($template, implode(PHP_EOL, array_map(function ($string) use($isDecorated, $lineTemplate) { if ($isDecorated) { $string = preg_replace(array('/^(\\+.*)/', '/^(\\-.*)/', '/^(@.*)/'), array('<fg=green>\\1</fg=green>', '<fg=red>\\1</fg=red>', '<fg=cyan>\\1</fg=cyan>'), $string); } $templated = sprintf($lineTemplate, $string); if (' ' === $string) { $templated = rtrim($templated); } return $templated; }, preg_split("#\n\r|\n#", $isDecorated ? OutputFormatter::escape(rtrim($diff)) : $diff)))); }
/** * {@inheritdoc} */ public function generate(ReportSummary $reportSummary) { $dom = new \DOMDocument('1.0', 'UTF-8'); $testsuites = $dom->appendChild($dom->createElement('testsuites')); /** @var \DomElement $testsuite */ $testsuite = $testsuites->appendChild($dom->createElement('testsuite')); $testsuite->setAttribute('name', 'PHP CS Fixer'); if (count($reportSummary->getChanged())) { $this->createFailedTestCases($dom, $testsuite, $reportSummary); } else { $this->createSuccessTestCase($dom, $testsuite); } if ($reportSummary->getTime()) { $testsuite->setAttribute('time', sprintf('%.3f', $reportSummary->getTime() / 1000)); } $dom->formatOutput = true; return $reportSummary->isDecoratedOutput() ? OutputFormatter::escape($dom->saveXML()) : $dom->saveXML(); }
/** * Writes a file in two phase to the filesystem. * * First write the data to a temporary file (in the same directory) and than renames the temporary file. If the file * already exists and its content is equal to the data that must be written no action is taken. This has the * following advantages: * * In case of some write error (e.g. disk full) the original file is kept in tact and no file with partially data * is written. * * Renaming a file is atomic. So, running processes will never read a partially written data. * * @param string $filename The name of the file were the data must be stored. * @param string $data The data that must be written. * @param StratumStyle $io The output decorator. */ static function writeTwoPhases($filename, $data, $io) { $write_flag = true; if (file_exists($filename)) { $old_data = file_get_contents($filename); if ($data == $old_data) { $write_flag = false; } } if ($write_flag) { $tmp_filename = $filename . '.tmp'; file_put_contents($tmp_filename, $data); rename($tmp_filename, $filename); $io->text(sprintf('Wrote <fso>%s</fso>', OutputFormatter::escape($filename))); } else { $io->text(sprintf('File <fso>%s</fso> is up to date', OutputFormatter::escape($filename))); } }
private function format($value) { // Handle floats. if (is_float($value)) { // Some are unencodable... if (is_nan($value)) { return 'NAN'; } elseif (is_infinite($value)) { return $value === INF ? 'INF' : '-INF'; } // ... others just encode as ints when there's no decimal $float = Json::encode($value); if (strpos($float, '.') === false) { $float .= '.0'; } return $float; } return OutputFormatter::escape(Json::encode($value)); }
/** * 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) { $this->autoPrependBlock(); $messages = is_array($messages) ? array_values($messages) : array($messages); $indentLength = 0; $lines = array(); if (null !== $type) { $typePrefix = sprintf('[%s] ', $type); $indentLength = strlen($typePrefix); $lineIndentation = str_repeat(' ', $indentLength); } // wrap and add newlines for each element foreach ($messages as $key => $message) { $message = OutputFormatter::escape($message); $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - Helper::strlen($prefix) - $indentLength, PHP_EOL, true))); // prefix each line with a number of spaces equivalent to the type length if (null !== $type) { foreach ($lines as &$line) { $line = $lineIndentation === substr($line, 0, $indentLength) ? $line : $lineIndentation . $line; } } if (count($messages) > 1 && $key < count($messages) - 1) { $lines[] = ''; } } if (null !== $type) { $lines[0] = substr_replace($lines[0], $typePrefix, 0, $indentLength); } 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($lines); $this->newLine(); }
/** * @param bool $isDecoratedOutput * @param array $fixResult * * @return string */ private function getDiff($isDecoratedOutput, array $fixResult) { if (empty($fixResult['diff'])) { return ''; } if ($isDecoratedOutput) { $template = '<comment> ---------- begin diff ----------</comment>%s<comment> ----------- end diff -----------</comment>'; $diff = implode(PHP_EOL, array_map(function ($string) { $string = preg_replace('/^(\\+){3}/', '<info>+++</info>', $string); $string = preg_replace('/^(\\+){1}/', '<info>+</info>', $string); $string = preg_replace('/^(\\-){3}/', '<error>---</error>', $string); $string = preg_replace('/^(\\-){1}/', '<error>-</error>', $string); return $string; }, explode(PHP_EOL, OutputFormatter::escape($fixResult['diff'])))); } else { $template = ' ---------- begin diff ----------%s ----------- end diff -----------'; $diff = $fixResult['diff']; } return PHP_EOL . sprintf($template, PHP_EOL . $diff . PHP_EOL) . PHP_EOL; }
private function renderCodeCoverage(array $reportLines, $widestLine) { $output = ''; $widestLineNum = mb_strlen(count($reportLines)); foreach ($reportLines as $i => $line) { $source = $this->formatter->escape(str_pad($line['line'], $widestLine)); $lineNum = str_pad((string) ($i + 1), $widestLineNum, ' ', STR_PAD_LEFT); switch ($line['code']) { case self::COVERAGE_COVERED: $output .= $this->formatter->format(sprintf('<bg=white;fg=green>[%s] %s</>', $lineNum, $source)); break; case self::COVERAGE_NOT_COVERED: $output .= $this->formatter->format(sprintf('<bg=white;fg=red>[%s] %s</>', $lineNum, $source)); break; case self::COVERAGE_META: $output .= $this->formatter->format(sprintf('<bg=white;fg=black>[%s] %s</>', $lineNum, $source)); break; } $output .= PHP_EOL; } return $output; }
/** * Formats a message as a block of text. * * @param string|array $messages The message to write in the block * @param string $style The style to apply to the whole block * @param bool $large Whether to return a large block * * @return string The formatter message */ public function formatBlock($messages, $style, $large = false) { $messages = (array) $messages; $len = 0; $lines = array(); foreach ($messages as $message) { $message = OutputFormatter::escape($message); $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); $len = max($this->strlen($message) + ($large ? 4 : 2), $len); } $messages = $large ? array(str_repeat(' ', $len)) : array(); foreach ($lines as $line) { $messages[] = $line . str_repeat(' ', $len - $this->strlen($line)); } if ($large) { $messages[] = str_repeat(' ', $len); } foreach ($messages as &$message) { $message = sprintf('<%s>%s</%s>', $style, $message, $style); } return implode("\n", $messages); }
/** * {@inheritdoc} */ public function generate(ReportSummary $reportSummary) { $jFiles = array(); foreach ($reportSummary->getChanged() as $file => $fixResult) { $jfile = array('name' => $file); if ($reportSummary->shouldAddAppliedFixers()) { $jfile['appliedFixers'] = $fixResult['appliedFixers']; } if (!empty($fixResult['diff'])) { $jfile['diff'] = $fixResult['diff']; } $jFiles[] = $jfile; } $json = array('files' => $jFiles); if (null !== $reportSummary->getTime()) { $json['time'] = array('total' => round($reportSummary->getTime() / 1000, 3)); } if (null !== $reportSummary->getMemory()) { $json['memory'] = round($reportSummary->getMemory() / 1024 / 1024, 3); } $json = json_encode($json); return $reportSummary->isDecoratedOutput() ? OutputFormatter::escape($json) : $json; }
/** * Renders a caught Exception. * * Exceptions are formatted according to severity. ErrorExceptions which were * warnings or Strict errors aren't rendered as harshly as real errors. * * Stores $e as the last Exception in the Shell Context. * * @param \Exception $e An exception instance * @param OutputInterface $output An OutputInterface instance */ public function renderException($e, $output) { $this->context->setLastException($e); $message = $e->getMessage(); if (!$e instanceof PsyException) { $message = sprintf('%s with message \'%s\'', get_class($e), $message); } $severity = $e instanceof \ErrorException ? $this->getSeverity($e) : 'error'; $output->writeln(sprintf('<%s>%s</%s>', $severity, OutputFormatter::escape($message), $severity)); $this->resetCodeBuffer(); }
public function testStyleEscaping() { $formatter = new OutputFormatter(true); $this->assertEquals("([32mz>=2.0,<a2.3[39m)", $formatter->format('(<info>' . $formatter->escape('z>=2.0,<a2.3') . '</info>)')); $this->assertEquals("[32m<error>some error</error>[39m", $formatter->format('<info>' . $formatter->escape('<error>some error</error>') . '</info>')); }
/** * Present a full representation of the value. * * If $depth is 0, the value will be presented as a ref instead. * * @param mixed $value * @param int $depth (default: null) * @param int $options One of Presenter constants * * @return string */ public function present($value, $depth = null, $options = 0) { $data = $this->cloner->cloneVar($value, !($options & self::VERBOSE) ? Caster::EXCLUDE_VERBOSE : 0); if (null !== $depth) { $data = $data->withMaxDepth($depth); } $output = ''; $this->dumper->dump($data, function ($line, $depth) use(&$output) { if ($depth >= 0) { if ('' !== $output) { $output .= PHP_EOL; } $output .= str_repeat(' ', $depth) . $line; } }); return OutputFormatter::escape($output); }
public static function escape($string) { return OutputFormatter::escape($string); }
/** * Print the function params. * * @param \ReflectionFunctionAbstract $reflector * * @return string */ private static function formatFunctionParams(\ReflectionFunctionAbstract $reflector) { $params = array(); foreach ($reflector->getParameters() as $param) { $hint = ''; try { if ($param->isArray()) { $hint = '<keyword>array</keyword> '; } elseif ($class = $param->getClass()) { $hint = sprintf('<class>%s</class> ', $class->getName()); } } catch (\Exception $e) { // sometimes we just don't know... // bad class names, or autoloaded classes that haven't been loaded yet, or whathaveyou. // come to think of it, the only time I've seen this is with the intl extension. // Hax: we'll try to extract it :P $chunks = explode('$' . $param->getName(), (string) $param); $chunks = explode(' ', trim($chunks[0])); $guess = end($chunks); $hint = sprintf('<urgent>%s</urgent> ', $guess); } if ($param->isOptional()) { if (!$param->isDefaultValueAvailable()) { $value = 'unknown'; $typeStyle = 'urgent'; } else { $value = $param->getDefaultValue(); $typeStyle = self::getTypeStyle($value); $value = is_array($value) ? 'array()' : is_null($value) ? 'null' : var_export($value, true); } $default = sprintf(' = <%s>%s</%s>', $typeStyle, OutputFormatter::escape($value), $typeStyle); } else { $default = ''; } $params[] = sprintf('%s%s<strong>$%s</strong>%s', $param->isPassedByReference() ? '&' : '', $hint, $param->getName(), $default); } return $params; }
/** * Format an item name given its visibility. * * @param array $item * * @return string */ private function formatItemName($item) { return sprintf('<%s>%s</%s>', $item['style'], OutputFormatter::escape($item['name']), $item['style']); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $dryRun = $input->getOption('dry-run'); $template = $input->getOption('template'); $config = $this->getConfig(); /** @var SymfonyStyle $styleHelper */ $styleHelper = $this->getHelper('gush_style'); if (null === ($metaHeader = $config->get('meta-header')) || $input->getOption('no-local')) { $metaHeader = $this->getHelper('template')->askAndRender('meta-header', $template); } $allFiles = $this->getHelper('git')->listFiles(); /** @var MetaHelper $meta */ $meta = $this->getHelper('meta'); if (null !== ($metaExcludePaths = $config->get('meta-exclude'))) { $allFiles = $meta->filterFilesList($allFiles, (array) $metaExcludePaths); } $supportedTypes = array_keys($meta->getSupportedFiles()); $styleHelper->title('Update copyright header of project files'); $styleHelper->note('This only updates files that are managed by Git version control.'); if ($dryRun) { $styleHelper->block('Dry-run is enabled files will not be updated.', 'INFO', 'fg=cyan', ' ! '); } foreach ($supportedTypes as $type) { $files = array_filter($allFiles, function ($value) use($type) { return pathinfo($value, PATHINFO_EXTENSION) === $type; }); if (0 === count($files)) { continue; } $styleHelper->section(sprintf('Update %s file(s)', $type)); $styleHelper->writeln([sprintf(' <info>The following header will be set on %d files:</info>', count($files)), '', '<comment>' . OutputFormatter::escape($meta->renderHeader($metaHeader, $type, ' ')) . '<comment>']); if (!$styleHelper->confirm('Do you want to continue?', true)) { continue; } $metaClass = $meta->getMetaClass($type); $header = $meta->renderHeader($metaHeader, $type, ''); foreach ($files as $file) { $fileContent = file_get_contents($file); if (!$meta->isUpdatable($metaClass, $fileContent)) { $output->writeln(sprintf(' <fg=red>[SKIPPED]</>: %s', $file)); continue; } $newContent = $meta->updateContent($metaClass, $header, $fileContent); if (false === $dryRun) { file_put_contents($file, $newContent); } if ($newContent !== $fileContent) { $output->writeln(sprintf(' <fg=cyan>[UPDATED]</>: %s', $file)); } else { $output->writeln(sprintf(' <fg=green>[IGNORED]</>: %s', $file)); } } $styleHelper->newLine(); $styleHelper->success(sprintf('%s files were updated.', $type)); } return self::COMMAND_SUCCESS; }
/** * Read a line of user input. * * This will return a line from the input buffer (if any exist). Otherwise, * it will ask the user for input. * * If readline is enabled, this delegates to readline. Otherwise, it's an * ugly `fgets` call. * * @return string One line of user input. */ protected function readline() { if (!empty($this->inputBuffer)) { $line = array_shift($this->inputBuffer); $this->output->writeln(sprintf('<aside>%s %s</aside>', self::REPLAY, OutputFormatter::escape($line))); return $line; } return $this->readline->readline($this->getPrompt()); }
/** * Outputs information about removed records. * * @param $record */ private function writeDeletions($record) { $this->io->text(sprintf('<sql>%s</sql>', OutputFormatter::escape('Deleted:'))); $output = "("; foreach ($record as $field_name => $field) { $output = $output . " {$field},"; } $output = rtrim($output, ", "); $output = $output . " )"; $this->io->text(sprintf('%s', $output)); }
/** * Build a command help from the Drush configuration array. * * Currently it's a word-wrapped description, plus any examples provided. * * @return string * The help string. */ protected function buildHelpFromConfig() { $help = wordwrap($this->config['description']); $examples = []; foreach ($this->config['examples'] as $ex => $def) { // Skip empty examples and things with obvious pipes... if ($ex === '' || strpos($ex, '|') !== FALSE) { continue; } $ex = preg_replace('/^drush\\s+/', '', $ex); $examples[$ex] = $def; } if (!empty($examples)) { $help .= "\n\ne.g."; foreach ($examples as $ex => $def) { $help .= sprintf("\n<return>// %s</return>\n", wordwrap(OutputFormatter::escape($def), 75, "</return>\n<return>// ")); $help .= sprintf("<return>>>> %s</return>\n", OutputFormatter::escape($ex)); } } return $help; }
/** * Present a reference to the object. * * @param object $value * * @return string */ public function presentRef($value, $color = false) { $format = $color ? self::COLOR_FMT : self::FMT; return sprintf($format, OutputFormatter::escape('<'), get_class($value), spl_object_hash($value)); }
/** * Format docblock tags. * * @param array $skip Tags to exclude * @param array $tags Tags to format * * @return string formatted tags */ private static function formatTags(array $skip, array $tags) { $chunks = array(); foreach ($tags as $name => $values) { if (in_array($name, $skip)) { continue; } foreach ($values as $value) { $chunks[] = sprintf('<comment>%s%s</comment> %s', self::inflect($name), empty($value) ? '' : ':', OutputFormatter::escape($value)); } $chunks[] = ''; } return implode("\n", $chunks); }
/** * Format a key => value pair. * * @param string $value * @param mixed $key * @param integer $pad Maximum key width, to align the hashrockets. * * @return string */ protected function formatKeyAndValue(&$value, $key, $pad = 0) { $value = sprintf("%-{$pad}s => %s", OutputFormatter::escape(json_encode($key, JSON_UNESCAPED_UNICODE)), $this->indentValue($value)); }