D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
home
/
t
/
a
/
t
/
tattooscyy
/
www
/
public
/
plugins
/
cropper
/
css
/
canvas
/
Filename :
Helper.tar
back
Copy
Table.php 0000604 00000062442 15225250340 0006306 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface; use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\OutputInterface; /** * Provides helpers to display a table. * * @author Fabien Potencier <fabien@symfony.com> * @author Саша Стаменковић <umpirsky@gmail.com> * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> * @author Max Grigorian <maxakawizard@gmail.com> * @author Dany Maillard <danymaillard93b@gmail.com> */ class Table { private const SEPARATOR_TOP = 0; private const SEPARATOR_TOP_BOTTOM = 1; private const SEPARATOR_MID = 2; private const SEPARATOR_BOTTOM = 3; private const BORDER_OUTSIDE = 0; private const BORDER_INSIDE = 1; private $headerTitle; private $footerTitle; /** * Table headers. */ private $headers = []; /** * Table rows. */ private $rows = []; /** * Column widths cache. */ private $effectiveColumnWidths = []; /** * Number of columns cache. * * @var int */ private $numberOfColumns; /** * @var OutputInterface */ private $output; /** * @var TableStyle */ private $style; /** * @var array */ private $columnStyles = []; /** * User set column widths. * * @var array */ private $columnWidths = []; private $columnMaxWidths = []; private static $styles; private $rendered = false; public function __construct(OutputInterface $output) { $this->output = $output; if (!self::$styles) { self::$styles = self::initStyles(); } $this->setStyle('default'); } /** * Sets a style definition. * * @param string $name The style name * @param TableStyle $style A TableStyle instance */ public static function setStyleDefinition($name, TableStyle $style) { if (!self::$styles) { self::$styles = self::initStyles(); } self::$styles[$name] = $style; } /** * Gets a style definition by name. * * @param string $name The style name * * @return TableStyle */ public static function getStyleDefinition($name) { if (!self::$styles) { self::$styles = self::initStyles(); } if (isset(self::$styles[$name])) { return self::$styles[$name]; } throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); } /** * Sets table style. * * @param TableStyle|string $name The style name or a TableStyle instance * * @return $this */ public function setStyle($name) { $this->style = $this->resolveStyle($name); return $this; } /** * Gets the current table style. * * @return TableStyle */ public function getStyle() { return $this->style; } /** * Sets table column style. * * @param int $columnIndex Column index * @param TableStyle|string $name The style name or a TableStyle instance * * @return $this */ public function setColumnStyle($columnIndex, $name) { $columnIndex = (int) $columnIndex; $this->columnStyles[$columnIndex] = $this->resolveStyle($name); return $this; } /** * Gets the current style for a column. * * If style was not set, it returns the global table style. * * @param int $columnIndex Column index * * @return TableStyle */ public function getColumnStyle($columnIndex) { return $this->columnStyles[$columnIndex] ?? $this->getStyle(); } /** * Sets the minimum width of a column. * * @param int $columnIndex Column index * @param int $width Minimum column width in characters * * @return $this */ public function setColumnWidth($columnIndex, $width) { $this->columnWidths[(int) $columnIndex] = (int) $width; return $this; } /** * Sets the minimum width of all columns. * * @return $this */ public function setColumnWidths(array $widths) { $this->columnWidths = []; foreach ($widths as $index => $width) { $this->setColumnWidth($index, $width); } return $this; } /** * Sets the maximum width of a column. * * Any cell within this column which contents exceeds the specified width will be wrapped into multiple lines, while * formatted strings are preserved. * * @return $this */ public function setColumnMaxWidth(int $columnIndex, int $width): self { if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) { throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, \get_class($this->output->getFormatter()))); } $this->columnMaxWidths[$columnIndex] = $width; return $this; } public function setHeaders(array $headers) { $headers = array_values($headers); if (!empty($headers) && !\is_array($headers[0])) { $headers = [$headers]; } $this->headers = $headers; return $this; } public function setRows(array $rows) { $this->rows = []; return $this->addRows($rows); } public function addRows(array $rows) { foreach ($rows as $row) { $this->addRow($row); } return $this; } public function addRow($row) { if ($row instanceof TableSeparator) { $this->rows[] = $row; return $this; } if (!\is_array($row)) { throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.'); } $this->rows[] = array_values($row); return $this; } /** * Adds a row to the table, and re-renders the table. */ public function appendRow($row): self { if (!$this->output instanceof ConsoleSectionOutput) { throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__)); } if ($this->rendered) { $this->output->clear($this->calculateRowCount()); } $this->addRow($row); $this->render(); return $this; } public function setRow($column, array $row) { $this->rows[$column] = $row; return $this; } public function setHeaderTitle(?string $title): self { $this->headerTitle = $title; return $this; } public function setFooterTitle(?string $title): self { $this->footerTitle = $title; return $this; } /** * Renders table to output. * * Example: * * +---------------+-----------------------+------------------+ * | ISBN | Title | Author | * +---------------+-----------------------+------------------+ * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | * +---------------+-----------------------+------------------+ */ public function render() { $rows = array_merge($this->headers, [$divider = new TableSeparator()], $this->rows); $this->calculateNumberOfColumns($rows); $rows = $this->buildTableRows($rows); $this->calculateColumnsWidth($rows); $isHeader = true; $isFirstRow = false; foreach ($rows as $row) { if ($divider === $row) { $isHeader = false; $isFirstRow = true; continue; } if ($row instanceof TableSeparator) { $this->renderRowSeparator(); continue; } if (!$row) { continue; } if ($isHeader || $isFirstRow) { if ($isFirstRow) { $this->renderRowSeparator(self::SEPARATOR_TOP_BOTTOM); $isFirstRow = false; } else { $this->renderRowSeparator(self::SEPARATOR_TOP, $this->headerTitle, $this->style->getHeaderTitleFormat()); } } $this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat()); } $this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat()); $this->cleanup(); $this->rendered = true; } /** * Renders horizontal header separator. * * Example: * * +-----+-----------+-------+ */ private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null) { if (0 === $count = $this->numberOfColumns) { return; } $borders = $this->style->getBorderChars(); if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) { return; } $crossings = $this->style->getCrossingChars(); if (self::SEPARATOR_MID === $type) { list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[2], $crossings[8], $crossings[0], $crossings[4]]; } elseif (self::SEPARATOR_TOP === $type) { list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[0], $crossings[1], $crossings[2], $crossings[3]]; } elseif (self::SEPARATOR_TOP_BOTTOM === $type) { list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[0], $crossings[9], $crossings[10], $crossings[11]]; } else { list($horizontal, $leftChar, $midChar, $rightChar) = [$borders[0], $crossings[7], $crossings[6], $crossings[5]]; } $markup = $leftChar; for ($column = 0; $column < $count; ++$column) { $markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]); $markup .= $column === $count - 1 ? $rightChar : $midChar; } if (null !== $title) { $titleLength = Helper::strlenWithoutDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title)); $markupLength = Helper::strlen($markup); if ($titleLength > $limit = $markupLength - 4) { $titleLength = $limit; $formatLength = Helper::strlenWithoutDecoration($formatter, sprintf($titleFormat, '')); $formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...'); } $titleStart = ($markupLength - $titleLength) / 2; if (false === mb_detect_encoding($markup, null, true)) { $markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength); } else { $markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength); } } $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); } /** * Renders vertical column separator. */ private function renderColumnSeparator($type = self::BORDER_OUTSIDE) { $borders = $this->style->getBorderChars(); return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]); } /** * Renders table row. * * Example: * * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | */ private function renderRow(array $row, string $cellFormat) { $rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE); $columns = $this->getRowColumns($row); $last = \count($columns) - 1; foreach ($columns as $i => $column) { $rowContent .= $this->renderCell($row, $column, $cellFormat); $rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE); } $this->output->writeln($rowContent); } /** * Renders table cell with padding. */ private function renderCell(array $row, int $column, string $cellFormat) { $cell = isset($row[$column]) ? $row[$column] : ''; $width = $this->effectiveColumnWidths[$column]; if ($cell instanceof TableCell && $cell->getColspan() > 1) { // add the width of the following columns(numbers of colspan). foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn]; } } // str_pad won't work properly with multi-byte strings, we need to fix the padding if (false !== $encoding = mb_detect_encoding($cell, null, true)) { $width += \strlen($cell) - mb_strwidth($cell, $encoding); } $style = $this->getColumnStyle($column); if ($cell instanceof TableSeparator) { return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width)); } $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); $content = sprintf($style->getCellRowContentFormat(), $cell); return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())); } /** * Calculate number of columns for this table. */ private function calculateNumberOfColumns($rows) { $columns = [0]; foreach ($rows as $row) { if ($row instanceof TableSeparator) { continue; } $columns[] = $this->getNumberOfColumns($row); } $this->numberOfColumns = max($columns); } private function buildTableRows($rows) { /** @var WrappableOutputFormatterInterface $formatter */ $formatter = $this->output->getFormatter(); $unmergedRows = []; for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) { $rows = $this->fillNextRows($rows, $rowKey); // Remove any new line breaks and replace it with a new line foreach ($rows[$rowKey] as $column => $cell) { $colspan = $cell instanceof TableCell ? $cell->getColspan() : 1; if (isset($this->columnMaxWidths[$column]) && Helper::strlenWithoutDecoration($formatter, $cell) > $this->columnMaxWidths[$column]) { $cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan); } if (!strstr($cell, "\n")) { continue; } $escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell))); $cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped; $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell)); foreach ($lines as $lineKey => $line) { if ($colspan > 1) { $line = new TableCell($line, ['colspan' => $colspan]); } if (0 === $lineKey) { $rows[$rowKey][$column] = $line; } else { $unmergedRows[$rowKey][$lineKey][$column] = $line; } } } } return new TableRows(function () use ($rows, $unmergedRows) { foreach ($rows as $rowKey => $row) { yield $this->fillCells($row); if (isset($unmergedRows[$rowKey])) { foreach ($unmergedRows[$rowKey] as $row) { yield $row; } } } }); } private function calculateRowCount(): int { $numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows)))); if ($this->headers) { ++$numberOfRows; // Add row for header separator } ++$numberOfRows; // Add row for footer separator return $numberOfRows; } /** * fill rows that contains rowspan > 1. * * @throws InvalidArgumentException */ private function fillNextRows(array $rows, int $line): array { $unmergedRows = []; foreach ($rows[$line] as $column => $cell) { if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) { throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing __toString, %s given.', \gettype($cell))); } if ($cell instanceof TableCell && $cell->getRowspan() > 1) { $nbLines = $cell->getRowspan() - 1; $lines = [$cell]; if (strstr($cell, "\n")) { $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell)); $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan()]); unset($lines[0]); } // create a two dimensional array (rowspan x colspan) $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows); foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan()]); if ($nbLines === $unmergedRowKey - $line) { break; } } } } foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { // we need to know if $unmergedRow will be merged or inserted into $rows if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { foreach ($unmergedRow as $cellKey => $cell) { // insert cell into row at cellKey position array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]); } } else { $row = $this->copyRow($rows, $unmergedRowKey - 1); foreach ($unmergedRow as $column => $cell) { if (!empty($cell)) { $row[$column] = $unmergedRow[$column]; } } array_splice($rows, $unmergedRowKey, 0, [$row]); } } return $rows; } /** * fill cells for a row that contains colspan > 1. */ private function fillCells($row) { $newRow = []; foreach ($row as $column => $cell) { $newRow[] = $cell; if ($cell instanceof TableCell && $cell->getColspan() > 1) { foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { // insert empty value at column position $newRow[] = ''; } } } return $newRow ?: $row; } private function copyRow(array $rows, int $line): array { $row = $rows[$line]; foreach ($row as $cellKey => $cellValue) { $row[$cellKey] = ''; if ($cellValue instanceof TableCell) { $row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]); } } return $row; } /** * Gets number of columns by row. */ private function getNumberOfColumns(array $row): int { $columns = \count($row); foreach ($row as $column) { $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; } return $columns; } /** * Gets list of columns for the given row. */ private function getRowColumns(array $row): array { $columns = range(0, $this->numberOfColumns - 1); foreach ($row as $cellKey => $cell) { if ($cell instanceof TableCell && $cell->getColspan() > 1) { // exclude grouped columns. $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1)); } } return $columns; } /** * Calculates columns widths. */ private function calculateColumnsWidth(iterable $rows) { for ($column = 0; $column < $this->numberOfColumns; ++$column) { $lengths = []; foreach ($rows as $row) { if ($row instanceof TableSeparator) { continue; } foreach ($row as $i => $cell) { if ($cell instanceof TableCell) { $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell); $textLength = Helper::strlen($textContent); if ($textLength > 0) { $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan())); foreach ($contentColumns as $position => $content) { $row[$i + $position] = $content; } } } } $lengths[] = $this->getCellWidth($row, $column); } $this->effectiveColumnWidths[$column] = max($lengths) + Helper::strlen($this->style->getCellRowContentFormat()) - 2; } } private function getColumnSeparatorWidth(): int { return Helper::strlen(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3])); } private function getCellWidth(array $row, int $column): int { $cellWidth = 0; if (isset($row[$column])) { $cell = $row[$column]; $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); } $columnWidth = isset($this->columnWidths[$column]) ? $this->columnWidths[$column] : 0; $cellWidth = max($cellWidth, $columnWidth); return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth; } /** * Called after rendering to cleanup cache data. */ private function cleanup() { $this->effectiveColumnWidths = []; $this->numberOfColumns = null; } private static function initStyles() { $borderless = new TableStyle(); $borderless ->setHorizontalBorderChars('=') ->setVerticalBorderChars(' ') ->setDefaultCrossingChar(' ') ; $compact = new TableStyle(); $compact ->setHorizontalBorderChars('') ->setVerticalBorderChars(' ') ->setDefaultCrossingChar('') ->setCellRowContentFormat('%s') ; $styleGuide = new TableStyle(); $styleGuide ->setHorizontalBorderChars('-') ->setVerticalBorderChars(' ') ->setDefaultCrossingChar(' ') ->setCellHeaderFormat('%s') ; $box = (new TableStyle()) ->setHorizontalBorderChars('─') ->setVerticalBorderChars('│') ->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├') ; $boxDouble = (new TableStyle()) ->setHorizontalBorderChars('═', '─') ->setVerticalBorderChars('║', '│') ->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣') ; return [ 'default' => new TableStyle(), 'borderless' => $borderless, 'compact' => $compact, 'symfony-style-guide' => $styleGuide, 'box' => $box, 'box-double' => $boxDouble, ]; } private function resolveStyle($name) { if ($name instanceof TableStyle) { return $name; } if (isset(self::$styles[$name])) { return self::$styles[$name]; } throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); } } FormatterHelper.php 0000604 00000005577 15225250340 0010370 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Formatter\OutputFormatter; /** * The Formatter class provides helpers to format messages. * * @author Fabien Potencier <fabien@symfony.com> */ class FormatterHelper extends Helper { /** * Formats a message within a section. * * @param string $section The section name * @param string $message The message * @param string $style The style to apply to the section * * @return string The format section */ public function formatSection($section, $message, $style = 'info') { return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message); } /** * 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) { if (!\is_array($messages)) { $messages = [$messages]; } $len = 0; $lines = []; 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 ? [str_repeat(' ', $len)] : []; for ($i = 0; isset($lines[$i]); ++$i) { $messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i])); } if ($large) { $messages[] = str_repeat(' ', $len); } for ($i = 0; isset($messages[$i]); ++$i) { $messages[$i] = sprintf('<%s>%s</%s>', $style, $messages[$i], $style); } return implode("\n", $messages); } /** * Truncates a message to the given length. * * @param string $message * @param int $length * @param string $suffix * * @return string */ public function truncate($message, $length, $suffix = '...') { $computedLength = $length - $this->strlen($suffix); if ($computedLength > $this->strlen($message)) { return $message; } if (false === $encoding = mb_detect_encoding($message, null, true)) { return substr($message, 0, $length).$suffix; } return mb_substr($message, 0, $length, $encoding).$suffix; } /** * {@inheritdoc} */ public function getName() { return 'formatter'; } } TableStyle.php 0000604 00000036267 15225250340 0007335 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\LogicException; /** * Defines the styles for a Table. * * @author Fabien Potencier <fabien@symfony.com> * @author Саша Стаменковић <umpirsky@gmail.com> * @author Dany Maillard <danymaillard93b@gmail.com> */ class TableStyle { private $paddingChar = ' '; private $horizontalOutsideBorderChar = '-'; private $horizontalInsideBorderChar = '-'; private $verticalOutsideBorderChar = '|'; private $verticalInsideBorderChar = '|'; private $crossingChar = '+'; private $crossingTopRightChar = '+'; private $crossingTopMidChar = '+'; private $crossingTopLeftChar = '+'; private $crossingMidRightChar = '+'; private $crossingBottomRightChar = '+'; private $crossingBottomMidChar = '+'; private $crossingBottomLeftChar = '+'; private $crossingMidLeftChar = '+'; private $crossingTopLeftBottomChar = '+'; private $crossingTopMidBottomChar = '+'; private $crossingTopRightBottomChar = '+'; private $headerTitleFormat = '<fg=black;bg=white;options=bold> %s </>'; private $footerTitleFormat = '<fg=black;bg=white;options=bold> %s </>'; private $cellHeaderFormat = '<info>%s</info>'; private $cellRowFormat = '%s'; private $cellRowContentFormat = ' %s '; private $borderFormat = '%s'; private $padType = STR_PAD_RIGHT; /** * Sets padding character, used for cell padding. * * @param string $paddingChar * * @return $this */ public function setPaddingChar($paddingChar) { if (!$paddingChar) { throw new LogicException('The padding char must not be empty'); } $this->paddingChar = $paddingChar; return $this; } /** * Gets padding character, used for cell padding. * * @return string */ public function getPaddingChar() { return $this->paddingChar; } /** * Sets horizontal border characters. * * <code> * ╔═══════════════╤══════════════════════════╤══════════════════╗ * 1 ISBN 2 Title │ Author ║ * ╠═══════════════╪══════════════════════════╪══════════════════╣ * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ * ╚═══════════════╧══════════════════════════╧══════════════════╝ * </code> * * @param string $outside Outside border char (see #1 of example) * @param string|null $inside Inside border char (see #2 of example), equals $outside if null */ public function setHorizontalBorderChars(string $outside, string $inside = null): self { $this->horizontalOutsideBorderChar = $outside; $this->horizontalInsideBorderChar = $inside ?? $outside; return $this; } /** * Sets horizontal border character. * * @param string $horizontalBorderChar * * @return $this * * @deprecated since Symfony 4.1, use {@link setHorizontalBorderChars()} instead. */ public function setHorizontalBorderChar($horizontalBorderChar) { @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setHorizontalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); return $this->setHorizontalBorderChars($horizontalBorderChar, $horizontalBorderChar); } /** * Gets horizontal border character. * * @return string * * @deprecated since Symfony 4.1, use {@link getBorderChars()} instead. */ public function getHorizontalBorderChar() { @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); return $this->horizontalOutsideBorderChar; } /** * Sets vertical border characters. * * <code> * ╔═══════════════╤══════════════════════════╤══════════════════╗ * ║ ISBN │ Title │ Author ║ * ╠═══════1═══════╪══════════════════════════╪══════════════════╣ * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ * ╟───────2───────┼──────────────────────────┼──────────────────╢ * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ * ╚═══════════════╧══════════════════════════╧══════════════════╝ * </code> * * @param string $outside Outside border char (see #1 of example) * @param string|null $inside Inside border char (see #2 of example), equals $outside if null */ public function setVerticalBorderChars(string $outside, string $inside = null): self { $this->verticalOutsideBorderChar = $outside; $this->verticalInsideBorderChar = $inside ?? $outside; return $this; } /** * Sets vertical border character. * * @param string $verticalBorderChar * * @return $this * * @deprecated since Symfony 4.1, use {@link setVerticalBorderChars()} instead. */ public function setVerticalBorderChar($verticalBorderChar) { @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setVerticalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); return $this->setVerticalBorderChars($verticalBorderChar, $verticalBorderChar); } /** * Gets vertical border character. * * @return string * * @deprecated since Symfony 4.1, use {@link getBorderChars()} instead. */ public function getVerticalBorderChar() { @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); return $this->verticalOutsideBorderChar; } /** * Gets border characters. * * @internal */ public function getBorderChars() { return [ $this->horizontalOutsideBorderChar, $this->verticalOutsideBorderChar, $this->horizontalInsideBorderChar, $this->verticalInsideBorderChar, ]; } /** * Sets crossing characters. * * Example: * <code> * 1═══════════════2══════════════════════════2══════════════════3 * ║ ISBN │ Title │ Author ║ * 8'══════════════0'═════════════════════════0'═════════════════4' * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ * 8───────────────0──────────────────────────0──────────────────4 * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ * 7═══════════════6══════════════════════════6══════════════════5 * </code> * * @param string $cross Crossing char (see #0 of example) * @param string $topLeft Top left char (see #1 of example) * @param string $topMid Top mid char (see #2 of example) * @param string $topRight Top right char (see #3 of example) * @param string $midRight Mid right char (see #4 of example) * @param string $bottomRight Bottom right char (see #5 of example) * @param string $bottomMid Bottom mid char (see #6 of example) * @param string $bottomLeft Bottom left char (see #7 of example) * @param string $midLeft Mid left char (see #8 of example) * @param string|null $topLeftBottom Top left bottom char (see #8' of example), equals to $midLeft if null * @param string|null $topMidBottom Top mid bottom char (see #0' of example), equals to $cross if null * @param string|null $topRightBottom Top right bottom char (see #4' of example), equals to $midRight if null */ public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft, string $topLeftBottom = null, string $topMidBottom = null, string $topRightBottom = null): self { $this->crossingChar = $cross; $this->crossingTopLeftChar = $topLeft; $this->crossingTopMidChar = $topMid; $this->crossingTopRightChar = $topRight; $this->crossingMidRightChar = $midRight; $this->crossingBottomRightChar = $bottomRight; $this->crossingBottomMidChar = $bottomMid; $this->crossingBottomLeftChar = $bottomLeft; $this->crossingMidLeftChar = $midLeft; $this->crossingTopLeftBottomChar = $topLeftBottom ?? $midLeft; $this->crossingTopMidBottomChar = $topMidBottom ?? $cross; $this->crossingTopRightBottomChar = $topRightBottom ?? $midRight; return $this; } /** * Sets default crossing character used for each cross. * * @see {@link setCrossingChars()} for setting each crossing individually. */ public function setDefaultCrossingChar(string $char): self { return $this->setCrossingChars($char, $char, $char, $char, $char, $char, $char, $char, $char); } /** * Sets crossing character. * * @param string $crossingChar * * @return $this * * @deprecated since Symfony 4.1. Use {@link setDefaultCrossingChar()} instead. */ public function setCrossingChar($crossingChar) { @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use setDefaultCrossingChar() instead.', __METHOD__), E_USER_DEPRECATED); return $this->setDefaultCrossingChar($crossingChar); } /** * Gets crossing character. * * @return string */ public function getCrossingChar() { return $this->crossingChar; } /** * Gets crossing characters. * * @internal */ public function getCrossingChars(): array { return [ $this->crossingChar, $this->crossingTopLeftChar, $this->crossingTopMidChar, $this->crossingTopRightChar, $this->crossingMidRightChar, $this->crossingBottomRightChar, $this->crossingBottomMidChar, $this->crossingBottomLeftChar, $this->crossingMidLeftChar, $this->crossingTopLeftBottomChar, $this->crossingTopMidBottomChar, $this->crossingTopRightBottomChar, ]; } /** * Sets header cell format. * * @param string $cellHeaderFormat * * @return $this */ public function setCellHeaderFormat($cellHeaderFormat) { $this->cellHeaderFormat = $cellHeaderFormat; return $this; } /** * Gets header cell format. * * @return string */ public function getCellHeaderFormat() { return $this->cellHeaderFormat; } /** * Sets row cell format. * * @param string $cellRowFormat * * @return $this */ public function setCellRowFormat($cellRowFormat) { $this->cellRowFormat = $cellRowFormat; return $this; } /** * Gets row cell format. * * @return string */ public function getCellRowFormat() { return $this->cellRowFormat; } /** * Sets row cell content format. * * @param string $cellRowContentFormat * * @return $this */ public function setCellRowContentFormat($cellRowContentFormat) { $this->cellRowContentFormat = $cellRowContentFormat; return $this; } /** * Gets row cell content format. * * @return string */ public function getCellRowContentFormat() { return $this->cellRowContentFormat; } /** * Sets table border format. * * @param string $borderFormat * * @return $this */ public function setBorderFormat($borderFormat) { $this->borderFormat = $borderFormat; return $this; } /** * Gets table border format. * * @return string */ public function getBorderFormat() { return $this->borderFormat; } /** * Sets cell padding type. * * @param int $padType STR_PAD_* * * @return $this */ public function setPadType($padType) { if (!\in_array($padType, [STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH], true)) { throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).'); } $this->padType = $padType; return $this; } /** * Gets cell padding type. * * @return int */ public function getPadType() { return $this->padType; } public function getHeaderTitleFormat(): string { return $this->headerTitleFormat; } public function setHeaderTitleFormat(string $format): self { $this->headerTitleFormat = $format; return $this; } public function getFooterTitleFormat(): string { return $this->footerTitleFormat; } public function setFooterTitleFormat(string $format): self { $this->footerTitleFormat = $format; return $this; } } DebugFormatterHelper.php 0000604 00000010110 15225250340 0011312 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\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 <fabien@symfony.com> */ class DebugFormatterHelper extends Helper { private $colors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default']; private $started = []; private $count = -1; /** * Starts a debug formatting session. * * @param string $id The id of the formatting session * @param string $message The message to display * @param string $prefix The prefix to use * * @return string */ public function start($id, $message, $prefix = 'RUN') { $this->started[$id] = ['border' => ++$this->count % \count($this->colors)]; return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message); } /** * Adds progress to a formatting session. * * @param string $id The id of the formatting session * @param string $buffer The message to display * @param bool $error Whether to consider the buffer as error * @param string $prefix The prefix for output * @param string $errorPrefix The prefix for error output * * @return string */ public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR') { $message = ''; if ($error) { if (isset($this->started[$id]['out'])) { $message .= "\n"; unset($this->started[$id]['out']); } if (!isset($this->started[$id]['err'])) { $message .= sprintf('%s<bg=red;fg=white> %s </> ', $this->getBorder($id), $errorPrefix); $this->started[$id]['err'] = true; } $message .= str_replace("\n", sprintf("\n%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix), $buffer); } else { if (isset($this->started[$id]['err'])) { $message .= "\n"; unset($this->started[$id]['err']); } if (!isset($this->started[$id]['out'])) { $message .= sprintf('%s<bg=green;fg=white> %s </> ', $this->getBorder($id), $prefix); $this->started[$id]['out'] = true; } $message .= str_replace("\n", sprintf("\n%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix), $buffer); } return $message; } /** * Stops a formatting session. * * @param string $id The id of the formatting session * @param string $message The message to display * @param bool $successful Whether to consider the result as success * @param string $prefix The prefix for the end output * * @return string */ 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<bg=green;fg=white> %s </> <fg=green>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message); } $message = sprintf("%s%s<bg=red;fg=white> %s </> <fg=red>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message); unset($this->started[$id]['out'], $this->started[$id]['err']); return $message; } /** * @param string $id The id of the formatting session * * @return string */ private function getBorder($id) { return sprintf('<bg=%s> </>', $this->colors[$this->started[$id]['border']]); } /** * {@inheritdoc} */ public function getName() { return 'debug_formatter'; } } QuestionHelper.php 0000604 00000036234 15225250340 0010226 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Exception\RuntimeException; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\StreamableInputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Question\Question; /** * The QuestionHelper class provides helpers to interact with the user. * * @author Fabien Potencier <fabien@symfony.com> */ class QuestionHelper extends Helper { private $inputStream; private static $shell; private static $stty; /** * Asks a question to the user. * * @return mixed The user answer * * @throws RuntimeException If there is no data to read in the input stream */ public function ask(InputInterface $input, OutputInterface $output, Question $question) { if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } if (!$input->isInteractive()) { $default = $question->getDefault(); if (null === $default) { return $default; } if ($validator = $question->getValidator()) { return \call_user_func($question->getValidator(), $default); } elseif ($question instanceof ChoiceQuestion) { $choices = $question->getChoices(); if (!$question->isMultiselect()) { return isset($choices[$default]) ? $choices[$default] : $default; } $default = explode(',', $default); foreach ($default as $k => $v) { $v = trim($v); $default[$k] = isset($choices[$v]) ? $choices[$v] : $v; } } return $default; } if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) { $this->inputStream = $stream; } if (!$question->getValidator()) { return $this->doAsk($output, $question); } $interviewer = function () use ($output, $question) { return $this->doAsk($output, $question); }; return $this->validateAttempts($interviewer, $output, $question); } /** * {@inheritdoc} */ public function getName() { return 'question'; } /** * Prevents usage of stty. */ public static function disableStty() { self::$stty = false; } /** * Asks the question to the user. * * @return bool|mixed|string|null * * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden */ private function doAsk(OutputInterface $output, Question $question) { $this->writePrompt($output, $question); $inputStream = $this->inputStream ?: STDIN; $autocomplete = $question->getAutocompleterCallback(); if (null === $autocomplete || !$this->hasSttyAvailable()) { $ret = false; if ($question->isHidden()) { try { $ret = trim($this->getHiddenResponse($output, $inputStream)); } catch (RuntimeException $e) { if (!$question->isHiddenFallback()) { throw $e; } } } if (false === $ret) { $ret = fgets($inputStream, 4096); if (false === $ret) { throw new RuntimeException('Aborted.'); } $ret = trim($ret); } } else { $ret = trim($this->autocomplete($output, $question, $inputStream, $autocomplete)); } if ($output instanceof ConsoleSectionOutput) { $output->addContent($ret); } $ret = \strlen($ret) > 0 ? $ret : $question->getDefault(); if ($normalizer = $question->getNormalizer()) { return $normalizer($ret); } return $ret; } /** * Outputs the question prompt. */ protected function writePrompt(OutputInterface $output, Question $question) { $message = $question->getQuestion(); if ($question instanceof ChoiceQuestion) { $maxWidth = max(array_map([$this, 'strlen'], array_keys($question->getChoices()))); $messages = (array) $question->getQuestion(); foreach ($question->getChoices() as $key => $value) { $width = $maxWidth - $this->strlen($key); $messages[] = ' [<info>'.$key.str_repeat(' ', $width).'</info>] '.$value; } $output->writeln($messages); $message = $question->getPrompt(); } $output->write($message); } /** * Outputs an error message. */ protected function writeError(OutputInterface $output, \Exception $error) { if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) { $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'); } else { $message = '<error>'.$error->getMessage().'</error>'; } $output->writeln($message); } /** * Autocompletes a question. * * @param resource $inputStream */ private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string { $fullChoice = ''; $ret = ''; $i = 0; $ofs = -1; $matches = $autocomplete($ret); $numMatches = \count($matches); $sttyMode = shell_exec('stty -g'); // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) shell_exec('stty -icanon -echo'); // Add highlighted text style $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); // Read a keypress while (!feof($inputStream)) { $c = fread($inputStream, 1); // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false. if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) { shell_exec(sprintf('stty %s', $sttyMode)); throw new RuntimeException('Aborted.'); } elseif ("\177" === $c) { // Backspace Character if (0 === $numMatches && 0 !== $i) { --$i; $fullChoice = substr($fullChoice, 0, -1); // Move cursor backwards $output->write("\033[1D"); } if (0 === $i) { $ofs = -1; $matches = $autocomplete($ret); $numMatches = \count($matches); } else { $numMatches = 0; } // Pop the last character off the end of our string $ret = substr($ret, 0, $i); } elseif ("\033" === $c) { // Did we read an escape sequence? $c .= fread($inputStream, 2); // A = Up Arrow. B = Down Arrow if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { if ('A' === $c[2] && -1 === $ofs) { $ofs = 0; } if (0 === $numMatches) { continue; } $ofs += ('A' === $c[2]) ? -1 : 1; $ofs = ($numMatches + $ofs) % $numMatches; } } elseif (\ord($c) < 32) { if ("\t" === $c || "\n" === $c) { if ($numMatches > 0 && -1 !== $ofs) { $ret = (string) $matches[$ofs]; // Echo out remaining chars for current match $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)))); $output->write($remainingCharacters); $fullChoice .= $remainingCharacters; $i = \strlen($fullChoice); $matches = array_filter( $autocomplete($ret), function ($match) use ($ret) { return '' === $ret || 0 === strpos($match, $ret); } ); $numMatches = \count($matches); $ofs = -1; } if ("\n" === $c) { $output->write($c); break; } } continue; } else { if ("\x80" <= $c) { $c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]); } $output->write($c); $ret .= $c; $fullChoice .= $c; ++$i; $tempRet = $ret; if ($question instanceof ChoiceQuestion && $question->isMultiselect()) { $tempRet = $this->mostRecentlyEnteredValue($fullChoice); } $numMatches = 0; $ofs = 0; foreach ($autocomplete($ret) as $value) { // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) if (0 === strpos($value, $tempRet)) { $matches[$numMatches++] = $value; } } } // Erase characters from cursor to end of line $output->write("\033[K"); if ($numMatches > 0 && -1 !== $ofs) { // Save cursor position $output->write("\0337"); // Write highlighted text, complete the partially entered response $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))); $output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>'); // Restore cursor position $output->write("\0338"); } } // Reset stty so it behaves normally again shell_exec(sprintf('stty %s', $sttyMode)); return $fullChoice; } private function mostRecentlyEnteredValue($entered) { // Determine the most recent value that the user entered if (false === strpos($entered, ',')) { return $entered; } $choices = explode(',', $entered); if (\strlen($lastChoice = trim($choices[\count($choices) - 1])) > 0) { return $lastChoice; } return $entered; } /** * Gets a hidden response from user. * * @param OutputInterface $output An Output instance * @param resource $inputStream The handler resource * * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden */ private function getHiddenResponse(OutputInterface $output, $inputStream): string { if ('\\' === \DIRECTORY_SEPARATOR) { $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; // handle code running from a phar if ('phar:' === substr(__FILE__, 0, 5)) { $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; copy($exe, $tmpExe); $exe = $tmpExe; } $value = rtrim(shell_exec($exe)); $output->writeln(''); if (isset($tmpExe)) { unlink($tmpExe); } return $value; } if ($this->hasSttyAvailable()) { $sttyMode = shell_exec('stty -g'); shell_exec('stty -echo'); $value = fgets($inputStream, 4096); shell_exec(sprintf('stty %s', $sttyMode)); if (false === $value) { throw new RuntimeException('Aborted.'); } $value = trim($value); $output->writeln(''); return $value; } if (false !== $shell = $this->getShell()) { $readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword'; $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); $value = rtrim(shell_exec($command)); $output->writeln(''); return $value; } throw new RuntimeException('Unable to hide the response.'); } /** * Validates an attempt. * * @param callable $interviewer A callable that will ask for a question and return the result * @param OutputInterface $output An Output instance * @param Question $question A Question instance * * @return mixed The validated response * * @throws \Exception In case the max number of attempts has been reached and no valid response has been given */ private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question) { $error = null; $attempts = $question->getMaxAttempts(); while (null === $attempts || $attempts--) { if (null !== $error) { $this->writeError($output, $error); } try { return $question->getValidator()($interviewer()); } catch (RuntimeException $e) { throw $e; } catch (\Exception $error) { } } throw $error; } /** * Returns a valid unix shell. * * @return string|bool The valid shell name, false in case no valid shell is found */ private function getShell() { if (null !== self::$shell) { return self::$shell; } self::$shell = false; if (file_exists('/usr/bin/env')) { // handle other OSs with bash/zsh/ksh/csh if available to hide the answer $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) { if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { self::$shell = $sh; break; } } } return self::$shell; } /** * Returns whether Stty is available or not. */ private function hasSttyAvailable(): bool { if (null !== self::$stty) { return self::$stty; } exec('stty 2>&1', $output, $exitcode); return self::$stty = 0 === $exitcode; } } TableCell.php 0000604 00000002703 15225250340 0007100 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Exception\InvalidArgumentException; /** * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ class TableCell { private $value; private $options = [ 'rowspan' => 1, 'colspan' => 1, ]; public function __construct(string $value = '', array $options = []) { $this->value = $value; // check option names if ($diff = array_diff(array_keys($options), array_keys($this->options))) { throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); } $this->options = array_merge($this->options, $options); } /** * Returns the cell value. * * @return string */ public function __toString() { return $this->value; } /** * Gets number of colspan. * * @return int */ public function getColspan() { return (int) $this->options['colspan']; } /** * Gets number of rowspan. * * @return int */ public function getRowspan() { return (int) $this->options['rowspan']; } } HelperInterface.php 0000604 00000001560 15225250340 0010311 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Helper; /** * HelperInterface is the interface all helpers must implement. * * @author Fabien Potencier <fabien@symfony.com> */ interface HelperInterface { /** * Sets the helper set associated with this helper. */ public function setHelperSet(HelperSet $helperSet = null); /** * Gets the helper set associated with this helper. * * @return HelperSet A HelperSet instance */ public function getHelperSet(); /** * Returns the canonical name of this helper. * * @return string The canonical name */ public function getName(); } ProgressIndicator.php 0000604 00000017174 15225250340 0010722 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\LogicException; use Symfony\Component\Console\Output\OutputInterface; /** * @author Kevin Bond <kevinbond@gmail.com> */ class ProgressIndicator { private $output; private $startTime; private $format; private $message; private $indicatorValues; private $indicatorCurrent; private $indicatorChangeInterval; private $indicatorUpdateTime; private $started = false; private static $formatters; private static $formats; /** * @param string|null $format Indicator format * @param int $indicatorChangeInterval Change interval in milliseconds * @param array|null $indicatorValues Animated indicator characters */ public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null) { $this->output = $output; if (null === $format) { $format = $this->determineBestFormat(); } if (null === $indicatorValues) { $indicatorValues = ['-', '\\', '|', '/']; } $indicatorValues = array_values($indicatorValues); if (2 > \count($indicatorValues)) { throw new InvalidArgumentException('Must have at least 2 indicator value characters.'); } $this->format = self::getFormatDefinition($format); $this->indicatorChangeInterval = $indicatorChangeInterval; $this->indicatorValues = $indicatorValues; $this->startTime = time(); } /** * Sets the current indicator message. * * @param string|null $message */ public function setMessage($message) { $this->message = $message; $this->display(); } /** * Starts the indicator output. * * @param $message */ public function start($message) { if ($this->started) { throw new LogicException('Progress indicator already started.'); } $this->message = $message; $this->started = true; $this->startTime = time(); $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval; $this->indicatorCurrent = 0; $this->display(); } /** * Advances the indicator. */ public function advance() { if (!$this->started) { throw new LogicException('Progress indicator has not yet been started.'); } if (!$this->output->isDecorated()) { return; } $currentTime = $this->getCurrentTimeInMilliseconds(); if ($currentTime < $this->indicatorUpdateTime) { return; } $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval; ++$this->indicatorCurrent; $this->display(); } /** * Finish the indicator with message. * * @param $message */ public function finish($message) { if (!$this->started) { throw new LogicException('Progress indicator has not yet been started.'); } $this->message = $message; $this->display(); $this->output->writeln(''); $this->started = false; } /** * Gets the format for a given name. * * @param string $name The format name * * @return string|null A format string */ public static function getFormatDefinition($name) { if (!self::$formats) { self::$formats = self::initFormats(); } return isset(self::$formats[$name]) ? self::$formats[$name] : null; } /** * Sets a placeholder formatter for a given name. * * This method also allow you to override an existing placeholder. * * @param string $name The placeholder name (including the delimiter char like %) * @param callable $callable A PHP callable */ public static function setPlaceholderFormatterDefinition($name, $callable) { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); } self::$formatters[$name] = $callable; } /** * Gets the placeholder formatter for a given name. * * @param string $name The placeholder name (including the delimiter char like %) * * @return callable|null A PHP callable */ public static function getPlaceholderFormatterDefinition($name) { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); } return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; } private function display() { if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { return; } $self = $this; $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) { if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { return $formatter($self); } return $matches[0]; }, $this->format)); } private function determineBestFormat() { switch ($this->output->getVerbosity()) { // OutputInterface::VERBOSITY_QUIET: display is disabled anyway case OutputInterface::VERBOSITY_VERBOSE: return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi'; case OutputInterface::VERBOSITY_VERY_VERBOSE: case OutputInterface::VERBOSITY_DEBUG: return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi'; default: return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi'; } } /** * Overwrites a previous message to the output. */ private function overwrite(string $message) { if ($this->output->isDecorated()) { $this->output->write("\x0D\x1B[2K"); $this->output->write($message); } else { $this->output->writeln($message); } } private function getCurrentTimeInMilliseconds() { return round(microtime(true) * 1000); } private static function initPlaceholderFormatters() { return [ 'indicator' => function (self $indicator) { return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)]; }, 'message' => function (self $indicator) { return $indicator->message; }, 'elapsed' => function (self $indicator) { return Helper::formatTime(time() - $indicator->startTime); }, 'memory' => function () { return Helper::formatMemory(memory_get_usage(true)); }, ]; } private static function initFormats() { return [ 'normal' => ' %indicator% %message%', 'normal_no_ansi' => ' %message%', 'verbose' => ' %indicator% %message% (%elapsed:6s%)', 'verbose_no_ansi' => ' %message% (%elapsed:6s%)', 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)', 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', ]; } } InputAwareHelper.php 0000604 00000001353 15225250340 0010470 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Input\InputAwareInterface; use Symfony\Component\Console\Input\InputInterface; /** * An implementation of InputAwareInterface for Helpers. * * @author Wouter J <waldio.webdesign@gmail.com> */ abstract class InputAwareHelper extends Helper implements InputAwareInterface { protected $input; /** * {@inheritdoc} */ public function setInput(InputInterface $input) { $this->input = $input; } } Dumper.php 0000604 00000003750 15225250340 0006510 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Output\OutputInterface; use Symfony\Component\VarDumper\Cloner\ClonerInterface; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; /** * @author Roland Franssen <franssen.roland@gmail.com> */ final class Dumper { private $output; private $dumper; private $cloner; private $handler; public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null) { $this->output = $output; $this->dumper = $dumper; $this->cloner = $cloner; if (class_exists(CliDumper::class)) { $this->handler = function ($var): string { $dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR); $dumper->setColors($this->output->isDecorated()); return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true)); }; } else { $this->handler = function ($var): string { switch (true) { case null === $var: return 'null'; case true === $var: return 'true'; case false === $var: return 'false'; case \is_string($var): return '"'.$var.'"'; default: return rtrim(print_r($var, true)); } }; } } public function __invoke($var): string { return ($this->handler)($var); } } DescriptorHelper.php 0000604 00000004621 15225250340 0010530 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Descriptor\DescriptorInterface; use Symfony\Component\Console\Descriptor\JsonDescriptor; use Symfony\Component\Console\Descriptor\MarkdownDescriptor; use Symfony\Component\Console\Descriptor\TextDescriptor; use Symfony\Component\Console\Descriptor\XmlDescriptor; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Output\OutputInterface; /** * This class adds helper method to describe objects in various formats. * * @author Jean-François Simon <contact@jfsimon.fr> */ class DescriptorHelper extends Helper { /** * @var DescriptorInterface[] */ private $descriptors = []; public function __construct() { $this ->register('txt', new TextDescriptor()) ->register('xml', new XmlDescriptor()) ->register('json', new JsonDescriptor()) ->register('md', new MarkdownDescriptor()) ; } /** * Describes an object if supported. * * Available options are: * * format: string, the output format name * * raw_text: boolean, sets output type as raw * * @param object $object * * @throws InvalidArgumentException when the given format is not supported */ public function describe(OutputInterface $output, $object, array $options = []) { $options = array_merge([ 'raw_text' => false, 'format' => 'txt', ], $options); if (!isset($this->descriptors[$options['format']])) { throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); } $descriptor = $this->descriptors[$options['format']]; $descriptor->describe($output, $object, $options); } /** * Registers a descriptor. * * @param string $format * * @return $this */ public function register($format, DescriptorInterface $descriptor) { $this->descriptors[$format] = $descriptor; return $this; } /** * {@inheritdoc} */ public function getName() { return 'descriptor'; } } TableSeparator.php 0000604 00000001023 15225250340 0010153 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Helper; /** * Marks a row as being a separator. * * @author Fabien Potencier <fabien@symfony.com> */ class TableSeparator extends TableCell { public function __construct(array $options = []) { parent::__construct('', $options); } } Helper.php 0000604 00000007260 15225250340 0006473 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Formatter\OutputFormatterInterface; /** * Helper is the base class for all helper classes. * * @author Fabien Potencier <fabien@symfony.com> */ abstract class Helper implements HelperInterface { protected $helperSet = null; /** * {@inheritdoc} */ public function setHelperSet(HelperSet $helperSet = null) { $this->helperSet = $helperSet; } /** * {@inheritdoc} */ public function getHelperSet() { return $this->helperSet; } /** * Returns the length of a string, using mb_strwidth if it is available. * * @param string $string The string to check its length * * @return int The length of the string */ public static function strlen($string) { if (false === $encoding = mb_detect_encoding($string, null, true)) { return \strlen($string); } return mb_strwidth($string, $encoding); } /** * Returns the subset of a string, using mb_substr if it is available. * * @param string $string String to subset * @param int $from Start offset * @param int|null $length Length to read * * @return string The string subset */ public static function substr($string, $from, $length = null) { if (false === $encoding = mb_detect_encoding($string, null, true)) { return substr($string, $from, $length); } return mb_substr($string, $from, $length, $encoding); } public static function formatTime($secs) { static $timeFormats = [ [0, '< 1 sec'], [1, '1 sec'], [2, 'secs', 1], [60, '1 min'], [120, 'mins', 60], [3600, '1 hr'], [7200, 'hrs', 3600], [86400, '1 day'], [172800, 'days', 86400], ]; foreach ($timeFormats as $index => $format) { if ($secs >= $format[0]) { if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0]) || $index == \count($timeFormats) - 1 ) { if (2 == \count($format)) { return $format[1]; } return floor($secs / $format[2]).' '.$format[1]; } } } } public static function formatMemory($memory) { if ($memory >= 1024 * 1024 * 1024) { return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); } if ($memory >= 1024 * 1024) { return sprintf('%.1f MiB', $memory / 1024 / 1024); } if ($memory >= 1024) { return sprintf('%d KiB', $memory / 1024); } return sprintf('%d B', $memory); } public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string) { return self::strlen(self::removeDecoration($formatter, $string)); } public static function removeDecoration(OutputFormatterInterface $formatter, $string) { $isDecorated = $formatter->isDecorated(); $formatter->setDecorated(false); // remove <...> formatting $string = $formatter->format($string); // remove already formatted characters $string = preg_replace("/\033\[[^m]*m/", '', $string); $formatter->setDecorated($isDecorated); return $string; } } SymfonyQuestionHelper.php 0000604 00000005735 15225250340 0011615 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Formatter\OutputFormatter; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Style\SymfonyStyle; /** * Symfony Style Guide compliant question helper. * * @author Kevin Bond <kevinbond@gmail.com> */ class SymfonyQuestionHelper extends QuestionHelper { /** * {@inheritdoc} */ protected function writePrompt(OutputInterface $output, Question $question) { $text = OutputFormatter::escapeTrailingBackslash($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(isset($choices[$default]) ? $choices[$default] : $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(' > '); } /** * {@inheritdoc} */ protected function writeError(OutputInterface $output, \Exception $error) { if ($output instanceof SymfonyStyle) { $output->newLine(); $output->error($error->getMessage()); return; } parent::writeError($output, $error); } } ProgressBar.php 0000604 00000040161 15225250340 0007502 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Exception\LogicException; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Terminal; /** * The ProgressBar provides helpers to display progress output. * * @author Fabien Potencier <fabien@symfony.com> * @author Chris Jones <leeked@gmail.com> */ final class ProgressBar { private $barWidth = 28; private $barChar; private $emptyBarChar = '-'; private $progressChar = '>'; private $format; private $internalFormat; private $redrawFreq = 1; private $output; private $step = 0; private $max; private $startTime; private $stepWidth; private $percent = 0.0; private $formatLineCount; private $messages = []; private $overwrite = true; private $terminal; private $firstRun = true; private static $formatters; private static $formats; /** * @param OutputInterface $output An OutputInterface instance * @param int $max Maximum steps (0 if unknown) */ public function __construct(OutputInterface $output, int $max = 0) { if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } $this->output = $output; $this->setMaxSteps($max); $this->terminal = new Terminal(); if (!$this->output->isDecorated()) { // disable overwrite when output does not support ANSI codes. $this->overwrite = false; // set a reasonable redraw frequency so output isn't flooded $this->setRedrawFrequency($max / 10); } $this->startTime = time(); } /** * Sets a placeholder formatter for a given name. * * This method also allow you to override an existing placeholder. * * @param string $name The placeholder name (including the delimiter char like %) * @param callable $callable A PHP callable */ public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); } self::$formatters[$name] = $callable; } /** * Gets the placeholder formatter for a given name. * * @param string $name The placeholder name (including the delimiter char like %) * * @return callable|null A PHP callable */ public static function getPlaceholderFormatterDefinition(string $name): ?callable { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); } return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; } /** * Sets a format for a given name. * * This method also allow you to override an existing format. * * @param string $name The format name * @param string $format A format string */ public static function setFormatDefinition(string $name, string $format): void { if (!self::$formats) { self::$formats = self::initFormats(); } self::$formats[$name] = $format; } /** * Gets the format for a given name. * * @param string $name The format name * * @return string|null A format string */ public static function getFormatDefinition(string $name): ?string { if (!self::$formats) { self::$formats = self::initFormats(); } return isset(self::$formats[$name]) ? self::$formats[$name] : null; } /** * Associates a text with a named placeholder. * * The text is displayed when the progress bar is rendered but only * when the corresponding placeholder is part of the custom format line * (by wrapping the name with %). * * @param string $message The text to associate with the placeholder * @param string $name The name of the placeholder */ public function setMessage(string $message, string $name = 'message') { $this->messages[$name] = $message; } public function getMessage(string $name = 'message') { return $this->messages[$name]; } public function getStartTime(): int { return $this->startTime; } public function getMaxSteps(): int { return $this->max; } public function getProgress(): int { return $this->step; } private function getStepWidth(): int { return $this->stepWidth; } public function getProgressPercent(): float { return $this->percent; } public function setBarWidth(int $size) { $this->barWidth = max(1, $size); } public function getBarWidth(): int { return $this->barWidth; } public function setBarCharacter(string $char) { $this->barChar = $char; } public function getBarCharacter(): string { if (null === $this->barChar) { return $this->max ? '=' : $this->emptyBarChar; } return $this->barChar; } public function setEmptyBarCharacter(string $char) { $this->emptyBarChar = $char; } public function getEmptyBarCharacter(): string { return $this->emptyBarChar; } public function setProgressCharacter(string $char) { $this->progressChar = $char; } public function getProgressCharacter(): string { return $this->progressChar; } public function setFormat(string $format) { $this->format = null; $this->internalFormat = $format; } /** * Sets the redraw frequency. * * @param int|float $freq The frequency in steps */ public function setRedrawFrequency(int $freq) { $this->redrawFreq = max($freq, 1); } /** * Returns an iterator that will automatically update the progress bar when iterated. * * @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable */ public function iterate(iterable $iterable, int $max = null): iterable { $this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0)); foreach ($iterable as $key => $value) { yield $key => $value; $this->advance(); } $this->finish(); } /** * Starts the progress output. * * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged */ public function start(int $max = null) { $this->startTime = time(); $this->step = 0; $this->percent = 0.0; if (null !== $max) { $this->setMaxSteps($max); } $this->display(); } /** * Advances the progress output X steps. * * @param int $step Number of steps to advance */ public function advance(int $step = 1) { $this->setProgress($this->step + $step); } /** * Sets whether to overwrite the progressbar, false for new line. */ public function setOverwrite(bool $overwrite) { $this->overwrite = $overwrite; } public function setProgress(int $step) { if ($this->max && $step > $this->max) { $this->max = $step; } elseif ($step < 0) { $step = 0; } $prevPeriod = (int) ($this->step / $this->redrawFreq); $currPeriod = (int) ($step / $this->redrawFreq); $this->step = $step; $this->percent = $this->max ? (float) $this->step / $this->max : 0; if ($prevPeriod !== $currPeriod || $this->max === $step) { $this->display(); } } public function setMaxSteps(int $max) { $this->format = null; $this->max = max(0, $max); $this->stepWidth = $this->max ? Helper::strlen((string) $this->max) : 4; } /** * Finishes the progress output. */ public function finish(): void { if (!$this->max) { $this->max = $this->step; } if ($this->step === $this->max && !$this->overwrite) { // prevent double 100% output return; } $this->setProgress($this->max); } /** * Outputs the current progress string. */ public function display(): void { if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { return; } if (null === $this->format) { $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); } $this->overwrite($this->buildLine()); } /** * Removes the progress bar from the current line. * * This is useful if you wish to write some output * while a progress bar is running. * Call display() to show the progress bar again. */ public function clear(): void { if (!$this->overwrite) { return; } if (null === $this->format) { $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); } $this->overwrite(''); } private function setRealFormat(string $format) { // try to use the _nomax variant if available if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { $this->format = self::getFormatDefinition($format.'_nomax'); } elseif (null !== self::getFormatDefinition($format)) { $this->format = self::getFormatDefinition($format); } else { $this->format = $format; } $this->formatLineCount = substr_count($this->format, "\n"); } /** * Overwrites a previous message to the output. */ private function overwrite(string $message): void { if ($this->overwrite) { if (!$this->firstRun) { if ($this->output instanceof ConsoleSectionOutput) { $lines = floor(Helper::strlen($message) / $this->terminal->getWidth()) + $this->formatLineCount + 1; $this->output->clear($lines); } else { // Erase previous lines if ($this->formatLineCount > 0) { $message = str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount).$message; } // Move the cursor to the beginning of the line and erase the line $message = "\x0D\x1B[2K$message"; } } } elseif ($this->step > 0) { $message = PHP_EOL.$message; } $this->firstRun = false; $this->output->write($message); } private function determineBestFormat(): string { switch ($this->output->getVerbosity()) { // OutputInterface::VERBOSITY_QUIET: display is disabled anyway case OutputInterface::VERBOSITY_VERBOSE: return $this->max ? 'verbose' : 'verbose_nomax'; case OutputInterface::VERBOSITY_VERY_VERBOSE: return $this->max ? 'very_verbose' : 'very_verbose_nomax'; case OutputInterface::VERBOSITY_DEBUG: return $this->max ? 'debug' : 'debug_nomax'; default: return $this->max ? 'normal' : 'normal_nomax'; } } private static function initPlaceholderFormatters(): array { return [ 'bar' => function (self $bar, OutputInterface $output) { $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth()); $display = str_repeat($bar->getBarCharacter(), $completeBars); if ($completeBars < $bar->getBarWidth()) { $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter()); $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); } return $display; }, 'elapsed' => function (self $bar) { return Helper::formatTime(time() - $bar->getStartTime()); }, 'remaining' => function (self $bar) { if (!$bar->getMaxSteps()) { throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); } if (!$bar->getProgress()) { $remaining = 0; } else { $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress())); } return Helper::formatTime($remaining); }, 'estimated' => function (self $bar) { if (!$bar->getMaxSteps()) { throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); } if (!$bar->getProgress()) { $estimated = 0; } else { $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps()); } return Helper::formatTime($estimated); }, 'memory' => function (self $bar) { return Helper::formatMemory(memory_get_usage(true)); }, 'current' => function (self $bar) { return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT); }, 'max' => function (self $bar) { return $bar->getMaxSteps(); }, 'percent' => function (self $bar) { return floor($bar->getProgressPercent() * 100); }, ]; } private static function initFormats(): array { return [ 'normal' => ' %current%/%max% [%bar%] %percent:3s%%', 'normal_nomax' => ' %current% [%bar%]', 'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', 'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', 'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', 'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', 'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', 'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%', ]; } private function buildLine(): string { $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i"; $callback = function ($matches) { if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) { $text = $formatter($this, $this->output); } elseif (isset($this->messages[$matches[1]])) { $text = $this->messages[$matches[1]]; } else { return $matches[0]; } if (isset($matches[2])) { $text = sprintf('%'.$matches[2], $text); } return $text; }; $line = preg_replace_callback($regex, $callback, $this->format); // gets string length for each sub line with multiline format $linesLength = array_map(function ($subLine) { return Helper::strlenWithoutDecoration($this->output->getFormatter(), rtrim($subLine, "\r")); }, explode("\n", $line)); $linesWidth = max($linesLength); $terminalWidth = $this->terminal->getWidth(); if ($linesWidth <= $terminalWidth) { return $line; } $this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth); return preg_replace_callback($regex, $callback, $this->format); } } ProcessHelper.php 0000604 00000012752 15225250340 0010034 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process; /** * The ProcessHelper class provides helpers to run external processes. * * @author Fabien Potencier <fabien@symfony.com> * * @final since Symfony 4.2 */ class ProcessHelper extends Helper { /** * Runs an external process. * * @param OutputInterface $output An OutputInterface instance * @param array|Process $cmd An instance of Process or an array of the command and arguments * @param string|null $error An error message that must be displayed if something went wrong * @param callable|null $callback A PHP callback to run whenever there is some * output available on STDOUT or STDERR * @param int $verbosity The threshold for verbosity * * @return Process The process that ran */ public function run(OutputInterface $output, $cmd, $error = null, callable $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE) { if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } $formatter = $this->getHelperSet()->get('debug_formatter'); if ($cmd instanceof Process) { $cmd = [$cmd]; } if (!\is_array($cmd)) { @trigger_error(sprintf('Passing a command as a string to "%s()" is deprecated since Symfony 4.2, pass it the command as an array of arguments instead.', __METHOD__), E_USER_DEPRECATED); $cmd = [method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline($cmd) : new Process($cmd)]; } if (\is_string($cmd[0] ?? null)) { $process = new Process($cmd); $cmd = []; } elseif (($cmd[0] ?? null) instanceof Process) { $process = $cmd[0]; unset($cmd[0]); } else { throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__)); } if ($verbosity <= $output->getVerbosity()) { $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine()))); } if ($output->isDebug()) { $callback = $this->wrapCallback($output, $process, $callback); } $process->run($callback, $cmd); if ($verbosity <= $output->getVerbosity()) { $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode()); $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); } if (!$process->isSuccessful() && null !== $error) { $output->writeln(sprintf('<error>%s</error>', $this->escapeString($error))); } return $process; } /** * Runs the process. * * This is identical to run() except that an exception is thrown if the process * exits with a non-zero exit code. * * @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 callable|null $callback A PHP callback to run whenever there is some * output available on STDOUT or STDERR * * @return Process The process that ran * * @throws ProcessFailedException * * @see run() */ public function mustRun(OutputInterface $output, $cmd, $error = null, callable $callback = null) { $process = $this->run($output, $cmd, $error, $callback); if (!$process->isSuccessful()) { throw new ProcessFailedException($process); } return $process; } /** * Wraps a Process callback to add debugging output. * * @param OutputInterface $output An OutputInterface interface * @param Process $process The Process * @param callable|null $callback A PHP callable * * @return callable */ public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null) { if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } $formatter = $this->getHelperSet()->get('debug_formatter'); return function ($type, $buffer) use ($output, $process, $callback, $formatter) { $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type)); if (null !== $callback) { $callback($type, $buffer); } }; } private function escapeString($str) { return str_replace('<', '\\<', $str); } /** * {@inheritdoc} */ public function getName() { return 'process'; } } HelperSet.php 0000604 00000004701 15225250340 0007144 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * 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\Command\Command; use Symfony\Component\Console\Exception\InvalidArgumentException; /** * HelperSet represents a set of helpers to be used with a command. * * @author Fabien Potencier <fabien@symfony.com> */ class HelperSet implements \IteratorAggregate { /** * @var Helper[] */ private $helpers = []; private $command; /** * @param Helper[] $helpers An array of helper */ public function __construct(array $helpers = []) { foreach ($helpers as $alias => $helper) { $this->set($helper, \is_int($alias) ? null : $alias); } } /** * Sets a helper. * * @param HelperInterface $helper The helper instance * @param string $alias An alias */ public function set(HelperInterface $helper, $alias = null) { $this->helpers[$helper->getName()] = $helper; if (null !== $alias) { $this->helpers[$alias] = $helper; } $helper->setHelperSet($this); } /** * Returns true if the helper if defined. * * @param string $name The helper name * * @return bool true if the helper is defined, false otherwise */ public function has($name) { return isset($this->helpers[$name]); } /** * Gets a helper value. * * @param string $name The helper name * * @return HelperInterface The helper instance * * @throws InvalidArgumentException if the helper is not defined */ public function get($name) { if (!$this->has($name)) { throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); } return $this->helpers[$name]; } public function setCommand(Command $command = null) { $this->command = $command; } /** * Gets the command associated with this helper set. * * @return Command A Command instance */ public function getCommand() { return $this->command; } /** * @return Helper[] */ public function getIterator() { return new \ArrayIterator($this->helpers); } } TableRows.php 0000604 00000001107 15225250340 0007150 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\Helper; /** * @internal */ class TableRows implements \IteratorAggregate { private $generator; public function __construct(callable $generator) { $this->generator = $generator; } public function getIterator() { $g = $this->generator; return $g(); } }