class ConsoleSectionOutput
@author Pierre du Plessis <pdples@gmail.com> @author Gabriel Ostrolucký <gabriel.ostrolucky@gmail.com>
Hierarchy
- class \Symfony\Component\Console\Output\Output implements \Symfony\Component\Console\Output\OutputInterface
- class \Symfony\Component\Console\Output\StreamOutput extends \Symfony\Component\Console\Output\Output
- class \Symfony\Component\Console\Output\ConsoleSectionOutput extends \Symfony\Component\Console\Output\StreamOutput
- class \Symfony\Component\Console\Output\StreamOutput extends \Symfony\Component\Console\Output\Output
Expanded class hierarchy of ConsoleSectionOutput
4 files declare their use of ConsoleSectionOutput
- ProgressBar.php in vendor/
symfony/ console/ Helper/ ProgressBar.php - QuestionHelper.php in vendor/
symfony/ console/ Helper/ QuestionHelper.php - SymfonyStyle.php in vendor/
symfony/ console/ Style/ SymfonyStyle.php - Table.php in vendor/
symfony/ console/ Helper/ Table.php
File
-
vendor/
symfony/ console/ Output/ ConsoleSectionOutput.php, line 22
Namespace
Symfony\Component\Console\OutputView source
class ConsoleSectionOutput extends StreamOutput {
private array $content = [];
private int $lines = 0;
private array $sections;
private Terminal $terminal;
private int $maxHeight = 0;
/**
* @param resource $stream
* @param ConsoleSectionOutput[] $sections
*/
public function __construct($stream, array &$sections, int $verbosity, bool $decorated, OutputFormatterInterface $formatter) {
parent::__construct($stream, $verbosity, $decorated, $formatter);
array_unshift($sections, $this);
$this->sections =& $sections;
$this->terminal = new Terminal();
}
/**
* Defines a maximum number of lines for this section.
*
* When more lines are added, the section will automatically scroll to the
* end (i.e. remove the first lines to comply with the max height).
*/
public function setMaxHeight(int $maxHeight) : void {
// when changing max height, clear output of current section and redraw again with the new height
$previousMaxHeight = $this->maxHeight;
$this->maxHeight = $maxHeight;
$existingContent = $this->popStreamContentUntilCurrentSection($previousMaxHeight ? min($previousMaxHeight, $this->lines) : $this->lines);
parent::doWrite($this->getVisibleContent(), false);
parent::doWrite($existingContent, false);
}
/**
* Clears previous output for this section.
*
* @param int $lines Number of lines to clear. If null, then the entire output of this section is cleared
*/
public function clear(?int $lines = null) : void {
if (!$this->content || !$this->isDecorated()) {
return;
}
if ($lines) {
array_splice($this->content, -$lines);
}
else {
$lines = $this->lines;
$this->content = [];
}
$this->lines -= $lines;
parent::doWrite($this->popStreamContentUntilCurrentSection($this->maxHeight ? min($this->maxHeight, $lines) : $lines), false);
}
/**
* Overwrites the previous output with a new message.
*/
public function overwrite(string|iterable $message) : void {
$this->clear();
$this->writeln($message);
}
public function getContent() : string {
return implode('', $this->content);
}
public function getVisibleContent() : string {
if (0 === $this->maxHeight) {
return $this->getContent();
}
return implode('', \array_slice($this->content, -$this->maxHeight));
}
/**
* @internal
*/
public function addContent(string $input, bool $newline = true) : int {
$width = $this->terminal
->getWidth();
$lines = explode(\PHP_EOL, $input);
$linesAdded = 0;
$count = \count($lines) - 1;
foreach ($lines as $i => $lineContent) {
// re-add the line break (that has been removed in the above `explode()` for
// - every line that is not the last line
// - if $newline is required, also add it to the last line
if ($i < $count || $newline) {
$lineContent .= \PHP_EOL;
}
// skip line if there is no text (or newline for that matter)
if ('' === $lineContent) {
continue;
}
// For the first line, check if the previous line (last entry of `$this->content`)
// needs to be continued (i.e. does not end with a line break).
if (0 === $i && false !== ($lastLine = end($this->content)) && !str_ends_with($lastLine, \PHP_EOL)) {
// deduct the line count of the previous line
$this->lines -= (int) ceil($this->getDisplayLength($lastLine) / $width) ?: 1;
// concatenate previous and new line
$lineContent = $lastLine . $lineContent;
// replace last entry of `$this->content` with the new expanded line
array_splice($this->content, -1, 1, $lineContent);
}
else {
// otherwise just add the new content
$this->content[] = $lineContent;
}
$linesAdded += (int) ceil($this->getDisplayLength($lineContent) / $width) ?: 1;
}
$this->lines += $linesAdded;
return $linesAdded;
}
/**
* @internal
*/
public function addNewLineOfInputSubmit() : void {
$this->content[] = \PHP_EOL;
++$this->lines;
}
protected function doWrite(string $message, bool $newline) : void {
// Simulate newline behavior for consistent output formatting, avoiding extra logic
if (!$newline && str_ends_with($message, \PHP_EOL)) {
$message = substr($message, 0, -\strlen(\PHP_EOL));
$newline = true;
}
if (!$this->isDecorated()) {
parent::doWrite($message, $newline);
return;
}
// Check if the previous line (last entry of `$this->content`) needs to be continued
// (i.e. does not end with a line break). In which case, it needs to be erased first.
$linesToClear = $deleteLastLine = ($lastLine = end($this->content) ?: '') && !str_ends_with($lastLine, \PHP_EOL) ? 1 : 0;
$linesAdded = $this->addContent($message, $newline);
if ($lineOverflow = $this->maxHeight > 0 && $this->lines > $this->maxHeight) {
// on overflow, clear the whole section and redraw again (to remove the first lines)
$linesToClear = $this->maxHeight;
}
$erasedContent = $this->popStreamContentUntilCurrentSection($linesToClear);
if ($lineOverflow) {
// redraw existing lines of the section
$previousLinesOfSection = \array_slice($this->content, $this->lines - $this->maxHeight, $this->maxHeight - $linesAdded);
parent::doWrite(implode('', $previousLinesOfSection), false);
}
// if the last line was removed, re-print its content together with the new content.
// otherwise, just print the new content.
parent::doWrite($deleteLastLine ? $lastLine . $message : $message, true);
parent::doWrite($erasedContent, false);
}
/**
* At initial stage, cursor is at the end of stream output. This method makes cursor crawl upwards until it hits
* current section. Then it erases content it crawled through. Optionally, it erases part of current section too.
*/
private function popStreamContentUntilCurrentSection(int $numberOfLinesToClearFromCurrentSection = 0) : string {
$numberOfLinesToClear = $numberOfLinesToClearFromCurrentSection;
$erasedContent = [];
foreach ($this->sections as $section) {
if ($section === $this) {
break;
}
$numberOfLinesToClear += $section->maxHeight ? min($section->lines, $section->maxHeight) : $section->lines;
if ('' !== ($sectionContent = $section->getVisibleContent())) {
if (!str_ends_with($sectionContent, \PHP_EOL)) {
$sectionContent .= \PHP_EOL;
}
$erasedContent[] = $sectionContent;
}
}
if ($numberOfLinesToClear > 0) {
// move cursor up n lines
parent::doWrite(\sprintf("\x1b[%dA", $numberOfLinesToClear), false);
// erase to end of screen
parent::doWrite("\x1b[0J", false);
}
return implode('', array_reverse($erasedContent));
}
private function getDisplayLength(string $text) : int {
return Helper::width(Helper::removeDecoration($this->getFormatter(), str_replace("\t", ' ', $text)));
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|
ConsoleSectionOutput::$content | private | property | |||
ConsoleSectionOutput::$lines | private | property | |||
ConsoleSectionOutput::$maxHeight | private | property | |||
ConsoleSectionOutput::$sections | private | property | |||
ConsoleSectionOutput::$terminal | private | property | |||
ConsoleSectionOutput::addContent | public | function | @internal | ||
ConsoleSectionOutput::addNewLineOfInputSubmit | public | function | @internal | ||
ConsoleSectionOutput::clear | public | function | Clears previous output for this section. | ||
ConsoleSectionOutput::doWrite | protected | function | Writes a message to the output. | Overrides StreamOutput::doWrite | |
ConsoleSectionOutput::getContent | public | function | |||
ConsoleSectionOutput::getDisplayLength | private | function | |||
ConsoleSectionOutput::getVisibleContent | public | function | |||
ConsoleSectionOutput::overwrite | public | function | Overwrites the previous output with a new message. | ||
ConsoleSectionOutput::popStreamContentUntilCurrentSection | private | function | At initial stage, cursor is at the end of stream output. This method makes cursor crawl upwards until it hits current section. Then it erases content it crawled through. Optionally, it erases part of current section too. |
||
ConsoleSectionOutput::setMaxHeight | public | function | Defines a maximum number of lines for this section. | ||
ConsoleSectionOutput::__construct | public | function | Overrides StreamOutput::__construct | ||
Output::$formatter | private | property | |||
Output::$verbosity | private | property | |||
Output::getFormatter | public | function | Returns current output formatter instance. | Overrides OutputInterface::getFormatter | |
Output::getVerbosity | public | function | Gets the current verbosity of the output. | Overrides OutputInterface::getVerbosity | |
Output::isDebug | public | function | Returns whether verbosity is debug (-vvv). | Overrides OutputInterface::isDebug | |
Output::isDecorated | public | function | Gets the decorated flag. | Overrides OutputInterface::isDecorated | |
Output::isQuiet | public | function | Returns whether verbosity is quiet (-q). | Overrides OutputInterface::isQuiet | |
Output::isSilent | public | function | |||
Output::isVerbose | public | function | Returns whether verbosity is verbose (-v). | Overrides OutputInterface::isVerbose | |
Output::isVeryVerbose | public | function | Returns whether verbosity is very verbose (-vv). | Overrides OutputInterface::isVeryVerbose | |
Output::setDecorated | public | function | Sets the decorated flag. | Overrides OutputInterface::setDecorated | 1 |
Output::setFormatter | public | function | Overrides OutputInterface::setFormatter | 1 | |
Output::setVerbosity | public | function | Sets the verbosity of the output. | Overrides OutputInterface::setVerbosity | 1 |
Output::write | public | function | Writes a message to the output. | Overrides OutputInterface::write | |
Output::writeln | public | function | Writes a message to the output and adds a newline at the end. | Overrides OutputInterface::writeln | |
OutputInterface::OUTPUT_NORMAL | public | constant | |||
OutputInterface::OUTPUT_PLAIN | public | constant | |||
OutputInterface::OUTPUT_RAW | public | constant | |||
OutputInterface::VERBOSITY_DEBUG | public | constant | |||
OutputInterface::VERBOSITY_NORMAL | public | constant | |||
OutputInterface::VERBOSITY_QUIET | public | constant | |||
OutputInterface::VERBOSITY_SILENT | public | constant | |||
OutputInterface::VERBOSITY_VERBOSE | public | constant | |||
OutputInterface::VERBOSITY_VERY_VERBOSE | public | constant | |||
StreamOutput::$stream | private | property | @var resource | ||
StreamOutput::getStream | public | function | Gets the stream attached to this StreamOutput instance. | ||
StreamOutput::hasColorSupport | protected | function | Returns true if the stream supports colorization. |