bug #39160 [Console] Use a partial buffer in SymfonyStyle (jderusse)
This PR was merged into the 4.4 branch.
Discussion
----------
[Console] Use a partial buffer in SymfonyStyle
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | Fix #39156
| License | MIT
| Doc PR | -
Symfony style needs to buffer output in order to get the last 2 chars in order to prepend Block.
By using a `BufferedOutput` symfony bufferize everything while it not needed.
This PR adds a new `TrimmedBufferOutput` that keep only the N last chars.
Commits
-------
18fca2984d
Use a partial buffer in SymfonyStyle
This commit is contained in:
commit
fd910eb5b5
67
src/Symfony/Component/Console/Output/TrimmedBufferOutput.php
Normal file
67
src/Symfony/Component/Console/Output/TrimmedBufferOutput.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?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\Output;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A BufferedOutput that keeps only the last N chars.
|
||||||
|
*
|
||||||
|
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||||
|
*/
|
||||||
|
class TrimmedBufferOutput extends Output
|
||||||
|
{
|
||||||
|
private $maxLength;
|
||||||
|
private $buffer = '';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
?int $verbosity = self::VERBOSITY_NORMAL,
|
||||||
|
bool $decorated = false,
|
||||||
|
OutputFormatterInterface $formatter = null,
|
||||||
|
int $maxLength
|
||||||
|
) {
|
||||||
|
if ($maxLength <= 0) {
|
||||||
|
throw new InvalidArgumentException(sprintf('"%s()" expects a strictly positive maxLength. Got %d.', __METHOD__, $maxLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($verbosity, $decorated, $formatter);
|
||||||
|
$this->maxLength = $maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empties buffer and returns its content.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function fetch()
|
||||||
|
{
|
||||||
|
$content = $this->buffer;
|
||||||
|
$this->buffer = '';
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doWrite($message, $newline)
|
||||||
|
{
|
||||||
|
$this->buffer .= $message;
|
||||||
|
|
||||||
|
if ($newline) {
|
||||||
|
$this->buffer .= \PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->buffer = substr($this->buffer, 0 - $this->maxLength);
|
||||||
|
}
|
||||||
|
}
|
@ -21,8 +21,8 @@ use Symfony\Component\Console\Helper\Table;
|
|||||||
use Symfony\Component\Console\Helper\TableCell;
|
use Symfony\Component\Console\Helper\TableCell;
|
||||||
use Symfony\Component\Console\Helper\TableSeparator;
|
use Symfony\Component\Console\Helper\TableSeparator;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\BufferedOutput;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Output\TrimmedBufferOutput;
|
||||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
use Symfony\Component\Console\Question\Question;
|
use Symfony\Component\Console\Question\Question;
|
||||||
@ -46,7 +46,7 @@ class SymfonyStyle extends OutputStyle
|
|||||||
public function __construct(InputInterface $input, OutputInterface $output)
|
public function __construct(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$this->input = $input;
|
$this->input = $input;
|
||||||
$this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter());
|
$this->bufferedOutput = new TrimmedBufferOutput($output->getVerbosity(), false, clone $output->getFormatter(), \DIRECTORY_SEPARATOR === '\\' ? 4 : 2);
|
||||||
// Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not.
|
// Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not.
|
||||||
$width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH;
|
$width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH;
|
||||||
$this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH);
|
$this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH);
|
||||||
@ -449,9 +449,8 @@ class SymfonyStyle extends OutputStyle
|
|||||||
|
|
||||||
private function writeBuffer(string $message, bool $newLine, int $type): void
|
private function writeBuffer(string $message, bool $newLine, int $type): void
|
||||||
{
|
{
|
||||||
// We need to know if the two last chars are PHP_EOL
|
// We need to know if the last chars are PHP_EOL
|
||||||
// Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer
|
$this->bufferedOutput->write($message, $newLine, $type);
|
||||||
$this->bufferedOutput->write(substr($message, -4), $newLine, $type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array
|
private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array
|
||||||
|
@ -14,8 +14,10 @@ namespace Symfony\Component\Console\Tests\Style;
|
|||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||||
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||||
|
use Symfony\Component\Console\Output\NullOutput;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
use Symfony\Component\Console\Tester\CommandTester;
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
@ -115,4 +117,17 @@ class SymfonyStyleTest extends TestCase
|
|||||||
|
|
||||||
$this->assertInstanceOf(SymfonyStyle::class, $style->getErrorStyle());
|
$this->assertInstanceOf(SymfonyStyle::class, $style->getErrorStyle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testMemoryConsumption()
|
||||||
|
{
|
||||||
|
$io = new SymfonyStyle(new ArrayInput([]), new NullOutput());
|
||||||
|
$str = 'teststr';
|
||||||
|
$io->writeln($str, SymfonyStyle::VERBOSITY_QUIET);
|
||||||
|
$start = memory_get_usage();
|
||||||
|
for ($i = 0; $i < 100; ++$i) {
|
||||||
|
$io->writeln($str, SymfonyStyle::VERBOSITY_QUIET);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSame(0, memory_get_usage() - $start);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user