One Hat Cyber Team
Your IP:
216.73.216.101
Server IP:
198.54.114.155
Server:
Linux server71.web-hosting.com 4.18.0-513.18.1.lve.el8.x86_64 #1 SMP Thu Feb 22 12:55:50 UTC 2024 x86_64
Server Software:
LiteSpeed
PHP Version:
5.6.40
Create File
|
Create Folder
Execute
Dir :
~
/
proc
/
thread-self
/
root
/
proc
/
thread-self
/
cwd
/
Edit File:
Helper.tar
InputAwareHelper.php 0000644 00000001352 15111773711 0010501 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; /** * @return void */ public function setInput(InputInterface $input) { $this->input = $input; } } TableCellStyle.php 0000644 00000004245 15111773711 0010136 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 Yewhen Khoptynskyi <khoptynskyi@gmail.com> */ class TableCellStyle { public const DEFAULT_ALIGN = 'left'; private const TAG_OPTIONS = [ 'fg', 'bg', 'options', ]; private const ALIGN_MAP = [ 'left' => \STR_PAD_RIGHT, 'center' => \STR_PAD_BOTH, 'right' => \STR_PAD_LEFT, ]; private array $options = [ 'fg' => 'default', 'bg' => 'default', 'options' => null, 'align' => self::DEFAULT_ALIGN, 'cellFormat' => null, ]; public function __construct(array $options = []) { if ($diff = array_diff(array_keys($options), array_keys($this->options))) { throw new InvalidArgumentException(sprintf('The TableCellStyle does not support the following options: \'%s\'.', implode('\', \'', $diff))); } if (isset($options['align']) && !\array_key_exists($options['align'], self::ALIGN_MAP)) { throw new InvalidArgumentException(sprintf('Wrong align value. Value must be following: \'%s\'.', implode('\', \'', array_keys(self::ALIGN_MAP)))); } $this->options = array_merge($this->options, $options); } public function getOptions(): array { return $this->options; } /** * Gets options we need for tag for example fg, bg. * * @return string[] */ public function getTagOptions(): array { return array_filter( $this->getOptions(), fn ($key) => \in_array($key, self::TAG_OPTIONS) && isset($this->options[$key]), \ARRAY_FILTER_USE_KEY ); } public function getPadByAlign(): int { return self::ALIGN_MAP[$this->getOptions()['align']]; } public function getCellFormat(): ?string { return $this->getOptions()['cellFormat']; } } TableCell.php 0000644 00000003337 15111773711 0007116 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 string $value; private array $options = [ 'rowspan' => 1, 'colspan' => 1, 'style' => null, ]; 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))); } if (isset($options['style']) && !$options['style'] instanceof TableCellStyle) { throw new InvalidArgumentException('The style option must be an instance of "TableCellStyle".'); } $this->options = array_merge($this->options, $options); } /** * Returns the cell value. */ public function __toString(): string { return $this->value; } /** * Gets number of colspan. */ public function getColspan(): int { return (int) $this->options['colspan']; } /** * Gets number of rowspan. */ public function getRowspan(): int { return (int) $this->options['rowspan']; } public function getStyle(): ?TableCellStyle { return $this->options['style']; } } TableStyle.php 0000644 00000030723 15111773711 0007336 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 string $paddingChar = ' '; private string $horizontalOutsideBorderChar = '-'; private string $horizontalInsideBorderChar = '-'; private string $verticalOutsideBorderChar = '|'; private string $verticalInsideBorderChar = '|'; private string $crossingChar = '+'; private string $crossingTopRightChar = '+'; private string $crossingTopMidChar = '+'; private string $crossingTopLeftChar = '+'; private string $crossingMidRightChar = '+'; private string $crossingBottomRightChar = '+'; private string $crossingBottomMidChar = '+'; private string $crossingBottomLeftChar = '+'; private string $crossingMidLeftChar = '+'; private string $crossingTopLeftBottomChar = '+'; private string $crossingTopMidBottomChar = '+'; private string $crossingTopRightBottomChar = '+'; private string $headerTitleFormat = '<fg=black;bg=white;options=bold> %s </>'; private string $footerTitleFormat = '<fg=black;bg=white;options=bold> %s </>'; private string $cellHeaderFormat = '<info>%s</info>'; private string $cellRowFormat = '%s'; private string $cellRowContentFormat = ' %s '; private string $borderFormat = '%s'; private int $padType = \STR_PAD_RIGHT; /** * Sets padding character, used for cell padding. * * @return $this */ public function setPaddingChar(string $paddingChar): static { if (!$paddingChar) { throw new LogicException('The padding char must not be empty.'); } $this->paddingChar = $paddingChar; return $this; } /** * Gets padding character, used for cell padding. */ public function getPaddingChar(): string { 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> * * @return $this */ public function setHorizontalBorderChars(string $outside, string $inside = null): static { $this->horizontalOutsideBorderChar = $outside; $this->horizontalInsideBorderChar = $inside ?? $outside; return $this; } /** * 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> * * @return $this */ public function setVerticalBorderChars(string $outside, string $inside = null): static { $this->verticalOutsideBorderChar = $outside; $this->verticalInsideBorderChar = $inside ?? $outside; return $this; } /** * Gets border characters. * * @internal */ public function getBorderChars(): array { 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 * * @return $this */ 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): static { $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); } /** * Gets crossing character. */ public function getCrossingChar(): string { 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. * * @return $this */ public function setCellHeaderFormat(string $cellHeaderFormat): static { $this->cellHeaderFormat = $cellHeaderFormat; return $this; } /** * Gets header cell format. */ public function getCellHeaderFormat(): string { return $this->cellHeaderFormat; } /** * Sets row cell format. * * @return $this */ public function setCellRowFormat(string $cellRowFormat): static { $this->cellRowFormat = $cellRowFormat; return $this; } /** * Gets row cell format. */ public function getCellRowFormat(): string { return $this->cellRowFormat; } /** * Sets row cell content format. * * @return $this */ public function setCellRowContentFormat(string $cellRowContentFormat): static { $this->cellRowContentFormat = $cellRowContentFormat; return $this; } /** * Gets row cell content format. */ public function getCellRowContentFormat(): string { return $this->cellRowContentFormat; } /** * Sets table border format. * * @return $this */ public function setBorderFormat(string $borderFormat): static { $this->borderFormat = $borderFormat; return $this; } /** * Gets table border format. */ public function getBorderFormat(): string { return $this->borderFormat; } /** * Sets cell padding type. * * @return $this */ public function setPadType(int $padType): static { 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. */ public function getPadType(): int { return $this->padType; } public function getHeaderTitleFormat(): string { return $this->headerTitleFormat; } /** * @return $this */ public function setHeaderTitleFormat(string $format): static { $this->headerTitleFormat = $format; return $this; } public function getFooterTitleFormat(): string { return $this->footerTitleFormat; } /** * @return $this */ public function setFooterTitleFormat(string $format): static { $this->footerTitleFormat = $format; return $this; } } Table.php 0000644 00000075250 15111773711 0006321 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 const DISPLAY_ORIENTATION_DEFAULT = 'default'; private const DISPLAY_ORIENTATION_HORIZONTAL = 'horizontal'; private const DISPLAY_ORIENTATION_VERTICAL = 'vertical'; private ?string $headerTitle = null; private ?string $footerTitle = null; private array $headers = []; private array $rows = []; private array $effectiveColumnWidths = []; private int $numberOfColumns; private OutputInterface $output; private TableStyle $style; private array $columnStyles = []; private array $columnWidths = []; private array $columnMaxWidths = []; private bool $rendered = false; private string $displayOrientation = self::DISPLAY_ORIENTATION_DEFAULT; private static array $styles; public function __construct(OutputInterface $output) { $this->output = $output; self::$styles ??= self::initStyles(); $this->setStyle('default'); } /** * Sets a style definition. * * @return void */ public static function setStyleDefinition(string $name, TableStyle $style) { self::$styles ??= self::initStyles(); self::$styles[$name] = $style; } /** * Gets a style definition by name. */ public static function getStyleDefinition(string $name): TableStyle { self::$styles ??= self::initStyles(); return self::$styles[$name] ?? throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); } /** * Sets table style. * * @return $this */ public function setStyle(TableStyle|string $name): static { $this->style = $this->resolveStyle($name); return $this; } /** * Gets the current table style. */ public function getStyle(): TableStyle { return $this->style; } /** * Sets table column style. * * @param TableStyle|string $name The style name or a TableStyle instance * * @return $this */ public function setColumnStyle(int $columnIndex, TableStyle|string $name): static { $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. */ public function getColumnStyle(int $columnIndex): TableStyle { return $this->columnStyles[$columnIndex] ?? $this->getStyle(); } /** * Sets the minimum width of a column. * * @return $this */ public function setColumnWidth(int $columnIndex, int $width): static { $this->columnWidths[$columnIndex] = $width; return $this; } /** * Sets the minimum width of all columns. * * @return $this */ public function setColumnWidths(array $widths): static { $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): static { 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_debug_type($this->output->getFormatter()))); } $this->columnMaxWidths[$columnIndex] = $width; return $this; } /** * @return $this */ public function setHeaders(array $headers): static { $headers = array_values($headers); if ($headers && !\is_array($headers[0])) { $headers = [$headers]; } $this->headers = $headers; return $this; } /** * @return $this */ public function setRows(array $rows) { $this->rows = []; return $this->addRows($rows); } /** * @return $this */ public function addRows(array $rows): static { foreach ($rows as $row) { $this->addRow($row); } return $this; } /** * @return $this */ public function addRow(TableSeparator|array $row): static { if ($row instanceof TableSeparator) { $this->rows[] = $row; return $this; } $this->rows[] = array_values($row); return $this; } /** * Adds a row to the table, and re-renders the table. * * @return $this */ public function appendRow(TableSeparator|array $row): static { 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; } /** * @return $this */ public function setRow(int|string $column, array $row): static { $this->rows[$column] = $row; return $this; } /** * @return $this */ public function setHeaderTitle(?string $title): static { $this->headerTitle = $title; return $this; } /** * @return $this */ public function setFooterTitle(?string $title): static { $this->footerTitle = $title; return $this; } /** * @return $this */ public function setHorizontal(bool $horizontal = true): static { $this->displayOrientation = $horizontal ? self::DISPLAY_ORIENTATION_HORIZONTAL : self::DISPLAY_ORIENTATION_DEFAULT; return $this; } /** * @return $this */ public function setVertical(bool $vertical = true): static { $this->displayOrientation = $vertical ? self::DISPLAY_ORIENTATION_VERTICAL : self::DISPLAY_ORIENTATION_DEFAULT; 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 | * +---------------+-----------------------+------------------+ * * @return void */ public function render() { $divider = new TableSeparator(); $isCellWithColspan = static fn ($cell) => $cell instanceof TableCell && $cell->getColspan() >= 2; $horizontal = self::DISPLAY_ORIENTATION_HORIZONTAL === $this->displayOrientation; $vertical = self::DISPLAY_ORIENTATION_VERTICAL === $this->displayOrientation; $rows = []; if ($horizontal) { foreach ($this->headers[0] ?? [] as $i => $header) { $rows[$i] = [$header]; foreach ($this->rows as $row) { if ($row instanceof TableSeparator) { continue; } if (isset($row[$i])) { $rows[$i][] = $row[$i]; } elseif ($isCellWithColspan($rows[$i][0])) { // Noop, there is a "title" } else { $rows[$i][] = null; } } } } elseif ($vertical) { $formatter = $this->output->getFormatter(); $maxHeaderLength = array_reduce($this->headers[0] ?? [], static fn ($max, $header) => max($max, Helper::width(Helper::removeDecoration($formatter, $header))), 0); foreach ($this->rows as $row) { if ($row instanceof TableSeparator) { continue; } if ($rows) { $rows[] = [$divider]; } $containsColspan = false; foreach ($row as $cell) { if ($containsColspan = $isCellWithColspan($cell)) { break; } } $headers = $this->headers[0] ?? []; $maxRows = max(\count($headers), \count($row)); for ($i = 0; $i < $maxRows; ++$i) { $cell = (string) ($row[$i] ?? ''); if ($headers && !$containsColspan) { $rows[] = [sprintf( '<comment>%s</>: %s', str_pad($headers[$i] ?? '', $maxHeaderLength, ' ', \STR_PAD_LEFT), $cell )]; } elseif ('' !== $cell) { $rows[] = [$cell]; } } } } else { $rows = array_merge($this->headers, [$divider], $this->rows); } $this->calculateNumberOfColumns($rows); $rowGroups = $this->buildTableRows($rows); $this->calculateColumnsWidth($rowGroups); $isHeader = !$horizontal; $isFirstRow = $horizontal; $hasTitle = (bool) $this->headerTitle; foreach ($rowGroups as $rowGroup) { $isHeaderSeparatorRendered = false; foreach ($rowGroup as $row) { if ($divider === $row) { $isHeader = false; $isFirstRow = true; continue; } if ($row instanceof TableSeparator) { $this->renderRowSeparator(); continue; } if (!$row) { continue; } if ($isHeader && !$isHeaderSeparatorRendered) { $this->renderRowSeparator( $isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM, $hasTitle ? $this->headerTitle : null, $hasTitle ? $this->style->getHeaderTitleFormat() : null ); $hasTitle = false; $isHeaderSeparatorRendered = true; } if ($isFirstRow) { $this->renderRowSeparator( $isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM, $hasTitle ? $this->headerTitle : null, $hasTitle ? $this->style->getHeaderTitleFormat() : null ); $isFirstRow = false; $hasTitle = false; } if ($vertical) { $isHeader = false; $isFirstRow = false; } if ($horizontal) { $this->renderRow($row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat()); } else { $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): void { if (!$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) { [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]]; } elseif (self::SEPARATOR_TOP === $type) { [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]]; } elseif (self::SEPARATOR_TOP_BOTTOM === $type) { [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]]; } else { [$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::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title))); $markupLength = Helper::width($markup); if ($titleLength > $limit = $markupLength - 4) { $titleLength = $limit; $formatLength = Helper::width(Helper::removeDecoration($formatter, sprintf($titleFormat, ''))); $formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...'); } $titleStart = intdiv($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(int $type = self::BORDER_OUTSIDE): string { $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, string $firstCellFormat = null): void { $rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE); $columns = $this->getRowColumns($row); $last = \count($columns) - 1; foreach ($columns as $i => $column) { if ($firstCellFormat && 0 === $i) { $rowContent .= $this->renderCell($row, $column, $firstCellFormat); } else { $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): string { $cell = $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::length($cell) - Helper::length(Helper::removeDecoration($this->output->getFormatter(), $cell)); $content = sprintf($style->getCellRowContentFormat(), $cell); $padType = $style->getPadType(); if ($cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle) { $isNotStyledByTag = !preg_match('/^<(\w+|(\w+=[\w,]+;?)*)>.+<\/(\w+|(\w+=\w+;?)*)?>$/', $cell); if ($isNotStyledByTag) { $cellFormat = $cell->getStyle()->getCellFormat(); if (!\is_string($cellFormat)) { $tag = http_build_query($cell->getStyle()->getTagOptions(), '', ';'); $cellFormat = '<'.$tag.'>%s</>'; } if (str_contains($content, '</>')) { $content = str_replace('</>', '', $content); $width -= 3; } if (str_contains($content, '<fg=default;bg=default>')) { $content = str_replace('<fg=default;bg=default>', '', $content); $width -= \strlen('<fg=default;bg=default>'); } } $padType = $cell->getStyle()->getPadByAlign(); } return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType)); } /** * Calculate number of columns for this table. */ private function calculateNumberOfColumns(array $rows): void { $columns = [0]; foreach ($rows as $row) { if ($row instanceof TableSeparator) { continue; } $columns[] = $this->getNumberOfColumns($row); } $this->numberOfColumns = max($columns); } private function buildTableRows(array $rows): TableRows { /** @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::width(Helper::removeDecoration($formatter, $cell)) > $this->columnMaxWidths[$column]) { $cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan); } if (!str_contains($cell ?? '', "\n")) { continue; } $escaped = implode("\n", array_map(OutputFormatter::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 { if (!\array_key_exists($rowKey, $unmergedRows) || !\array_key_exists($lineKey, $unmergedRows[$rowKey])) { $unmergedRows[$rowKey][$lineKey] = $this->copyRow($rows, $rowKey); } $unmergedRows[$rowKey][$lineKey][$column] = $line; } } } } return new TableRows(function () use ($rows, $unmergedRows): \Traversable { foreach ($rows as $rowKey => $row) { $rowGroup = [$row instanceof TableSeparator ? $row : $this->fillCells($row)]; if (isset($unmergedRows[$rowKey])) { foreach ($unmergedRows[$rowKey] as $row) { $rowGroup[] = $row instanceof TableSeparator ? $row : $this->fillCells($row); } } yield $rowGroup; } }); } 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 } if ($this->rows) { ++$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) && !$cell instanceof \Stringable) { throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell))); } if ($cell instanceof TableCell && $cell->getRowspan() > 1) { $nbLines = $cell->getRowspan() - 1; $lines = [$cell]; if (str_contains($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(), 'style' => $cell->getStyle()]); 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 = $lines[$unmergedRowKey - $line] ?? ''; $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]); 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(iterable $row): iterable { $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 $groups): void { for ($column = 0; $column < $this->numberOfColumns; ++$column) { $lengths = []; foreach ($groups as $group) { foreach ($group 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::width($textContent); if ($textLength > 0) { $contentColumns = mb_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::width($this->style->getCellRowContentFormat()) - 2; } } private function getColumnSeparatorWidth(): int { return Helper::width(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::width(Helper::removeDecoration($this->output->getFormatter(), $cell)); } $columnWidth = $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(): void { $this->effectiveColumnWidths = []; unset($this->numberOfColumns); } /** * @return array<string, TableStyle> */ private static function initStyles(): array { $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(TableStyle|string $name): TableStyle { if ($name instanceof TableStyle) { return $name; } return self::$styles[$name] ?? throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); } } DescriptorHelper.php 0000644 00000005115 15111773711 0010541 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\ReStructuredTextDescriptor; 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 array $descriptors = []; public function __construct() { $this ->register('txt', new TextDescriptor()) ->register('xml', new XmlDescriptor()) ->register('json', new JsonDescriptor()) ->register('md', new MarkdownDescriptor()) ->register('rst', new ReStructuredTextDescriptor()) ; } /** * Describes an object if supported. * * Available options are: * * format: string, the output format name * * raw_text: boolean, sets output type as raw * * @return void * * @throws InvalidArgumentException when the given format is not supported */ public function describe(OutputInterface $output, ?object $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. * * @return $this */ public function register(string $format, DescriptorInterface $descriptor): static { $this->descriptors[$format] = $descriptor; return $this; } public function getName(): string { return 'descriptor'; } public function getFormats(): array { return array_keys($this->descriptors); } } ProcessHelper.php 0000644 00000011217 15111773711 0010041 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 */ class ProcessHelper extends Helper { /** * Runs an external process. * * @param array|Process $cmd An instance of Process or an array of the command and arguments * @param callable|null $callback A PHP callback to run whenever there is some * output available on STDOUT or STDERR */ public function run(OutputInterface $output, array|Process $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process { if (!class_exists(Process::class)) { throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".'); } if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } $formatter = $this->getHelperSet()->get('debug_formatter'); if ($cmd instanceof Process) { $cmd = [$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 array|Process $cmd An instance of Process or a command to run * @param callable|null $callback A PHP callback to run whenever there is some * output available on STDOUT or STDERR * * @throws ProcessFailedException * * @see run() */ public function mustRun(OutputInterface $output, array|Process $cmd, string $error = null, callable $callback = null): Process { $process = $this->run($output, $cmd, $error, $callback); if (!$process->isSuccessful()) { throw new ProcessFailedException($process); } return $process; } /** * Wraps a Process callback to add debugging output. */ public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null): callable { 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(string $str): string { return str_replace('<', '\\<', $str); } public function getName(): string { return 'process'; } } TableRows.php 0000644 00000001116 15111773711 0007162 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 \Closure $generator; public function __construct(\Closure $generator) { $this->generator = $generator; } public function getIterator(): \Traversable { return ($this->generator)(); } } TableSeparator.php 0000644 00000001023 15111773711 0010165 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); } } SymfonyQuestionHelper.php 0000644 00000006315 15111773711 0011622 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 { /** * @return void */ protected function writePrompt(OutputInterface $output, Question $question) { $text = OutputFormatter::escapeTrailingBackslash($question->getQuestion()); $default = $question->getDefault(); if ($question->isMultiline()) { $text .= sprintf(' (press %s to continue)', $this->getEofShortcut()); } 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] ?? $default)); break; default: $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($default)); } $output->writeln($text); $prompt = ' > '; if ($question instanceof ChoiceQuestion) { $output->writeln($this->formatChoiceQuestionChoices($question, 'comment')); $prompt = $question->getPrompt(); } $output->write($prompt); } /** * @return void */ protected function writeError(OutputInterface $output, \Exception $error) { if ($output instanceof SymfonyStyle) { $output->newLine(); $output->error($error->getMessage()); return; } parent::writeError($output, $error); } private function getEofShortcut(): string { if ('Windows' === \PHP_OS_FAMILY) { return '<comment>Ctrl+Z</comment> then <comment>Enter</comment>'; } return '<comment>Ctrl+D</comment>'; } } ProgressIndicator.php 0000644 00000015515 15111773711 0010731 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 const FORMATS = [ '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%)', ]; private OutputInterface $output; private int $startTime; private ?string $format = null; private ?string $message = null; private array $indicatorValues; private int $indicatorCurrent; private int $indicatorChangeInterval; private float $indicatorUpdateTime; private bool $started = false; /** * @var array<string, callable> */ private static array $formatters; /** * @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; $format ??= $this->determineBestFormat(); $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. * * @return void */ public function setMessage(?string $message) { $this->message = $message; $this->display(); } /** * Starts the indicator output. * * @return void */ public function start(string $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. * * @return void */ 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. * * @return void */ public function finish(string $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. */ public static function getFormatDefinition(string $name): ?string { return self::FORMATS[$name] ?? null; } /** * Sets a placeholder formatter for a given name. * * This method also allow you to override an existing placeholder. * * @return void */ public static function setPlaceholderFormatterDefinition(string $name, callable $callable) { self::$formatters ??= self::initPlaceholderFormatters(); self::$formatters[$name] = $callable; } /** * Gets the placeholder formatter for a given name (including the delimiter char like %). */ public static function getPlaceholderFormatterDefinition(string $name): ?callable { self::$formatters ??= self::initPlaceholderFormatters(); return self::$formatters[$name] ?? null; } private function display(): void { if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { return; } $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) { if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) { return $formatter($this); } return $matches[0]; }, $this->format ?? '')); } private function determineBestFormat(): string { return match ($this->output->getVerbosity()) { // OutputInterface::VERBOSITY_QUIET: display is disabled anyway OutputInterface::VERBOSITY_VERBOSE => $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi', OutputInterface::VERBOSITY_VERY_VERBOSE, OutputInterface::VERBOSITY_DEBUG => $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi', default => $this->output->isDecorated() ? 'normal' : 'normal_no_ansi', }; } /** * Overwrites a previous message to the output. */ private function overwrite(string $message): void { if ($this->output->isDecorated()) { $this->output->write("\x0D\x1B[2K"); $this->output->write($message); } else { $this->output->writeln($message); } } private function getCurrentTimeInMilliseconds(): float { return round(microtime(true) * 1000); } /** * @return array<string, \Closure> */ private static function initPlaceholderFormatters(): array { return [ 'indicator' => fn (self $indicator) => $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)], 'message' => fn (self $indicator) => $indicator->message, 'elapsed' => fn (self $indicator) => Helper::formatTime(time() - $indicator->startTime), 'memory' => fn () => Helper::formatMemory(memory_get_usage(true)), ]; } } ProgressBar.php 0000644 00000046207 15111773711 0007523 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\Cursor; 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 { public const FORMAT_VERBOSE = 'verbose'; public const FORMAT_VERY_VERBOSE = 'very_verbose'; public const FORMAT_DEBUG = 'debug'; public const FORMAT_NORMAL = 'normal'; private const FORMAT_VERBOSE_NOMAX = 'verbose_nomax'; private const FORMAT_VERY_VERBOSE_NOMAX = 'very_verbose_nomax'; private const FORMAT_DEBUG_NOMAX = 'debug_nomax'; private const FORMAT_NORMAL_NOMAX = 'normal_nomax'; private int $barWidth = 28; private string $barChar; private string $emptyBarChar = '-'; private string $progressChar = '>'; private ?string $format = null; private ?string $internalFormat = null; private ?int $redrawFreq = 1; private int $writeCount = 0; private float $lastWriteTime = 0; private float $minSecondsBetweenRedraws = 0; private float $maxSecondsBetweenRedraws = 1; private OutputInterface $output; private int $step = 0; private int $startingStep = 0; private ?int $max = null; private int $startTime; private int $stepWidth; private float $percent = 0.0; private array $messages = []; private bool $overwrite = true; private Terminal $terminal; private ?string $previousMessage = null; private Cursor $cursor; private array $placeholders = []; private static array $formatters; private static array $formats; /** * @param int $max Maximum steps (0 if unknown) */ public function __construct(OutputInterface $output, int $max = 0, float $minSecondsBetweenRedraws = 1 / 25) { if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } $this->output = $output; $this->setMaxSteps($max); $this->terminal = new Terminal(); if (0 < $minSecondsBetweenRedraws) { $this->redrawFreq = null; $this->minSecondsBetweenRedraws = $minSecondsBetweenRedraws; } 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->redrawFreq = null; } $this->startTime = time(); $this->cursor = new Cursor($output); } /** * Sets a placeholder formatter for a given name, globally for all instances of ProgressBar. * * This method also allow you to override an existing placeholder. * * @param string $name The placeholder name (including the delimiter char like %) * @param callable(ProgressBar):string $callable A PHP callable */ public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void { 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 %) */ public static function getPlaceholderFormatterDefinition(string $name): ?callable { self::$formatters ??= self::initPlaceholderFormatters(); return self::$formatters[$name] ?? null; } /** * Sets a placeholder formatter for a given name, for this instance only. * * @param callable(ProgressBar):string $callable A PHP callable */ public function setPlaceholderFormatter(string $name, callable $callable): void { $this->placeholders[$name] = $callable; } /** * Gets the placeholder formatter for a given name. * * @param string $name The placeholder name (including the delimiter char like %) */ public function getPlaceholderFormatter(string $name): ?callable { return $this->placeholders[$name] ?? $this::getPlaceholderFormatterDefinition($name); } /** * 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 { self::$formats ??= self::initFormats(); self::$formats[$name] = $format; } /** * Gets the format for a given name. * * @param string $name The format name */ public static function getFormatDefinition(string $name): ?string { self::$formats ??= self::initFormats(); return 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'): void { $this->messages[$name] = $message; } public function getMessage(string $name = 'message'): string { 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 getBarOffset(): float { return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth); } public function getEstimated(): float { if (0 === $this->step || $this->step === $this->startingStep) { return 0; } return round((time() - $this->startTime) / ($this->step - $this->startingStep) * $this->max); } public function getRemaining(): float { if (!$this->step) { return 0; } return round((time() - $this->startTime) / ($this->step - $this->startingStep) * ($this->max - $this->step)); } public function setBarWidth(int $size): void { $this->barWidth = max(1, $size); } public function getBarWidth(): int { return $this->barWidth; } public function setBarCharacter(string $char): void { $this->barChar = $char; } public function getBarCharacter(): string { return $this->barChar ?? ($this->max ? '=' : $this->emptyBarChar); } public function setEmptyBarCharacter(string $char): void { $this->emptyBarChar = $char; } public function getEmptyBarCharacter(): string { return $this->emptyBarChar; } public function setProgressCharacter(string $char): void { $this->progressChar = $char; } public function getProgressCharacter(): string { return $this->progressChar; } public function setFormat(string $format): void { $this->format = null; $this->internalFormat = $format; } /** * Sets the redraw frequency. * * @param int|null $freq The frequency in steps */ public function setRedrawFrequency(?int $freq): void { $this->redrawFreq = null !== $freq ? max(1, $freq) : null; } public function minSecondsBetweenRedraws(float $seconds): void { $this->minSecondsBetweenRedraws = $seconds; } public function maxSecondsBetweenRedraws(float $seconds): void { $this->maxSecondsBetweenRedraws = $seconds; } /** * 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 * @param int $startAt The starting point of the bar (useful e.g. when resuming a previously started bar) */ public function start(int $max = null, int $startAt = 0): void { $this->startTime = time(); $this->step = $startAt; $this->startingStep = $startAt; $startAt > 0 ? $this->setProgress($startAt) : $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): void { $this->setProgress($this->step + $step); } /** * Sets whether to overwrite the progressbar, false for new line. */ public function setOverwrite(bool $overwrite): void { $this->overwrite = $overwrite; } public function setProgress(int $step): void { if ($this->max && $step > $this->max) { $this->max = $step; } elseif ($step < 0) { $step = 0; } $redrawFreq = $this->redrawFreq ?? (($this->max ?: 10) / 10); $prevPeriod = (int) ($this->step / $redrawFreq); $currPeriod = (int) ($step / $redrawFreq); $this->step = $step; $this->percent = $this->max ? (float) $this->step / $this->max : 0; $timeInterval = microtime(true) - $this->lastWriteTime; // Draw regardless of other limits if ($this->max === $step) { $this->display(); return; } // Throttling if ($timeInterval < $this->minSecondsBetweenRedraws) { return; } // Draw each step period, but not too late if ($prevPeriod !== $currPeriod || $timeInterval >= $this->maxSecondsBetweenRedraws) { $this->display(); } } public function setMaxSteps(int $max): void { $this->format = null; $this->max = max(0, $max); $this->stepWidth = $this->max ? Helper::width((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): void { // 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; } } /** * Overwrites a previous message to the output. */ private function overwrite(string $message): void { if ($this->previousMessage === $message) { return; } $originalMessage = $message; if ($this->overwrite) { if (null !== $this->previousMessage) { if ($this->output instanceof ConsoleSectionOutput) { $messageLines = explode("\n", $this->previousMessage); $lineCount = \count($messageLines); foreach ($messageLines as $messageLine) { $messageLineLength = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $messageLine)); if ($messageLineLength > $this->terminal->getWidth()) { $lineCount += floor($messageLineLength / $this->terminal->getWidth()); } } $this->output->clear($lineCount); } else { $lineCount = substr_count($this->previousMessage, "\n"); for ($i = 0; $i < $lineCount; ++$i) { $this->cursor->moveToColumn(1); $this->cursor->clearLine(); $this->cursor->moveUp(); } $this->cursor->moveToColumn(1); $this->cursor->clearLine(); } } } elseif ($this->step > 0) { $message = \PHP_EOL.$message; } $this->previousMessage = $originalMessage; $this->lastWriteTime = microtime(true); $this->output->write($message); ++$this->writeCount; } private function determineBestFormat(): string { return match ($this->output->getVerbosity()) { // OutputInterface::VERBOSITY_QUIET: display is disabled anyway OutputInterface::VERBOSITY_VERBOSE => $this->max ? self::FORMAT_VERBOSE : self::FORMAT_VERBOSE_NOMAX, OutputInterface::VERBOSITY_VERY_VERBOSE => $this->max ? self::FORMAT_VERY_VERBOSE : self::FORMAT_VERY_VERBOSE_NOMAX, OutputInterface::VERBOSITY_DEBUG => $this->max ? self::FORMAT_DEBUG : self::FORMAT_DEBUG_NOMAX, default => $this->max ? self::FORMAT_NORMAL : self::FORMAT_NORMAL_NOMAX, }; } private static function initPlaceholderFormatters(): array { return [ 'bar' => function (self $bar, OutputInterface $output) { $completeBars = $bar->getBarOffset(); $display = str_repeat($bar->getBarCharacter(), $completeBars); if ($completeBars < $bar->getBarWidth()) { $emptyBars = $bar->getBarWidth() - $completeBars - Helper::length(Helper::removeDecoration($output->getFormatter(), $bar->getProgressCharacter())); $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); } return $display; }, 'elapsed' => fn (self $bar) => 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.'); } return Helper::formatTime($bar->getRemaining()); }, '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.'); } return Helper::formatTime($bar->getEstimated()); }, 'memory' => fn (self $bar) => Helper::formatMemory(memory_get_usage(true)), 'current' => fn (self $bar) => str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT), 'max' => fn (self $bar) => $bar->getMaxSteps(), 'percent' => fn (self $bar) => floor($bar->getProgressPercent() * 100), ]; } private static function initFormats(): array { return [ self::FORMAT_NORMAL => ' %current%/%max% [%bar%] %percent:3s%%', self::FORMAT_NORMAL_NOMAX => ' %current% [%bar%]', self::FORMAT_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', self::FORMAT_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%', self::FORMAT_VERY_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', self::FORMAT_VERY_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%', self::FORMAT_DEBUG => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', self::FORMAT_DEBUG_NOMAX => ' %current% [%bar%] %elapsed:6s% %memory:6s%', ]; } private function buildLine(): string { \assert(null !== $this->format); $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i"; $callback = function ($matches) { if ($formatter = $this->getPlaceholderFormatter($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(fn ($subLine) => Helper::width(Helper::removeDecoration($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); } } QuestionHelper.php 0000644 00000046576 15111773711 0010252 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\Cursor; use Symfony\Component\Console\Exception\MissingInputException; 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; use Symfony\Component\Console\Terminal; use function Symfony\Component\String\s; /** * The QuestionHelper class provides helpers to interact with the user. * * @author Fabien Potencier <fabien@symfony.com> */ class QuestionHelper extends Helper { /** * @var resource|null */ private $inputStream; private static bool $stty = true; private static bool $stdinIsInteractive; /** * 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): mixed { if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } if (!$input->isInteractive()) { return $this->getDefaultAnswer($question); } if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) { $this->inputStream = $stream; } try { if (!$question->getValidator()) { return $this->doAsk($output, $question); } $interviewer = fn () => $this->doAsk($output, $question); return $this->validateAttempts($interviewer, $output, $question); } catch (MissingInputException $exception) { $input->setInteractive(false); if (null === $fallbackOutput = $this->getDefaultAnswer($question)) { throw $exception; } return $fallbackOutput; } } public function getName(): string { return 'question'; } /** * Prevents usage of stty. * * @return void */ public static function disableStty() { self::$stty = false; } /** * Asks the question to the user. * * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden */ private function doAsk(OutputInterface $output, Question $question): mixed { $this->writePrompt($output, $question); $inputStream = $this->inputStream ?: \STDIN; $autocomplete = $question->getAutocompleterCallback(); if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) { $ret = false; if ($question->isHidden()) { try { $hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable()); $ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse; } catch (RuntimeException $e) { if (!$question->isHiddenFallback()) { throw $e; } } } if (false === $ret) { $isBlocked = stream_get_meta_data($inputStream)['blocked'] ?? true; if (!$isBlocked) { stream_set_blocking($inputStream, true); } $ret = $this->readInput($inputStream, $question); if (!$isBlocked) { stream_set_blocking($inputStream, false); } if (false === $ret) { throw new MissingInputException('Aborted.'); } if ($question->isTrimmable()) { $ret = trim($ret); } } } else { $autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete); $ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete; } if ($output instanceof ConsoleSectionOutput) { $output->addContent(''); // add EOL to the question $output->addContent($ret); } $ret = \strlen($ret) > 0 ? $ret : $question->getDefault(); if ($normalizer = $question->getNormalizer()) { return $normalizer($ret); } return $ret; } private function getDefaultAnswer(Question $question): mixed { $default = $question->getDefault(); if (null === $default) { return $default; } if ($validator = $question->getValidator()) { return \call_user_func($validator, $default); } elseif ($question instanceof ChoiceQuestion) { $choices = $question->getChoices(); if (!$question->isMultiselect()) { return $choices[$default] ?? $default; } $default = explode(',', $default); foreach ($default as $k => $v) { $v = $question->isTrimmable() ? trim($v) : $v; $default[$k] = $choices[$v] ?? $v; } } return $default; } /** * Outputs the question prompt. * * @return void */ protected function writePrompt(OutputInterface $output, Question $question) { $message = $question->getQuestion(); if ($question instanceof ChoiceQuestion) { $output->writeln(array_merge([ $question->getQuestion(), ], $this->formatChoiceQuestionChoices($question, 'info'))); $message = $question->getPrompt(); } $output->write($message); } /** * @return string[] */ protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag): array { $messages = []; $maxWidth = max(array_map([__CLASS__, 'width'], array_keys($choices = $question->getChoices()))); foreach ($choices as $key => $value) { $padding = str_repeat(' ', $maxWidth - self::width($key)); $messages[] = sprintf(" [<$tag>%s$padding</$tag>] %s", $key, $value); } return $messages; } /** * Outputs an error message. * * @return void */ 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 { $cursor = new Cursor($output, $inputStream); $fullChoice = ''; $ret = ''; $i = 0; $ofs = -1; $matches = $autocomplete($ret); $numMatches = \count($matches); $sttyMode = shell_exec('stty -g'); $isStdin = 'php://stdin' === (stream_get_meta_data($inputStream)['uri'] ?? null); $r = [$inputStream]; $w = []; // 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)) { while ($isStdin && 0 === @stream_select($r, $w, $w, 0, 100)) { // Give signal handlers a chance to run $r = [$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('stty '.$sttyMode); throw new MissingInputException('Aborted.'); } elseif ("\177" === $c) { // Backspace Character if (0 === $numMatches && 0 !== $i) { --$i; $cursor->moveLeft(s($fullChoice)->slice(-1)->width(false)); $fullChoice = self::substr($fullChoice, 0, $i); } 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 = self::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 = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding); $matches = array_filter( $autocomplete($ret), fn ($match) => '' === $ret || str_starts_with($match, $ret) ); $numMatches = \count($matches); $ofs = -1; } if ("\n" === $c) { $output->write($c); break; } $numMatches = 0; } 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 (str_starts_with($value, $tempRet)) { $matches[$numMatches++] = $value; } } } $cursor->clearLineAfter(); if ($numMatches > 0 && -1 !== $ofs) { $cursor->savePosition(); // Write highlighted text, complete the partially entered response $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))); $output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>'); $cursor->restorePosition(); } } // Reset stty so it behaves normally again shell_exec('stty '.$sttyMode); return $fullChoice; } private function mostRecentlyEnteredValue(string $entered): string { // Determine the most recent value that the user entered if (!str_contains($entered, ',')) { return $entered; } $choices = explode(',', $entered); if ('' !== $lastChoice = trim($choices[\count($choices) - 1])) { return $lastChoice; } return $entered; } /** * Gets a hidden response from user. * * @param resource $inputStream The handler resource * @param bool $trimmable Is the answer trimmable * * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden */ private function getHiddenResponse(OutputInterface $output, $inputStream, bool $trimmable = true): string { if ('\\' === \DIRECTORY_SEPARATOR) { $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; // handle code running from a phar if (str_starts_with(__FILE__, 'phar:')) { $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; copy($exe, $tmpExe); $exe = $tmpExe; } $sExec = shell_exec('"'.$exe.'"'); $value = $trimmable ? rtrim($sExec) : $sExec; $output->writeln(''); if (isset($tmpExe)) { unlink($tmpExe); } return $value; } if (self::$stty && Terminal::hasSttyAvailable()) { $sttyMode = shell_exec('stty -g'); shell_exec('stty -echo'); } elseif ($this->isInteractiveInput($inputStream)) { throw new RuntimeException('Unable to hide the response.'); } $value = fgets($inputStream, 4096); if (4095 === \strlen($value)) { $errOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; $errOutput->warning('The value was possibly truncated by your shell or terminal emulator'); } if (self::$stty && Terminal::hasSttyAvailable()) { shell_exec('stty '.$sttyMode); } if (false === $value) { throw new MissingInputException('Aborted.'); } if ($trimmable) { $value = trim($value); } $output->writeln(''); return $value; } /** * Validates an attempt. * * @param callable $interviewer A callable that will ask for a question and return the result * * @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): mixed { $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; } private function isInteractiveInput($inputStream): bool { if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) { return false; } if (isset(self::$stdinIsInteractive)) { return self::$stdinIsInteractive; } if (\function_exists('stream_isatty')) { return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r')); } if (\function_exists('posix_isatty')) { return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r')); } if (!\function_exists('shell_exec')) { return self::$stdinIsInteractive = true; } return self::$stdinIsInteractive = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null')); } /** * Reads one or more lines of input and returns what is read. * * @param resource $inputStream The handler resource * @param Question $question The question being asked */ private function readInput($inputStream, Question $question): string|false { if (!$question->isMultiline()) { $cp = $this->setIOCodepage(); $ret = fgets($inputStream, 4096); return $this->resetIOCodepage($cp, $ret); } $multiLineStreamReader = $this->cloneInputStream($inputStream); if (null === $multiLineStreamReader) { return false; } $ret = ''; $cp = $this->setIOCodepage(); while (false !== ($char = fgetc($multiLineStreamReader))) { if (\PHP_EOL === "{$ret}{$char}") { break; } $ret .= $char; } return $this->resetIOCodepage($cp, $ret); } private function setIOCodepage(): int { if (\function_exists('sapi_windows_cp_set')) { $cp = sapi_windows_cp_get(); sapi_windows_cp_set(sapi_windows_cp_get('oem')); return $cp; } return 0; } /** * Sets console I/O to the specified code page and converts the user input. */ private function resetIOCodepage(int $cp, string|false $input): string|false { if (0 !== $cp) { sapi_windows_cp_set($cp); if (false !== $input && '' !== $input) { $input = sapi_windows_cp_conv(sapi_windows_cp_get('oem'), $cp, $input); } } return $input; } /** * Clones an input stream in order to act on one instance of the same * stream without affecting the other instance. * * @param resource $inputStream The handler resource * * @return resource|null The cloned resource, null in case it could not be cloned */ private function cloneInputStream($inputStream) { $streamMetaData = stream_get_meta_data($inputStream); $seekable = $streamMetaData['seekable'] ?? false; $mode = $streamMetaData['mode'] ?? 'rb'; $uri = $streamMetaData['uri'] ?? null; if (null === $uri) { return null; } $cloneStream = fopen($uri, $mode); // For seekable and writable streams, add all the same data to the // cloned stream and then seek to the same offset. if (true === $seekable && !\in_array($mode, ['r', 'rb', 'rt'])) { $offset = ftell($inputStream); rewind($inputStream); stream_copy_to_stream($inputStream, $cloneStream); fseek($inputStream, $offset); fseek($cloneStream, $offset); } return $cloneStream; } } OutputWrapper.php 0000644 00000005700 15111773712 0010125 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; /** * Simple output wrapper for "tagged outputs" instead of wordwrap(). This solution is based on a StackOverflow * answer: https://stackoverflow.com/a/20434776/1476819 from user557597 (alias SLN). * * (?: * # -- Words/Characters * ( # (1 start) * (?> # Atomic Group - Match words with valid breaks * .{1,16} # 1-N characters * # Followed by one of 4 prioritized, non-linebreak whitespace * (?: # break types: * (?<= [^\S\r\n] ) # 1. - Behind a non-linebreak whitespace * [^\S\r\n]? # ( optionally accept an extra non-linebreak whitespace ) * | (?= \r? \n ) # 2. - Ahead a linebreak * | $ # 3. - EOS * | [^\S\r\n] # 4. - Accept an extra non-linebreak whitespace * ) * ) # End atomic group * | * .{1,16} # No valid word breaks, just break on the N'th character * ) # (1 end) * (?: \r? \n )? # Optional linebreak after Words/Characters * | * # -- Or, Linebreak * (?: \r? \n | $ ) # Stand alone linebreak or at EOS * ) * * @author Krisztián Ferenczi <ferenczi.krisztian@gmail.com> * * @see https://stackoverflow.com/a/20434776/1476819 */ final class OutputWrapper { private const TAG_OPEN_REGEX_SEGMENT = '[a-z](?:[^\\\\<>]*+ | \\\\.)*'; private const TAG_CLOSE_REGEX_SEGMENT = '[a-z][^<>]*+'; private const URL_PATTERN = 'https?://\S+'; public function __construct( private bool $allowCutUrls = false ) { } public function wrap(string $text, int $width, string $break = "\n"): string { if (!$width) { return $text; } $tagPattern = sprintf('<(?:(?:%s)|/(?:%s)?)>', self::TAG_OPEN_REGEX_SEGMENT, self::TAG_CLOSE_REGEX_SEGMENT); $limitPattern = "{1,$width}"; $patternBlocks = [$tagPattern]; if (!$this->allowCutUrls) { $patternBlocks[] = self::URL_PATTERN; } $patternBlocks[] = '.'; $blocks = implode('|', $patternBlocks); $rowPattern = "(?:$blocks)$limitPattern"; $pattern = sprintf('#(?:((?>(%1$s)((?<=[^\S\r\n])[^\S\r\n]?|(?=\r?\n)|$|[^\S\r\n]))|(%1$s))(?:\r?\n)?|(?:\r?\n|$))#imux', $rowPattern); $output = rtrim(preg_replace($pattern, '\\1'.$break, $text), $break); return str_replace(' '.$break, $break, $output); } } DebugFormatterHelper.php 0000644 00000006370 15111773712 0011342 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 const COLORS = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default']; private array $started = []; private int $count = -1; /** * Starts a debug formatting session. */ public function start(string $id, string $message, string $prefix = 'RUN'): string { $this->started[$id] = ['border' => ++$this->count % \count(self::COLORS)]; return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message); } /** * Adds progress to a formatting session. */ public function progress(string $id, string $buffer, bool $error = false, string $prefix = 'OUT', string $errorPrefix = 'ERR'): string { $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. */ public function stop(string $id, string $message, bool $successful, string $prefix = 'RES'): string { $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; } private function getBorder(string $id): string { return sprintf('<bg=%s> </>', self::COLORS[$this->started[$id]['border']]); } public function getName(): string { return 'debug_formatter'; } } error_log 0000644 00000031526 15111773712 0006475 0 ustar 00 [19-Nov-2025 21:35:55 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\QuestionHelper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php on line 26 [19-Nov-2025 22:26:34 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php:28 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php on line 28 [19-Nov-2025 22:28:36 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php on line 21 [19-Nov-2025 22:41:19 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\QuestionHelper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php on line 26 [19-Nov-2025 22:45:57 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php:28 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php on line 28 [19-Nov-2025 22:48:04 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php on line 21 [19-Nov-2025 23:02:59 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php on line 21 [19-Nov-2025 23:06:11 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\QuestionHelper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php on line 26 [19-Nov-2025 23:13:43 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php:28 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php on line 28 [19-Nov-2025 23:21:56 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php on line 21 [19-Nov-2025 23:25:07 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\QuestionHelper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php on line 26 [20-Nov-2025 03:50:09 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php:28 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php on line 28 [20-Nov-2025 04:24:19 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php:28 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php on line 28 [20-Nov-2025 04:43:26 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php:28 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php on line 28 [20-Nov-2025 04:51:56 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php:28 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php on line 28 [20-Nov-2025 06:12:43 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/FormatterHelper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/FormatterHelper.php on line 21 [20-Nov-2025 09:17:33 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/QuestionHelper.php:35 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/QuestionHelper.php on line 35 [20-Nov-2025 10:53:50 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/FormatterHelper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/FormatterHelper.php on line 21 [20-Nov-2025 10:58:47 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/InputAwareHelper.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/InputAwareHelper.php on line 22 [25-Nov-2025 02:29:26 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/FormatterHelper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/FormatterHelper.php on line 21 [25-Nov-2025 02:33:25 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php:28 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php on line 28 [25-Nov-2025 02:33:26 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php:28 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php on line 28 [25-Nov-2025 02:56:48 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/ProcessHelper.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/ProcessHelper.php on line 26 [25-Nov-2025 03:01:06 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\TableCell" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/TableSeparator.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/TableSeparator.php on line 19 [25-Nov-2025 03:02:20 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php on line 21 [25-Nov-2025 03:05:35 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Console\Helper\HelperInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/Helper.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/Helper.php on line 22 [25-Nov-2025 04:29:00 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/QuestionHelper.php:35 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/QuestionHelper.php on line 35 [25-Nov-2025 05:31:25 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\QuestionHelper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php on line 26 [25-Nov-2025 05:32:37 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/InputAwareHelper.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/InputAwareHelper.php on line 22 [26-Nov-2025 01:36:03 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php:28 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DescriptorHelper.php on line 28 [26-Nov-2025 01:38:32 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/InputAwareHelper.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/InputAwareHelper.php on line 22 [26-Nov-2025 03:31:36 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/DebugFormatterHelper.php on line 21 [26-Nov-2025 03:32:56 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/ProcessHelper.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/ProcessHelper.php on line 26 [26-Nov-2025 03:33:06 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/FormatterHelper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/FormatterHelper.php on line 21 [26-Nov-2025 03:33:46 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\TableCell" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/TableSeparator.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/TableSeparator.php on line 19 [26-Nov-2025 03:34:33 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\QuestionHelper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/SymfonyQuestionHelper.php on line 26 [26-Nov-2025 03:35:09 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Helper\Helper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/QuestionHelper.php:35 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/QuestionHelper.php on line 35 [26-Nov-2025 03:43:40 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Console\Helper\HelperInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/Helper.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/console/Helper/Helper.php on line 22 HelperSet.php 0000644 00000003511 15111773712 0007155 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; /** * HelperSet represents a set of helpers to be used with a command. * * @author Fabien Potencier <fabien@symfony.com> * * @implements \IteratorAggregate<string, HelperInterface> */ class HelperSet implements \IteratorAggregate { /** @var array<string, HelperInterface> */ private array $helpers = []; /** * @param HelperInterface[] $helpers */ public function __construct(array $helpers = []) { foreach ($helpers as $alias => $helper) { $this->set($helper, \is_int($alias) ? null : $alias); } } /** * @return void */ public function set(HelperInterface $helper, string $alias = null) { $this->helpers[$helper->getName()] = $helper; if (null !== $alias) { $this->helpers[$alias] = $helper; } $helper->setHelperSet($this); } /** * Returns true if the helper if defined. */ public function has(string $name): bool { return isset($this->helpers[$name]); } /** * Gets a helper value. * * @throws InvalidArgumentException if the helper is not defined */ public function get(string $name): HelperInterface { if (!$this->has($name)) { throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); } return $this->helpers[$name]; } public function getIterator(): \Traversable { return new \ArrayIterator($this->helpers); } } FormatterHelper.php 0000644 00000004273 15111773712 0010373 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. */ public function formatSection(string $section, string $message, string $style = 'info'): string { return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message); } /** * Formats a message as a block of text. */ public function formatBlock(string|array $messages, string $style, bool $large = false): string { 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(self::width($message) + ($large ? 4 : 2), $len); } $messages = $large ? [str_repeat(' ', $len)] : []; for ($i = 0; isset($lines[$i]); ++$i) { $messages[] = $lines[$i].str_repeat(' ', $len - self::width($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. */ public function truncate(string $message, int $length, string $suffix = '...'): string { $computedLength = $length - self::width($suffix); if ($computedLength > self::width($message)) { return $message; } return self::substr($message, 0, $length).$suffix; } public function getName(): string { return 'formatter'; } } Dumper.php 0000644 00000003444 15111773712 0006523 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 OutputInterface $output; private ?CliDumper $dumper; private ?ClonerInterface $cloner; private \Closure $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 ??= new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR); $dumper->setColors($this->output->isDecorated()); return rtrim($dumper->dump(($this->cloner ??= new VarCloner())->cloneVar($var)->withRefHandles(false), true)); }; } else { $this->handler = fn ($var): string => match (true) { null === $var => 'null', true === $var => 'true', false === $var => 'false', \is_string($var) => '"'.$var.'"', default => rtrim(print_r($var, true)), }; } } public function __invoke(mixed $var): string { return ($this->handler)($var); } } Helper.php 0000644 00000010722 15111773712 0006503 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; use Symfony\Component\String\UnicodeString; /** * Helper is the base class for all helper classes. * * @author Fabien Potencier <fabien@symfony.com> */ abstract class Helper implements HelperInterface { protected $helperSet; /** * @return void */ public function setHelperSet(HelperSet $helperSet = null) { if (1 > \func_num_args()) { trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); } $this->helperSet = $helperSet; } public function getHelperSet(): ?HelperSet { return $this->helperSet; } /** * Returns the width of a string, using mb_strwidth if it is available. * The width is how many characters positions the string will use. */ public static function width(?string $string): int { $string ??= ''; if (preg_match('//u', $string)) { return (new UnicodeString($string))->width(false); } if (false === $encoding = mb_detect_encoding($string, null, true)) { return \strlen($string); } return mb_strwidth($string, $encoding); } /** * Returns the length of a string, using mb_strlen if it is available. * The length is related to how many bytes the string will use. */ public static function length(?string $string): int { $string ??= ''; if (preg_match('//u', $string)) { return (new UnicodeString($string))->length(); } if (false === $encoding = mb_detect_encoding($string, null, true)) { return \strlen($string); } return mb_strlen($string, $encoding); } /** * Returns the subset of a string, using mb_substr if it is available. */ public static function substr(?string $string, int $from, int $length = null): string { $string ??= ''; if (false === $encoding = mb_detect_encoding($string, null, true)) { return substr($string, $from, $length); } return mb_substr($string, $from, $length, $encoding); } /** * @return string */ public static function formatTime(int|float $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]; } } } } /** * @return string */ public static function formatMemory(int $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); } /** * @return string */ public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string) { $isDecorated = $formatter->isDecorated(); $formatter->setDecorated(false); // remove <...> formatting $string = $formatter->format($string ?? ''); // remove already formatted characters $string = preg_replace("/\033\[[^m]*m/", '', $string ?? ''); // remove terminal hyperlinks $string = preg_replace('/\\033]8;[^;]*;[^\\033]*\\033\\\\/', '', $string ?? ''); $formatter->setDecorated($isDecorated); return $string; } } HelperInterface.php 0000644 00000001511 15111773712 0010320 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. * * @return void */ public function setHelperSet(?HelperSet $helperSet); /** * Gets the helper set associated with this helper. */ public function getHelperSet(): ?HelperSet; /** * Returns the canonical name of this helper. * * @return string */ public function getName(); }
Simpan