updated-packages
This commit is contained in:
@@ -20,8 +20,8 @@ namespace Symfony\Component\Console\Helper;
|
||||
*/
|
||||
class DebugFormatterHelper extends Helper
|
||||
{
|
||||
private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default');
|
||||
private $started = array();
|
||||
private $colors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'];
|
||||
private $started = [];
|
||||
private $count = -1;
|
||||
|
||||
/**
|
||||
@@ -35,7 +35,7 @@ class DebugFormatterHelper extends Helper
|
||||
*/
|
||||
public function start($id, $message, $prefix = 'RUN')
|
||||
{
|
||||
$this->started[$id] = array('border' => ++$this->count % \count($this->colors));
|
||||
$this->started[$id] = ['border' => ++$this->count % \count($this->colors)];
|
||||
|
||||
return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message);
|
||||
}
|
||||
@@ -107,12 +107,7 @@ class DebugFormatterHelper extends Helper
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id The id of the formatting session
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getBorder($id)
|
||||
private function getBorder(string $id): string
|
||||
{
|
||||
return sprintf('<bg=%s> </>', $this->colors[$this->started[$id]['border']]);
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ class DescriptorHelper extends Helper
|
||||
/**
|
||||
* @var DescriptorInterface[]
|
||||
*/
|
||||
private $descriptors = array();
|
||||
private $descriptors = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -48,18 +48,16 @@ class DescriptorHelper extends Helper
|
||||
* * format: string, the output format name
|
||||
* * raw_text: boolean, sets output type as raw
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param object $object
|
||||
* @param array $options
|
||||
* @param object $object
|
||||
*
|
||||
* @throws InvalidArgumentException when the given format is not supported
|
||||
*/
|
||||
public function describe(OutputInterface $output, $object, array $options = array())
|
||||
public function describe(OutputInterface $output, $object, array $options = [])
|
||||
{
|
||||
$options = array_merge(array(
|
||||
$options = array_merge([
|
||||
'raw_text' => false,
|
||||
'format' => 'txt',
|
||||
), $options);
|
||||
], $options);
|
||||
|
||||
if (!isset($this->descriptors[$options['format']])) {
|
||||
throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
|
||||
@@ -72,8 +70,7 @@ class DescriptorHelper extends Helper
|
||||
/**
|
||||
* Registers a descriptor.
|
||||
*
|
||||
* @param string $format
|
||||
* @param DescriptorInterface $descriptor
|
||||
* @param string $format
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
|
64
vendor/symfony/console/Helper/Dumper.php
vendored
Normal file
64
vendor/symfony/console/Helper/Dumper.php
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Console\Helper;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Symfony\Component\VarDumper\Dumper\CliDumper;
|
||||
|
||||
/**
|
||||
* @author Roland Franssen <franssen.roland@gmail.com>
|
||||
*/
|
||||
final class Dumper
|
||||
{
|
||||
private $output;
|
||||
private $dumper;
|
||||
private $cloner;
|
||||
private $handler;
|
||||
|
||||
public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null)
|
||||
{
|
||||
$this->output = $output;
|
||||
$this->dumper = $dumper;
|
||||
$this->cloner = $cloner;
|
||||
|
||||
if (class_exists(CliDumper::class)) {
|
||||
$this->handler = function ($var): string {
|
||||
$dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
|
||||
$dumper->setColors($this->output->isDecorated());
|
||||
|
||||
return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true));
|
||||
};
|
||||
} else {
|
||||
$this->handler = function ($var): string {
|
||||
switch (true) {
|
||||
case null === $var:
|
||||
return 'null';
|
||||
case true === $var:
|
||||
return 'true';
|
||||
case false === $var:
|
||||
return 'false';
|
||||
case \is_string($var):
|
||||
return '"'.$var.'"';
|
||||
default:
|
||||
return rtrim(print_r($var, true));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public function __invoke($var): string
|
||||
{
|
||||
return ($this->handler)($var);
|
||||
}
|
||||
}
|
@@ -46,20 +46,20 @@ class FormatterHelper extends Helper
|
||||
public function formatBlock($messages, $style, $large = false)
|
||||
{
|
||||
if (!\is_array($messages)) {
|
||||
$messages = array($messages);
|
||||
$messages = [$messages];
|
||||
}
|
||||
|
||||
$len = 0;
|
||||
$lines = array();
|
||||
$lines = [];
|
||||
foreach ($messages as $message) {
|
||||
$message = OutputFormatter::escape($message);
|
||||
$lines[] = sprintf($large ? ' %s ' : ' %s ', $message);
|
||||
$len = max($this->strlen($message) + ($large ? 4 : 2), $len);
|
||||
$len = max(self::strlen($message) + ($large ? 4 : 2), $len);
|
||||
}
|
||||
|
||||
$messages = $large ? array(str_repeat(' ', $len)) : array();
|
||||
$messages = $large ? [str_repeat(' ', $len)] : [];
|
||||
for ($i = 0; isset($lines[$i]); ++$i) {
|
||||
$messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i]));
|
||||
$messages[] = $lines[$i].str_repeat(' ', $len - self::strlen($lines[$i]));
|
||||
}
|
||||
if ($large) {
|
||||
$messages[] = str_repeat(' ', $len);
|
||||
@@ -83,17 +83,13 @@ class FormatterHelper extends Helper
|
||||
*/
|
||||
public function truncate($message, $length, $suffix = '...')
|
||||
{
|
||||
$computedLength = $length - $this->strlen($suffix);
|
||||
$computedLength = $length - self::strlen($suffix);
|
||||
|
||||
if ($computedLength > $this->strlen($message)) {
|
||||
if ($computedLength > self::strlen($message)) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
if (false === $encoding = mb_detect_encoding($message, null, true)) {
|
||||
return substr($message, 0, $length).$suffix;
|
||||
}
|
||||
|
||||
return mb_substr($message, 0, $length, $encoding).$suffix;
|
||||
return self::substr($message, 0, $length).$suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
|
28
vendor/symfony/console/Helper/Helper.php
vendored
28
vendor/symfony/console/Helper/Helper.php
vendored
@@ -47,6 +47,8 @@ abstract class Helper implements HelperInterface
|
||||
*/
|
||||
public static function strlen($string)
|
||||
{
|
||||
$string = (string) $string;
|
||||
|
||||
if (false === $encoding = mb_detect_encoding($string, null, true)) {
|
||||
return \strlen($string);
|
||||
}
|
||||
@@ -65,6 +67,8 @@ abstract class Helper implements HelperInterface
|
||||
*/
|
||||
public static function substr($string, $from, $length = null)
|
||||
{
|
||||
$string = (string) $string;
|
||||
|
||||
if (false === $encoding = mb_detect_encoding($string, null, true)) {
|
||||
return substr($string, $from, $length);
|
||||
}
|
||||
@@ -74,17 +78,17 @@ abstract class Helper implements HelperInterface
|
||||
|
||||
public static function formatTime($secs)
|
||||
{
|
||||
static $timeFormats = array(
|
||||
array(0, '< 1 sec'),
|
||||
array(1, '1 sec'),
|
||||
array(2, 'secs', 1),
|
||||
array(60, '1 min'),
|
||||
array(120, 'mins', 60),
|
||||
array(3600, '1 hr'),
|
||||
array(7200, 'hrs', 3600),
|
||||
array(86400, '1 day'),
|
||||
array(172800, 'days', 86400),
|
||||
);
|
||||
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]) {
|
||||
@@ -131,6 +135,8 @@ abstract class Helper implements HelperInterface
|
||||
$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;
|
||||
|
10
vendor/symfony/console/Helper/HelperSet.php
vendored
10
vendor/symfony/console/Helper/HelperSet.php
vendored
@@ -24,13 +24,13 @@ class HelperSet implements \IteratorAggregate
|
||||
/**
|
||||
* @var Helper[]
|
||||
*/
|
||||
private $helpers = array();
|
||||
private $helpers = [];
|
||||
private $command;
|
||||
|
||||
/**
|
||||
* @param Helper[] $helpers An array of helper
|
||||
*/
|
||||
public function __construct(array $helpers = array())
|
||||
public function __construct(array $helpers = [])
|
||||
{
|
||||
foreach ($helpers as $alias => $helper) {
|
||||
$this->set($helper, \is_int($alias) ? null : $alias);
|
||||
@@ -40,8 +40,7 @@ class HelperSet implements \IteratorAggregate
|
||||
/**
|
||||
* Sets a helper.
|
||||
*
|
||||
* @param HelperInterface $helper The helper instance
|
||||
* @param string $alias An alias
|
||||
* @param string $alias An alias
|
||||
*/
|
||||
public function set(HelperInterface $helper, $alias = null)
|
||||
{
|
||||
@@ -99,8 +98,9 @@ class HelperSet implements \IteratorAggregate
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Helper[]
|
||||
* @return \Traversable<Helper>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->helpers);
|
||||
|
42
vendor/symfony/console/Helper/ProcessHelper.php
vendored
42
vendor/symfony/console/Helper/ProcessHelper.php
vendored
@@ -28,17 +28,20 @@ class ProcessHelper extends Helper
|
||||
/**
|
||||
* Runs an external process.
|
||||
*
|
||||
* @param OutputInterface $output An OutputInterface instance
|
||||
* @param array|Process $cmd An instance of Process or an array of the command and arguments
|
||||
* @param string|null $error An error message that must be displayed if something went wrong
|
||||
* @param callable|null $callback A PHP callback to run whenever there is some
|
||||
* output available on STDOUT or STDERR
|
||||
* @param int $verbosity The threshold for verbosity
|
||||
* @param array|Process $cmd An instance of Process or an array of the command and arguments
|
||||
* @param string|null $error An error message that must be displayed if something went wrong
|
||||
* @param callable|null $callback A PHP callback to run whenever there is some
|
||||
* output available on STDOUT or STDERR
|
||||
* @param int $verbosity The threshold for verbosity
|
||||
*
|
||||
* @return Process The process that ran
|
||||
*/
|
||||
public function run(OutputInterface $output, $cmd, $error = null, callable $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE)
|
||||
{
|
||||
if (!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();
|
||||
}
|
||||
@@ -46,22 +49,22 @@ class ProcessHelper extends Helper
|
||||
$formatter = $this->getHelperSet()->get('debug_formatter');
|
||||
|
||||
if ($cmd instanceof Process) {
|
||||
$cmd = array($cmd);
|
||||
$cmd = [$cmd];
|
||||
}
|
||||
|
||||
if (!\is_array($cmd)) {
|
||||
@trigger_error(sprintf('Passing a command as a string to "%s()" is deprecated since Symfony 4.2, pass it the command as an array of arguments instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
$cmd = array(\method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline($cmd) : new Process($cmd));
|
||||
@trigger_error(sprintf('Passing a command as a string to "%s()" is deprecated since Symfony 4.2, pass it the command as an array of arguments instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
$cmd = [method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline($cmd) : new Process($cmd)];
|
||||
}
|
||||
|
||||
if (\is_string($cmd[0] ?? null)) {
|
||||
$process = new Process($cmd);
|
||||
$cmd = array();
|
||||
$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 an array whose first is element is either the path to the binary to run of a "Process" object.', __METHOD__));
|
||||
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()) {
|
||||
@@ -92,11 +95,10 @@ class ProcessHelper extends Helper
|
||||
* This is identical to run() except that an exception is thrown if the process
|
||||
* exits with a non-zero exit code.
|
||||
*
|
||||
* @param OutputInterface $output An OutputInterface instance
|
||||
* @param string|Process $cmd An instance of Process or a command to run
|
||||
* @param string|null $error An error message that must be displayed if something went wrong
|
||||
* @param callable|null $callback A PHP callback to run whenever there is some
|
||||
* output available on STDOUT or STDERR
|
||||
* @param array|Process $cmd An instance of Process or a command to run
|
||||
* @param string|null $error An error message that must be displayed if something went wrong
|
||||
* @param callable|null $callback A PHP callback to run whenever there is some
|
||||
* output available on STDOUT or STDERR
|
||||
*
|
||||
* @return Process The process that ran
|
||||
*
|
||||
@@ -118,10 +120,6 @@ class ProcessHelper extends Helper
|
||||
/**
|
||||
* Wraps a Process callback to add debugging output.
|
||||
*
|
||||
* @param OutputInterface $output An OutputInterface interface
|
||||
* @param Process $process The Process
|
||||
* @param callable|null $callback A PHP callable
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null)
|
||||
@@ -136,12 +134,12 @@ class ProcessHelper extends Helper
|
||||
$output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type));
|
||||
|
||||
if (null !== $callback) {
|
||||
\call_user_func($callback, $type, $buffer);
|
||||
$callback($type, $buffer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private function escapeString($str)
|
||||
private function escapeString(string $str): string
|
||||
{
|
||||
return str_replace('<', '\\<', $str);
|
||||
}
|
||||
|
151
vendor/symfony/console/Helper/ProgressBar.php
vendored
151
vendor/symfony/console/Helper/ProgressBar.php
vendored
@@ -32,6 +32,10 @@ final class ProgressBar
|
||||
private $format;
|
||||
private $internalFormat;
|
||||
private $redrawFreq = 1;
|
||||
private $writeCount;
|
||||
private $lastWriteTime;
|
||||
private $minSecondsBetweenRedraws = 0;
|
||||
private $maxSecondsBetweenRedraws = 1;
|
||||
private $output;
|
||||
private $step = 0;
|
||||
private $max;
|
||||
@@ -39,19 +43,18 @@ final class ProgressBar
|
||||
private $stepWidth;
|
||||
private $percent = 0.0;
|
||||
private $formatLineCount;
|
||||
private $messages = array();
|
||||
private $messages = [];
|
||||
private $overwrite = true;
|
||||
private $terminal;
|
||||
private $firstRun = true;
|
||||
private $previousMessage;
|
||||
|
||||
private static $formatters;
|
||||
private static $formats;
|
||||
|
||||
/**
|
||||
* @param OutputInterface $output An OutputInterface instance
|
||||
* @param int $max Maximum steps (0 if unknown)
|
||||
* @param int $max Maximum steps (0 if unknown)
|
||||
*/
|
||||
public function __construct(OutputInterface $output, int $max = 0)
|
||||
public function __construct(OutputInterface $output, int $max = 0, float $minSecondsBetweenRedraws = 0.1)
|
||||
{
|
||||
if ($output instanceof ConsoleOutputInterface) {
|
||||
$output = $output->getErrorOutput();
|
||||
@@ -61,12 +64,17 @@ final class ProgressBar
|
||||
$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->setRedrawFrequency($max / 10);
|
||||
$this->redrawFreq = null;
|
||||
}
|
||||
|
||||
$this->startTime = time();
|
||||
@@ -102,7 +110,7 @@ final class ProgressBar
|
||||
self::$formatters = self::initPlaceholderFormatters();
|
||||
}
|
||||
|
||||
return isset(self::$formatters[$name]) ? self::$formatters[$name] : null;
|
||||
return self::$formatters[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +143,7 @@ final class ProgressBar
|
||||
self::$formats = self::initFormats();
|
||||
}
|
||||
|
||||
return isset(self::$formats[$name]) ? self::$formats[$name] : null;
|
||||
return self::$formats[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,6 +191,11 @@ final class ProgressBar
|
||||
return $this->percent;
|
||||
}
|
||||
|
||||
public function getBarOffset(): int
|
||||
{
|
||||
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 setBarWidth(int $size)
|
||||
{
|
||||
$this->barWidth = max(1, $size);
|
||||
@@ -236,11 +249,39 @@ final class ProgressBar
|
||||
/**
|
||||
* Sets the redraw frequency.
|
||||
*
|
||||
* @param int|float $freq The frequency in steps
|
||||
* @param int|null $freq The frequency in steps
|
||||
*/
|
||||
public function setRedrawFrequency(int $freq)
|
||||
public function setRedrawFrequency(?int $freq)
|
||||
{
|
||||
$this->redrawFreq = max($freq, 1);
|
||||
$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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,11 +328,27 @@ final class ProgressBar
|
||||
$step = 0;
|
||||
}
|
||||
|
||||
$prevPeriod = (int) ($this->step / $this->redrawFreq);
|
||||
$currPeriod = (int) ($step / $this->redrawFreq);
|
||||
$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;
|
||||
if ($prevPeriod !== $currPeriod || $this->max === $step) {
|
||||
$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();
|
||||
}
|
||||
}
|
||||
@@ -375,31 +432,43 @@ final class ProgressBar
|
||||
*/
|
||||
private function overwrite(string $message): void
|
||||
{
|
||||
if ($this->previousMessage === $message) {
|
||||
return;
|
||||
}
|
||||
|
||||
$originalMessage = $message;
|
||||
|
||||
if ($this->overwrite) {
|
||||
if (!$this->firstRun) {
|
||||
if (null !== $this->previousMessage) {
|
||||
if ($this->output instanceof ConsoleSectionOutput) {
|
||||
$lines = floor(Helper::strlen($message) / $this->terminal->getWidth()) + $this->formatLineCount + 1;
|
||||
$this->output->clear($lines);
|
||||
$messageLines = explode("\n", $message);
|
||||
$lineCount = \count($messageLines);
|
||||
foreach ($messageLines as $messageLine) {
|
||||
$messageLineLength = Helper::strlenWithoutDecoration($this->output->getFormatter(), $messageLine);
|
||||
if ($messageLineLength > $this->terminal->getWidth()) {
|
||||
$lineCount += floor($messageLineLength / $this->terminal->getWidth());
|
||||
}
|
||||
}
|
||||
$this->output->clear($lineCount);
|
||||
} else {
|
||||
// Move the cursor to the beginning of the line
|
||||
$this->output->write("\x0D");
|
||||
|
||||
// Erase the line
|
||||
$this->output->write("\x1B[2K");
|
||||
|
||||
// Erase previous lines
|
||||
if ($this->formatLineCount > 0) {
|
||||
$this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount));
|
||||
$message = str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount).$message;
|
||||
}
|
||||
|
||||
// Move the cursor to the beginning of the line and erase the line
|
||||
$message = "\x0D\x1B[2K$message";
|
||||
}
|
||||
}
|
||||
} elseif ($this->step > 0) {
|
||||
$this->output->writeln('');
|
||||
$message = \PHP_EOL.$message;
|
||||
}
|
||||
|
||||
$this->firstRun = false;
|
||||
$this->previousMessage = $originalMessage;
|
||||
$this->lastWriteTime = microtime(true);
|
||||
|
||||
$this->output->write($message);
|
||||
++$this->writeCount;
|
||||
}
|
||||
|
||||
private function determineBestFormat(): string
|
||||
@@ -419,9 +488,9 @@ final class ProgressBar
|
||||
|
||||
private static function initPlaceholderFormatters(): array
|
||||
{
|
||||
return array(
|
||||
'bar' => function (ProgressBar $bar, OutputInterface $output) {
|
||||
$completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth());
|
||||
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::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter());
|
||||
@@ -430,10 +499,10 @@ final class ProgressBar
|
||||
|
||||
return $display;
|
||||
},
|
||||
'elapsed' => function (ProgressBar $bar) {
|
||||
'elapsed' => function (self $bar) {
|
||||
return Helper::formatTime(time() - $bar->getStartTime());
|
||||
},
|
||||
'remaining' => function (ProgressBar $bar) {
|
||||
'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.');
|
||||
}
|
||||
@@ -446,7 +515,7 @@ final class ProgressBar
|
||||
|
||||
return Helper::formatTime($remaining);
|
||||
},
|
||||
'estimated' => function (ProgressBar $bar) {
|
||||
'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.');
|
||||
}
|
||||
@@ -459,24 +528,24 @@ final class ProgressBar
|
||||
|
||||
return Helper::formatTime($estimated);
|
||||
},
|
||||
'memory' => function (ProgressBar $bar) {
|
||||
'memory' => function (self $bar) {
|
||||
return Helper::formatMemory(memory_get_usage(true));
|
||||
},
|
||||
'current' => function (ProgressBar $bar) {
|
||||
return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT);
|
||||
'current' => function (self $bar) {
|
||||
return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT);
|
||||
},
|
||||
'max' => function (ProgressBar $bar) {
|
||||
'max' => function (self $bar) {
|
||||
return $bar->getMaxSteps();
|
||||
},
|
||||
'percent' => function (ProgressBar $bar) {
|
||||
'percent' => function (self $bar) {
|
||||
return floor($bar->getProgressPercent() * 100);
|
||||
},
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
private static function initFormats(): array
|
||||
{
|
||||
return array(
|
||||
return [
|
||||
'normal' => ' %current%/%max% [%bar%] %percent:3s%%',
|
||||
'normal_nomax' => ' %current% [%bar%]',
|
||||
|
||||
@@ -488,7 +557,7 @@ final class ProgressBar
|
||||
|
||||
'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%',
|
||||
'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
private function buildLine(): string
|
||||
@@ -496,7 +565,7 @@ final class ProgressBar
|
||||
$regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
|
||||
$callback = function ($matches) {
|
||||
if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) {
|
||||
$text = \call_user_func($formatter, $this, $this->output);
|
||||
$text = $formatter($this, $this->output);
|
||||
} elseif (isset($this->messages[$matches[1]])) {
|
||||
$text = $this->messages[$matches[1]];
|
||||
} else {
|
||||
|
@@ -34,10 +34,9 @@ class ProgressIndicator
|
||||
private static $formats;
|
||||
|
||||
/**
|
||||
* @param OutputInterface $output
|
||||
* @param string|null $format Indicator format
|
||||
* @param int $indicatorChangeInterval Change interval in milliseconds
|
||||
* @param array|null $indicatorValues Animated indicator characters
|
||||
* @param string|null $format Indicator format
|
||||
* @param int $indicatorChangeInterval Change interval in milliseconds
|
||||
* @param array|null $indicatorValues Animated indicator characters
|
||||
*/
|
||||
public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null)
|
||||
{
|
||||
@@ -48,7 +47,7 @@ class ProgressIndicator
|
||||
}
|
||||
|
||||
if (null === $indicatorValues) {
|
||||
$indicatorValues = array('-', '\\', '|', '/');
|
||||
$indicatorValues = ['-', '\\', '|', '/'];
|
||||
}
|
||||
|
||||
$indicatorValues = array_values($indicatorValues);
|
||||
@@ -150,7 +149,7 @@ class ProgressIndicator
|
||||
self::$formats = self::initFormats();
|
||||
}
|
||||
|
||||
return isset(self::$formats[$name]) ? self::$formats[$name] : null;
|
||||
return self::$formats[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,7 +182,7 @@ class ProgressIndicator
|
||||
self::$formatters = self::initPlaceholderFormatters();
|
||||
}
|
||||
|
||||
return isset(self::$formatters[$name]) ? self::$formatters[$name] : null;
|
||||
return self::$formatters[$name] ?? null;
|
||||
}
|
||||
|
||||
private function display()
|
||||
@@ -192,18 +191,16 @@ class ProgressIndicator
|
||||
return;
|
||||
}
|
||||
|
||||
$self = $this;
|
||||
|
||||
$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) {
|
||||
if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) {
|
||||
return \call_user_func($formatter, $self);
|
||||
$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) {
|
||||
if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) {
|
||||
return $formatter($this);
|
||||
}
|
||||
|
||||
return $matches[0];
|
||||
}, $this->format));
|
||||
}, $this->format ?? ''));
|
||||
}
|
||||
|
||||
private function determineBestFormat()
|
||||
private function determineBestFormat(): string
|
||||
{
|
||||
switch ($this->output->getVerbosity()) {
|
||||
// OutputInterface::VERBOSITY_QUIET: display is disabled anyway
|
||||
@@ -230,32 +227,32 @@ class ProgressIndicator
|
||||
}
|
||||
}
|
||||
|
||||
private function getCurrentTimeInMilliseconds()
|
||||
private function getCurrentTimeInMilliseconds(): float
|
||||
{
|
||||
return round(microtime(true) * 1000);
|
||||
}
|
||||
|
||||
private static function initPlaceholderFormatters()
|
||||
private static function initPlaceholderFormatters(): array
|
||||
{
|
||||
return array(
|
||||
'indicator' => function (ProgressIndicator $indicator) {
|
||||
return [
|
||||
'indicator' => function (self $indicator) {
|
||||
return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)];
|
||||
},
|
||||
'message' => function (ProgressIndicator $indicator) {
|
||||
'message' => function (self $indicator) {
|
||||
return $indicator->message;
|
||||
},
|
||||
'elapsed' => function (ProgressIndicator $indicator) {
|
||||
'elapsed' => function (self $indicator) {
|
||||
return Helper::formatTime(time() - $indicator->startTime);
|
||||
},
|
||||
'memory' => function () {
|
||||
return Helper::formatMemory(memory_get_usage(true));
|
||||
},
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
private static function initFormats()
|
||||
private static function initFormats(): array
|
||||
{
|
||||
return array(
|
||||
return [
|
||||
'normal' => ' %indicator% %message%',
|
||||
'normal_no_ansi' => ' %message%',
|
||||
|
||||
@@ -264,6 +261,6 @@ class ProgressIndicator
|
||||
|
||||
'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)',
|
||||
'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)',
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
346
vendor/symfony/console/Helper/QuestionHelper.php
vendored
346
vendor/symfony/console/Helper/QuestionHelper.php
vendored
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Console\Helper;
|
||||
|
||||
use Symfony\Component\Console\Exception\MissingInputException;
|
||||
use Symfony\Component\Console\Exception\RuntimeException;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
@@ -21,6 +22,7 @@ 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;
|
||||
|
||||
/**
|
||||
* The QuestionHelper class provides helpers to interact with the user.
|
||||
@@ -31,7 +33,8 @@ class QuestionHelper extends Helper
|
||||
{
|
||||
private $inputStream;
|
||||
private static $shell;
|
||||
private static $stty;
|
||||
private static $stty = true;
|
||||
private static $stdinIsInteractive;
|
||||
|
||||
/**
|
||||
* Asks a question to the user.
|
||||
@@ -47,38 +50,32 @@ class QuestionHelper extends Helper
|
||||
}
|
||||
|
||||
if (!$input->isInteractive()) {
|
||||
$default = $question->getDefault();
|
||||
|
||||
if (null !== $default && $question instanceof ChoiceQuestion) {
|
||||
$choices = $question->getChoices();
|
||||
|
||||
if (!$question->isMultiselect()) {
|
||||
return isset($choices[$default]) ? $choices[$default] : $default;
|
||||
}
|
||||
|
||||
$default = explode(',', $default);
|
||||
foreach ($default as $k => $v) {
|
||||
$v = trim($v);
|
||||
$default[$k] = isset($choices[$v]) ? $choices[$v] : $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
return $this->getDefaultAnswer($question);
|
||||
}
|
||||
|
||||
if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) {
|
||||
$this->inputStream = $stream;
|
||||
}
|
||||
|
||||
if (!$question->getValidator()) {
|
||||
return $this->doAsk($output, $question);
|
||||
try {
|
||||
if (!$question->getValidator()) {
|
||||
return $this->doAsk($output, $question);
|
||||
}
|
||||
|
||||
$interviewer = function () use ($output, $question) {
|
||||
return $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;
|
||||
}
|
||||
|
||||
$interviewer = function () use ($output, $question) {
|
||||
return $this->doAsk($output, $question);
|
||||
};
|
||||
|
||||
return $this->validateAttempts($interviewer, $output, $question);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +97,7 @@ class QuestionHelper extends Helper
|
||||
/**
|
||||
* Asks the question to the user.
|
||||
*
|
||||
* @return bool|mixed|string|null
|
||||
* @return mixed
|
||||
*
|
||||
* @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
|
||||
*/
|
||||
@@ -108,14 +105,15 @@ class QuestionHelper extends Helper
|
||||
{
|
||||
$this->writePrompt($output, $question);
|
||||
|
||||
$inputStream = $this->inputStream ?: STDIN;
|
||||
$autocomplete = $question->getAutocompleterValues();
|
||||
$inputStream = $this->inputStream ?: \STDIN;
|
||||
$autocomplete = $question->getAutocompleterCallback();
|
||||
|
||||
if (null === $autocomplete || !$this->hasSttyAvailable()) {
|
||||
if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) {
|
||||
$ret = false;
|
||||
if ($question->isHidden()) {
|
||||
try {
|
||||
$ret = trim($this->getHiddenResponse($output, $inputStream));
|
||||
$hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable());
|
||||
$ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse;
|
||||
} catch (RuntimeException $e) {
|
||||
if (!$question->isHiddenFallback()) {
|
||||
throw $e;
|
||||
@@ -124,14 +122,19 @@ class QuestionHelper extends Helper
|
||||
}
|
||||
|
||||
if (false === $ret) {
|
||||
$cp = $this->setIOCodepage();
|
||||
$ret = fgets($inputStream, 4096);
|
||||
$ret = $this->resetIOCodepage($cp, $ret);
|
||||
if (false === $ret) {
|
||||
throw new RuntimeException('Aborted');
|
||||
throw new MissingInputException('Aborted.');
|
||||
}
|
||||
if ($question->isTrimmable()) {
|
||||
$ret = trim($ret);
|
||||
}
|
||||
$ret = trim($ret);
|
||||
}
|
||||
} else {
|
||||
$ret = trim($this->autocomplete($output, $question, $inputStream, \is_array($autocomplete) ? $autocomplete : iterator_to_array($autocomplete, false)));
|
||||
$autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete);
|
||||
$ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete;
|
||||
}
|
||||
|
||||
if ($output instanceof ConsoleSectionOutput) {
|
||||
@@ -147,6 +150,36 @@ class QuestionHelper extends Helper
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private function getDefaultAnswer(Question $question)
|
||||
{
|
||||
$default = $question->getDefault();
|
||||
|
||||
if (null === $default) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if ($validator = $question->getValidator()) {
|
||||
return \call_user_func($question->getValidator(), $default);
|
||||
} elseif ($question instanceof ChoiceQuestion) {
|
||||
$choices = $question->getChoices();
|
||||
|
||||
if (!$question->isMultiselect()) {
|
||||
return $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.
|
||||
*/
|
||||
@@ -155,15 +188,9 @@ class QuestionHelper extends Helper
|
||||
$message = $question->getQuestion();
|
||||
|
||||
if ($question instanceof ChoiceQuestion) {
|
||||
$maxWidth = max(array_map(array($this, 'strlen'), array_keys($question->getChoices())));
|
||||
|
||||
$messages = (array) $question->getQuestion();
|
||||
foreach ($question->getChoices() as $key => $value) {
|
||||
$width = $maxWidth - $this->strlen($key);
|
||||
$messages[] = ' [<info>'.$key.str_repeat(' ', $width).'</info>] '.$value;
|
||||
}
|
||||
|
||||
$output->writeln($messages);
|
||||
$output->writeln(array_merge([
|
||||
$question->getQuestion(),
|
||||
], $this->formatChoiceQuestionChoices($question, 'info')));
|
||||
|
||||
$message = $question->getPrompt();
|
||||
}
|
||||
@@ -171,6 +198,26 @@ class QuestionHelper extends Helper
|
||||
$output->write($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function formatChoiceQuestionChoices(ChoiceQuestion $question, $tag)
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
$maxWidth = max(array_map([__CLASS__, 'strlen'], array_keys($choices = $question->getChoices())));
|
||||
|
||||
foreach ($choices as $key => $value) {
|
||||
$padding = str_repeat(' ', $maxWidth - self::strlen($key));
|
||||
|
||||
$messages[] = sprintf(" [<$tag>%s$padding</$tag>] %s", $key, $value);
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs an error message.
|
||||
*/
|
||||
@@ -188,17 +235,16 @@ class QuestionHelper extends Helper
|
||||
/**
|
||||
* Autocompletes a question.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param Question $question
|
||||
* @param resource $inputStream
|
||||
* @param resource $inputStream
|
||||
*/
|
||||
private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete): string
|
||||
private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string
|
||||
{
|
||||
$fullChoice = '';
|
||||
$ret = '';
|
||||
|
||||
$i = 0;
|
||||
$ofs = -1;
|
||||
$matches = $autocomplete;
|
||||
$matches = $autocomplete($ret);
|
||||
$numMatches = \count($matches);
|
||||
|
||||
$sttyMode = shell_exec('stty -g');
|
||||
@@ -213,24 +259,28 @@ class QuestionHelper extends Helper
|
||||
while (!feof($inputStream)) {
|
||||
$c = fread($inputStream, 1);
|
||||
|
||||
// Backspace Character
|
||||
if ("\177" === $c) {
|
||||
// as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
|
||||
if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
|
||||
shell_exec(sprintf('stty %s', $sttyMode));
|
||||
throw new MissingInputException('Aborted.');
|
||||
} elseif ("\177" === $c) { // Backspace Character
|
||||
if (0 === $numMatches && 0 !== $i) {
|
||||
--$i;
|
||||
$fullChoice = self::substr($fullChoice, 0, $i);
|
||||
// Move cursor backwards
|
||||
$output->write("\033[1D");
|
||||
}
|
||||
|
||||
if (0 === $i) {
|
||||
$ofs = -1;
|
||||
$matches = $autocomplete;
|
||||
$matches = $autocomplete($ret);
|
||||
$numMatches = \count($matches);
|
||||
} else {
|
||||
$numMatches = 0;
|
||||
}
|
||||
|
||||
// Pop the last character off the end of our string
|
||||
$ret = substr($ret, 0, $i);
|
||||
$ret = self::substr($ret, 0, $i);
|
||||
} elseif ("\033" === $c) {
|
||||
// Did we read an escape sequence?
|
||||
$c .= fread($inputStream, 2);
|
||||
@@ -251,10 +301,21 @@ class QuestionHelper extends Helper
|
||||
} elseif (\ord($c) < 32) {
|
||||
if ("\t" === $c || "\n" === $c) {
|
||||
if ($numMatches > 0 && -1 !== $ofs) {
|
||||
$ret = $matches[$ofs];
|
||||
$ret = (string) $matches[$ofs];
|
||||
// Echo out remaining chars for current match
|
||||
$output->write(substr($ret, $i));
|
||||
$i = \strlen($ret);
|
||||
$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),
|
||||
function ($match) use ($ret) {
|
||||
return '' === $ret || str_starts_with($match, $ret);
|
||||
}
|
||||
);
|
||||
$numMatches = \count($matches);
|
||||
$ofs = -1;
|
||||
}
|
||||
|
||||
if ("\n" === $c) {
|
||||
@@ -267,16 +328,27 @@ class QuestionHelper extends Helper
|
||||
|
||||
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 as $value) {
|
||||
foreach ($autocomplete($ret) as $value) {
|
||||
// If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
|
||||
if (0 === strpos($value, $ret)) {
|
||||
if (str_starts_with($value, $tempRet)) {
|
||||
$matches[$numMatches++] = $value;
|
||||
}
|
||||
}
|
||||
@@ -288,8 +360,9 @@ class QuestionHelper extends Helper
|
||||
if ($numMatches > 0 && -1 !== $ofs) {
|
||||
// Save cursor position
|
||||
$output->write("\0337");
|
||||
// Write highlighted text
|
||||
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $i)).'</hl>');
|
||||
// Write highlighted text, complete the partially entered response
|
||||
$charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
|
||||
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
|
||||
// Restore cursor position
|
||||
$output->write("\0338");
|
||||
}
|
||||
@@ -298,18 +371,33 @@ class QuestionHelper extends Helper
|
||||
// Reset stty so it behaves normally again
|
||||
shell_exec(sprintf('stty %s', $sttyMode));
|
||||
|
||||
return $ret;
|
||||
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 OutputInterface $output An Output instance
|
||||
* @param resource $inputStream The handler resource
|
||||
* @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): string
|
||||
private function getHiddenResponse(OutputInterface $output, $inputStream, bool $trimmable = true): string
|
||||
{
|
||||
if ('\\' === \DIRECTORY_SEPARATOR) {
|
||||
$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
|
||||
@@ -321,7 +409,8 @@ class QuestionHelper extends Helper
|
||||
$exe = $tmpExe;
|
||||
}
|
||||
|
||||
$value = rtrim(shell_exec($exe));
|
||||
$sExec = shell_exec('"'.$exe.'"');
|
||||
$value = $trimmable ? rtrim($sExec) : $sExec;
|
||||
$output->writeln('');
|
||||
|
||||
if (isset($tmpExe)) {
|
||||
@@ -331,41 +420,34 @@ class QuestionHelper extends Helper
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($this->hasSttyAvailable()) {
|
||||
if (self::$stty && Terminal::hasSttyAvailable()) {
|
||||
$sttyMode = shell_exec('stty -g');
|
||||
|
||||
shell_exec('stty -echo');
|
||||
$value = fgets($inputStream, 4096);
|
||||
} elseif ($this->isInteractiveInput($inputStream)) {
|
||||
throw new RuntimeException('Unable to hide the response.');
|
||||
}
|
||||
|
||||
$value = fgets($inputStream, 4096);
|
||||
|
||||
if (self::$stty && Terminal::hasSttyAvailable()) {
|
||||
shell_exec(sprintf('stty %s', $sttyMode));
|
||||
}
|
||||
|
||||
if (false === $value) {
|
||||
throw new RuntimeException('Aborted');
|
||||
}
|
||||
|
||||
if (false === $value) {
|
||||
throw new MissingInputException('Aborted.');
|
||||
}
|
||||
if ($trimmable) {
|
||||
$value = trim($value);
|
||||
$output->writeln('');
|
||||
|
||||
return $value;
|
||||
}
|
||||
$output->writeln('');
|
||||
|
||||
if (false !== $shell = $this->getShell()) {
|
||||
$readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword';
|
||||
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
|
||||
$value = rtrim(shell_exec($command));
|
||||
$output->writeln('');
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
throw new RuntimeException('Unable to hide the response.');
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an attempt.
|
||||
*
|
||||
* @param callable $interviewer A callable that will ask for a question and return the result
|
||||
* @param OutputInterface $output An Output instance
|
||||
* @param Question $question A Question instance
|
||||
* @param callable $interviewer A callable that will ask for a question and return the result
|
||||
*
|
||||
* @return mixed The validated response
|
||||
*
|
||||
@@ -375,13 +457,14 @@ class QuestionHelper extends Helper
|
||||
{
|
||||
$error = null;
|
||||
$attempts = $question->getMaxAttempts();
|
||||
|
||||
while (null === $attempts || $attempts--) {
|
||||
if (null !== $error) {
|
||||
$this->writeError($output, $error);
|
||||
}
|
||||
|
||||
try {
|
||||
return \call_user_func($question->getValidator(), $interviewer());
|
||||
return $question->getValidator()($interviewer());
|
||||
} catch (RuntimeException $e) {
|
||||
throw $e;
|
||||
} catch (\Exception $error) {
|
||||
@@ -391,44 +474,67 @@ class QuestionHelper extends Helper
|
||||
throw $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a valid unix shell.
|
||||
*
|
||||
* @return string|bool The valid shell name, false in case no valid shell is found
|
||||
*/
|
||||
private function getShell()
|
||||
private function isInteractiveInput($inputStream): bool
|
||||
{
|
||||
if (null !== self::$shell) {
|
||||
return self::$shell;
|
||||
if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self::$shell = false;
|
||||
if (null !== self::$stdinIsInteractive) {
|
||||
return self::$stdinIsInteractive;
|
||||
}
|
||||
|
||||
if (file_exists('/usr/bin/env')) {
|
||||
// handle other OSs with bash/zsh/ksh/csh if available to hide the answer
|
||||
$test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
|
||||
foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
|
||||
if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
|
||||
self::$shell = $sh;
|
||||
break;
|
||||
}
|
||||
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('exec')) {
|
||||
return self::$stdinIsInteractive = true;
|
||||
}
|
||||
|
||||
exec('stty 2> /dev/null', $output, $status);
|
||||
|
||||
return self::$stdinIsInteractive = 1 !== $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets console I/O to the host code page.
|
||||
*
|
||||
* @return int Previous code page in IBM/EBCDIC format
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param string|false $input
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
private function resetIOCodepage(int $cp, $input)
|
||||
{
|
||||
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 self::$shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Stty is available or not.
|
||||
*/
|
||||
private function hasSttyAvailable(): bool
|
||||
{
|
||||
if (null !== self::$stty) {
|
||||
return self::$stty;
|
||||
}
|
||||
|
||||
exec('stty 2>&1', $output, $exitcode);
|
||||
|
||||
return self::$stty = 0 === $exitcode;
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ class SymfonyQuestionHelper extends QuestionHelper
|
||||
|
||||
case $question instanceof ChoiceQuestion:
|
||||
$choices = $question->getChoices();
|
||||
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape(isset($choices[$default]) ? $choices[$default] : $default));
|
||||
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($choices[$default] ?? $default));
|
||||
|
||||
break;
|
||||
|
||||
@@ -68,15 +68,15 @@ class SymfonyQuestionHelper extends QuestionHelper
|
||||
|
||||
$output->writeln($text);
|
||||
|
||||
if ($question instanceof ChoiceQuestion) {
|
||||
$width = max(array_map('strlen', array_keys($question->getChoices())));
|
||||
$prompt = ' > ';
|
||||
|
||||
foreach ($question->getChoices() as $key => $value) {
|
||||
$output->writeln(sprintf(" [<comment>%-${width}s</comment>] %s", $key, $value));
|
||||
}
|
||||
if ($question instanceof ChoiceQuestion) {
|
||||
$output->writeln($this->formatChoiceQuestionChoices($question, 'comment'));
|
||||
|
||||
$prompt = $question->getPrompt();
|
||||
}
|
||||
|
||||
$output->write(' > ');
|
||||
$output->write($prompt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
277
vendor/symfony/console/Helper/Table.php
vendored
277
vendor/symfony/console/Helper/Table.php
vendored
@@ -13,6 +13,7 @@ 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;
|
||||
@@ -41,17 +42,18 @@ class Table
|
||||
/**
|
||||
* Table headers.
|
||||
*/
|
||||
private $headers = array();
|
||||
private $headers = [];
|
||||
|
||||
/**
|
||||
* Table rows.
|
||||
*/
|
||||
private $rows = array();
|
||||
private $rows = [];
|
||||
private $horizontal = false;
|
||||
|
||||
/**
|
||||
* Column widths cache.
|
||||
*/
|
||||
private $effectiveColumnWidths = array();
|
||||
private $effectiveColumnWidths = [];
|
||||
|
||||
/**
|
||||
* Number of columns cache.
|
||||
@@ -73,15 +75,15 @@ class Table
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $columnStyles = array();
|
||||
private $columnStyles = [];
|
||||
|
||||
/**
|
||||
* User set column widths.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $columnWidths = array();
|
||||
private $columnMaxWidths = array();
|
||||
private $columnWidths = [];
|
||||
private $columnMaxWidths = [];
|
||||
|
||||
private static $styles;
|
||||
|
||||
@@ -101,8 +103,7 @@ class Table
|
||||
/**
|
||||
* Sets a style definition.
|
||||
*
|
||||
* @param string $name The style name
|
||||
* @param TableStyle $style A TableStyle instance
|
||||
* @param string $name The style name
|
||||
*/
|
||||
public static function setStyleDefinition($name, TableStyle $style)
|
||||
{
|
||||
@@ -206,13 +207,11 @@ class Table
|
||||
/**
|
||||
* Sets the minimum width of all columns.
|
||||
*
|
||||
* @param array $widths
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setColumnWidths(array $widths)
|
||||
{
|
||||
$this->columnWidths = array();
|
||||
$this->columnWidths = [];
|
||||
foreach ($widths as $index => $width) {
|
||||
$this->setColumnWidth($index, $width);
|
||||
}
|
||||
@@ -243,7 +242,7 @@ class Table
|
||||
{
|
||||
$headers = array_values($headers);
|
||||
if (!empty($headers) && !\is_array($headers[0])) {
|
||||
$headers = array($headers);
|
||||
$headers = [$headers];
|
||||
}
|
||||
|
||||
$this->headers = $headers;
|
||||
@@ -253,7 +252,7 @@ class Table
|
||||
|
||||
public function setRows(array $rows)
|
||||
{
|
||||
$this->rows = array();
|
||||
$this->rows = [];
|
||||
|
||||
return $this->addRows($rows);
|
||||
}
|
||||
@@ -324,6 +323,13 @@ class Table
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setHorizontal(bool $horizontal = true): self
|
||||
{
|
||||
$this->horizontal = $horizontal;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders table to output.
|
||||
*
|
||||
@@ -339,40 +345,84 @@ class Table
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$rows = array_merge($this->headers, array($divider = new TableSeparator()), $this->rows);
|
||||
$this->calculateNumberOfColumns($rows);
|
||||
|
||||
$rows = $this->buildTableRows($rows);
|
||||
$this->calculateColumnsWidth($rows);
|
||||
|
||||
$isHeader = true;
|
||||
$isFirstRow = false;
|
||||
foreach ($rows as $row) {
|
||||
if ($divider === $row) {
|
||||
$isHeader = false;
|
||||
$isFirstRow = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($row instanceof TableSeparator) {
|
||||
$this->renderRowSeparator();
|
||||
|
||||
continue;
|
||||
}
|
||||
if (!$row) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($isHeader || $isFirstRow) {
|
||||
if ($isFirstRow) {
|
||||
$this->renderRowSeparator(self::SEPARATOR_TOP_BOTTOM);
|
||||
$isFirstRow = false;
|
||||
} else {
|
||||
$this->renderRowSeparator(self::SEPARATOR_TOP, $this->headerTitle, $this->style->getHeaderTitleFormat());
|
||||
$divider = new TableSeparator();
|
||||
if ($this->horizontal) {
|
||||
$rows = [];
|
||||
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 ($rows[$i][0] instanceof TableCell && $rows[$i][0]->getColspan() >= 2) {
|
||||
// Noop, there is a "title"
|
||||
} else {
|
||||
$rows[$i][] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$rows = array_merge($this->headers, [$divider], $this->rows);
|
||||
}
|
||||
|
||||
$this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat());
|
||||
$this->calculateNumberOfColumns($rows);
|
||||
|
||||
$rowGroups = $this->buildTableRows($rows);
|
||||
$this->calculateColumnsWidth($rowGroups);
|
||||
|
||||
$isHeader = !$this->horizontal;
|
||||
$isFirstRow = $this->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 ($this->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());
|
||||
|
||||
@@ -400,13 +450,13 @@ class Table
|
||||
|
||||
$crossings = $this->style->getCrossingChars();
|
||||
if (self::SEPARATOR_MID === $type) {
|
||||
list($horizontal, $leftChar, $midChar, $rightChar) = array($borders[2], $crossings[8], $crossings[0], $crossings[4]);
|
||||
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]];
|
||||
} elseif (self::SEPARATOR_TOP === $type) {
|
||||
list($horizontal, $leftChar, $midChar, $rightChar) = array($borders[0], $crossings[1], $crossings[2], $crossings[3]);
|
||||
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]];
|
||||
} elseif (self::SEPARATOR_TOP_BOTTOM === $type) {
|
||||
list($horizontal, $leftChar, $midChar, $rightChar) = array($borders[0], $crossings[9], $crossings[10], $crossings[11]);
|
||||
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]];
|
||||
} else {
|
||||
list($horizontal, $leftChar, $midChar, $rightChar) = array($borders[0], $crossings[7], $crossings[6], $crossings[5]);
|
||||
[$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]];
|
||||
}
|
||||
|
||||
$markup = $leftChar;
|
||||
@@ -424,7 +474,7 @@ class Table
|
||||
$formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...');
|
||||
}
|
||||
|
||||
$titleStart = ($markupLength - $titleLength) / 2;
|
||||
$titleStart = intdiv($markupLength - $titleLength, 2);
|
||||
if (false === mb_detect_encoding($markup, null, true)) {
|
||||
$markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength);
|
||||
} else {
|
||||
@@ -438,7 +488,7 @@ class Table
|
||||
/**
|
||||
* Renders vertical column separator.
|
||||
*/
|
||||
private function renderColumnSeparator($type = self::BORDER_OUTSIDE)
|
||||
private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string
|
||||
{
|
||||
$borders = $this->style->getBorderChars();
|
||||
|
||||
@@ -452,13 +502,17 @@ class Table
|
||||
*
|
||||
* | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
|
||||
*/
|
||||
private function renderRow(array $row, string $cellFormat)
|
||||
private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null)
|
||||
{
|
||||
$rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
|
||||
$columns = $this->getRowColumns($row);
|
||||
$last = \count($columns) - 1;
|
||||
foreach ($columns as $i => $column) {
|
||||
$rowContent .= $this->renderCell($row, $column, $cellFormat);
|
||||
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);
|
||||
@@ -467,9 +521,9 @@ class Table
|
||||
/**
|
||||
* Renders table cell with padding.
|
||||
*/
|
||||
private function renderCell(array $row, int $column, string $cellFormat)
|
||||
private function renderCell(array $row, int $column, string $cellFormat): string
|
||||
{
|
||||
$cell = isset($row[$column]) ? $row[$column] : '';
|
||||
$cell = $row[$column] ?? '';
|
||||
$width = $this->effectiveColumnWidths[$column];
|
||||
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
|
||||
// add the width of the following columns(numbers of colspan).
|
||||
@@ -498,9 +552,9 @@ class Table
|
||||
/**
|
||||
* Calculate number of columns for this table.
|
||||
*/
|
||||
private function calculateNumberOfColumns($rows)
|
||||
private function calculateNumberOfColumns(array $rows)
|
||||
{
|
||||
$columns = array(0);
|
||||
$columns = [0];
|
||||
foreach ($rows as $row) {
|
||||
if ($row instanceof TableSeparator) {
|
||||
continue;
|
||||
@@ -512,58 +566,68 @@ class Table
|
||||
$this->numberOfColumns = max($columns);
|
||||
}
|
||||
|
||||
private function buildTableRows($rows)
|
||||
private function buildTableRows(array $rows): TableRows
|
||||
{
|
||||
/** @var WrappableOutputFormatterInterface $formatter */
|
||||
$formatter = $this->output->getFormatter();
|
||||
$unmergedRows = array();
|
||||
$unmergedRows = [];
|
||||
for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
|
||||
$rows = $this->fillNextRows($rows, $rowKey);
|
||||
|
||||
// Remove any new line breaks and replace it with a new line
|
||||
foreach ($rows[$rowKey] as $column => $cell) {
|
||||
$colspan = $cell instanceof TableCell ? $cell->getColspan() : 1;
|
||||
|
||||
if (isset($this->columnMaxWidths[$column]) && Helper::strlenWithoutDecoration($formatter, $cell) > $this->columnMaxWidths[$column]) {
|
||||
$cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column]);
|
||||
$cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan);
|
||||
}
|
||||
if (!strstr($cell, "\n")) {
|
||||
if (!strstr($cell ?? '', "\n")) {
|
||||
continue;
|
||||
}
|
||||
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
|
||||
$escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell)));
|
||||
$cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped;
|
||||
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default></>\n", $cell));
|
||||
foreach ($lines as $lineKey => $line) {
|
||||
if ($cell instanceof TableCell) {
|
||||
$line = new TableCell($line, array('colspan' => $cell->getColspan()));
|
||||
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) {
|
||||
return new TableRows(function () use ($rows, $unmergedRows): \Traversable {
|
||||
foreach ($rows as $rowKey => $row) {
|
||||
yield $this->fillCells($row);
|
||||
$rowGroup = [$row instanceof TableSeparator ? $row : $this->fillCells($row)];
|
||||
|
||||
if (isset($unmergedRows[$rowKey])) {
|
||||
foreach ($unmergedRows[$rowKey] as $row) {
|
||||
yield $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, array(new TableSeparator()), $this->rows))));
|
||||
$numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows))));
|
||||
|
||||
if ($this->headers) {
|
||||
++$numberOfRows; // Add row for header separator
|
||||
}
|
||||
|
||||
++$numberOfRows; // Add row for footer separator
|
||||
if (\count($this->rows) > 0) {
|
||||
++$numberOfRows; // Add row for footer separator
|
||||
}
|
||||
|
||||
return $numberOfRows;
|
||||
}
|
||||
@@ -575,27 +639,27 @@ class Table
|
||||
*/
|
||||
private function fillNextRows(array $rows, int $line): array
|
||||
{
|
||||
$unmergedRows = array();
|
||||
$unmergedRows = [];
|
||||
foreach ($rows[$line] as $column => $cell) {
|
||||
if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
|
||||
throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing __toString, %s given.', \gettype($cell)));
|
||||
if (null !== $cell && !$cell instanceof TableCell && !\is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
|
||||
throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', \gettype($cell)));
|
||||
}
|
||||
if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
|
||||
$nbLines = $cell->getRowspan() - 1;
|
||||
$lines = array($cell);
|
||||
$lines = [$cell];
|
||||
if (strstr($cell, "\n")) {
|
||||
$lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
|
||||
$nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
|
||||
|
||||
$rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan()));
|
||||
$rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan()]);
|
||||
unset($lines[0]);
|
||||
}
|
||||
|
||||
// create a two dimensional array (rowspan x colspan)
|
||||
$unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, array()), $unmergedRows);
|
||||
$unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows);
|
||||
foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
|
||||
$value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : '';
|
||||
$unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan()));
|
||||
$value = $lines[$unmergedRowKey - $line] ?? '';
|
||||
$unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan()]);
|
||||
if ($nbLines === $unmergedRowKey - $line) {
|
||||
break;
|
||||
}
|
||||
@@ -608,7 +672,7 @@ class Table
|
||||
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, array($cell));
|
||||
array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]);
|
||||
}
|
||||
} else {
|
||||
$row = $this->copyRow($rows, $unmergedRowKey - 1);
|
||||
@@ -617,7 +681,7 @@ class Table
|
||||
$row[$column] = $unmergedRow[$column];
|
||||
}
|
||||
}
|
||||
array_splice($rows, $unmergedRowKey, 0, array($row));
|
||||
array_splice($rows, $unmergedRowKey, 0, [$row]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,9 +691,10 @@ class Table
|
||||
/**
|
||||
* fill cells for a row that contains colspan > 1.
|
||||
*/
|
||||
private function fillCells($row)
|
||||
private function fillCells(iterable $row)
|
||||
{
|
||||
$newRow = array();
|
||||
$newRow = [];
|
||||
|
||||
foreach ($row as $column => $cell) {
|
||||
$newRow[] = $cell;
|
||||
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
|
||||
@@ -649,7 +714,7 @@ class Table
|
||||
foreach ($row as $cellKey => $cellValue) {
|
||||
$row[$cellKey] = '';
|
||||
if ($cellValue instanceof TableCell) {
|
||||
$row[$cellKey] = new TableCell('', array('colspan' => $cellValue->getColspan()));
|
||||
$row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,29 +753,31 @@ class Table
|
||||
/**
|
||||
* Calculates columns widths.
|
||||
*/
|
||||
private function calculateColumnsWidth(iterable $rows)
|
||||
private function calculateColumnsWidth(iterable $groups)
|
||||
{
|
||||
for ($column = 0; $column < $this->numberOfColumns; ++$column) {
|
||||
$lengths = array();
|
||||
foreach ($rows as $row) {
|
||||
if ($row instanceof TableSeparator) {
|
||||
continue;
|
||||
}
|
||||
$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::strlen($textContent);
|
||||
if ($textLength > 0) {
|
||||
$contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
|
||||
foreach ($contentColumns as $position => $content) {
|
||||
$row[$i + $position] = $content;
|
||||
foreach ($row as $i => $cell) {
|
||||
if ($cell instanceof TableCell) {
|
||||
$textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
|
||||
$textLength = Helper::strlen($textContent);
|
||||
if ($textLength > 0) {
|
||||
$contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
|
||||
foreach ($contentColumns as $position => $content) {
|
||||
$row[$i + $position] = $content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$lengths[] = $this->getCellWidth($row, $column);
|
||||
$lengths[] = $this->getCellWidth($row, $column);
|
||||
}
|
||||
}
|
||||
|
||||
$this->effectiveColumnWidths[$column] = max($lengths) + Helper::strlen($this->style->getCellRowContentFormat()) - 2;
|
||||
@@ -731,7 +798,7 @@ class Table
|
||||
$cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
|
||||
}
|
||||
|
||||
$columnWidth = isset($this->columnWidths[$column]) ? $this->columnWidths[$column] : 0;
|
||||
$columnWidth = $this->columnWidths[$column] ?? 0;
|
||||
$cellWidth = max($cellWidth, $columnWidth);
|
||||
|
||||
return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth;
|
||||
@@ -742,11 +809,11 @@ class Table
|
||||
*/
|
||||
private function cleanup()
|
||||
{
|
||||
$this->effectiveColumnWidths = array();
|
||||
$this->effectiveColumnWidths = [];
|
||||
$this->numberOfColumns = null;
|
||||
}
|
||||
|
||||
private static function initStyles()
|
||||
private static function initStyles(): array
|
||||
{
|
||||
$borderless = new TableStyle();
|
||||
$borderless
|
||||
@@ -758,9 +825,9 @@ class Table
|
||||
$compact = new TableStyle();
|
||||
$compact
|
||||
->setHorizontalBorderChars('')
|
||||
->setVerticalBorderChars(' ')
|
||||
->setVerticalBorderChars('')
|
||||
->setDefaultCrossingChar('')
|
||||
->setCellRowContentFormat('%s')
|
||||
->setCellRowContentFormat('%s ')
|
||||
;
|
||||
|
||||
$styleGuide = new TableStyle();
|
||||
@@ -783,17 +850,17 @@ class Table
|
||||
->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣')
|
||||
;
|
||||
|
||||
return array(
|
||||
return [
|
||||
'default' => new TableStyle(),
|
||||
'borderless' => $borderless,
|
||||
'compact' => $compact,
|
||||
'symfony-style-guide' => $styleGuide,
|
||||
'box' => $box,
|
||||
'box-double' => $boxDouble,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
private function resolveStyle($name)
|
||||
private function resolveStyle($name): TableStyle
|
||||
{
|
||||
if ($name instanceof TableStyle) {
|
||||
return $name;
|
||||
|
6
vendor/symfony/console/Helper/TableCell.php
vendored
6
vendor/symfony/console/Helper/TableCell.php
vendored
@@ -19,12 +19,12 @@ use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
class TableCell
|
||||
{
|
||||
private $value;
|
||||
private $options = array(
|
||||
private $options = [
|
||||
'rowspan' => 1,
|
||||
'colspan' => 1,
|
||||
);
|
||||
];
|
||||
|
||||
public function __construct(string $value = '', array $options = array())
|
||||
public function __construct(string $value = '', array $options = [])
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
|
2
vendor/symfony/console/Helper/TableRows.php
vendored
2
vendor/symfony/console/Helper/TableRows.php
vendored
@@ -23,7 +23,7 @@ class TableRows implements \IteratorAggregate
|
||||
$this->generator = $generator;
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
public function getIterator(): \Traversable
|
||||
{
|
||||
$g = $this->generator;
|
||||
|
||||
|
@@ -18,7 +18,7 @@ namespace Symfony\Component\Console\Helper;
|
||||
*/
|
||||
class TableSeparator extends TableCell
|
||||
{
|
||||
public function __construct(array $options = array())
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
parent::__construct('', $options);
|
||||
}
|
||||
|
26
vendor/symfony/console/Helper/TableStyle.php
vendored
26
vendor/symfony/console/Helper/TableStyle.php
vendored
@@ -46,7 +46,7 @@ class TableStyle
|
||||
private $cellRowFormat = '%s';
|
||||
private $cellRowContentFormat = ' %s ';
|
||||
private $borderFormat = '%s';
|
||||
private $padType = STR_PAD_RIGHT;
|
||||
private $padType = \STR_PAD_RIGHT;
|
||||
|
||||
/**
|
||||
* Sets padding character, used for cell padding.
|
||||
@@ -58,7 +58,7 @@ class TableStyle
|
||||
public function setPaddingChar($paddingChar)
|
||||
{
|
||||
if (!$paddingChar) {
|
||||
throw new LogicException('The padding char must not be empty');
|
||||
throw new LogicException('The padding char must not be empty.');
|
||||
}
|
||||
|
||||
$this->paddingChar = $paddingChar;
|
||||
@@ -112,7 +112,7 @@ class TableStyle
|
||||
*/
|
||||
public function setHorizontalBorderChar($horizontalBorderChar)
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setHorizontalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setHorizontalBorderChars() instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
return $this->setHorizontalBorderChars($horizontalBorderChar, $horizontalBorderChar);
|
||||
}
|
||||
@@ -126,7 +126,7 @@ class TableStyle
|
||||
*/
|
||||
public function getHorizontalBorderChar()
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
return $this->horizontalOutsideBorderChar;
|
||||
}
|
||||
@@ -168,7 +168,7 @@ class TableStyle
|
||||
*/
|
||||
public function setVerticalBorderChar($verticalBorderChar)
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setVerticalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use setVerticalBorderChars() instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
return $this->setVerticalBorderChars($verticalBorderChar, $verticalBorderChar);
|
||||
}
|
||||
@@ -182,7 +182,7 @@ class TableStyle
|
||||
*/
|
||||
public function getVerticalBorderChar()
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
return $this->verticalOutsideBorderChar;
|
||||
}
|
||||
@@ -192,14 +192,14 @@ class TableStyle
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getBorderChars()
|
||||
public function getBorderChars(): array
|
||||
{
|
||||
return array(
|
||||
return [
|
||||
$this->horizontalOutsideBorderChar,
|
||||
$this->verticalOutsideBorderChar,
|
||||
$this->horizontalInsideBorderChar,
|
||||
$this->verticalInsideBorderChar,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,7 +270,7 @@ class TableStyle
|
||||
*/
|
||||
public function setCrossingChar($crossingChar)
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use setDefaultCrossingChar() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use setDefaultCrossingChar() instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
return $this->setDefaultCrossingChar($crossingChar);
|
||||
}
|
||||
@@ -292,7 +292,7 @@ class TableStyle
|
||||
*/
|
||||
public function getCrossingChars(): array
|
||||
{
|
||||
return array(
|
||||
return [
|
||||
$this->crossingChar,
|
||||
$this->crossingTopLeftChar,
|
||||
$this->crossingTopMidChar,
|
||||
@@ -305,7 +305,7 @@ class TableStyle
|
||||
$this->crossingTopLeftBottomChar,
|
||||
$this->crossingTopMidBottomChar,
|
||||
$this->crossingTopRightBottomChar,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,7 +413,7 @@ class TableStyle
|
||||
*/
|
||||
public function setPadType($padType)
|
||||
{
|
||||
if (!\in_array($padType, array(STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH), true)) {
|
||||
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).');
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user