diff --git a/UPGRADE-4.1.md b/UPGRADE-4.1.md index 15f6366ebe631..8d96b710ed9ec 100644 --- a/UPGRADE-4.1.md +++ b/UPGRADE-4.1.md @@ -12,6 +12,10 @@ Console * Deprecated the `setCrossingChar()` method in favor of the `setDefaultCrossingChar()` method in `TableStyle`. * The `Processor` class has been made final + * Deprecated the `setHorizontalBorderChar()` method in favor of the `setDefaultCrossingChars()` method in `TableStyle`. + * Deprecated the `getHorizontalBorderChar()` method in favor of the `getBorderChars()` method in `TableStyle`. + * Deprecated the `setVerticalBorderChar()` method in favor of the `setVerticalBorderChars()` method in `TableStyle`. + * Deprecated the `getVerticalBorderChar()` method in favor of the `getBorderChars()` method in `TableStyle`. DependencyInjection ------------------- diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index a8bda44be4086..2141dbb1df13c 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -11,6 +11,10 @@ Console ------- * Removed the `setCrossingChar()` method in favor of the `setDefaultCrossingChar()` method in `TableStyle`. + * Removed the `setHorizontalBorderChar()` method in favor of the `setDefaultCrossingChars()` method in `TableStyle`. + * Removed the `getHorizontalBorderChar()` method in favor of the `getBorderChars()` method in `TableStyle`. + * Removed the `setVerticalBorderChar()` method in favor of the `setVerticalBorderChars()` method in `TableStyle`. + * Removed the `getVerticalBorderChar()` method in favor of the `getBorderChars()` method in `TableStyle`. DependencyInjection ------------------- diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index df08c99cbc7b0..54f61dc289b9f 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -28,8 +28,11 @@ class Table { private const SEPARATOR_TOP = 0; - private const SEPARATOR_MID = 1; - private const SEPARATOR_BOTTOM = 2; + private const SEPARATOR_TOP_BOTTOM = 1; + private const SEPARATOR_MID = 2; + private const SEPARATOR_BOTTOM = 3; + private const BORDER_OUTSIDE = 0; + private const BORDER_INSIDE = 1; /** * Table headers. @@ -328,7 +331,7 @@ public function render() } if ($isHeader || $isFirstRow) { - $this->renderRowSeparator($isFirstRow ? self::SEPARATOR_MID : self::SEPARATOR_TOP); + $this->renderRowSeparator($isFirstRow ? self::SEPARATOR_TOP_BOTTOM : self::SEPARATOR_TOP); if ($isFirstRow) { $isFirstRow = false; } @@ -353,22 +356,25 @@ private function renderRowSeparator(int $type = self::SEPARATOR_MID) return; } - if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) { + $borders = $this->style->getBorderChars(); + if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) { return; } - $chars = $this->style->getCrossingChars(); + $crossings = $this->style->getCrossingChars(); if (self::SEPARATOR_MID === $type) { - list($leftChar, $midChar, $rightChar) = array($chars[8], $chars[0], $chars[4]); + list($horizontal, $leftChar, $midChar, $rightChar) = array($borders[2], $crossings[8], $crossings[0], $crossings[4]); } elseif (self::SEPARATOR_TOP === $type) { - list($leftChar, $midChar, $rightChar) = array($chars[1], $chars[2], $chars[3]); + list($horizontal, $leftChar, $midChar, $rightChar) = array($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]); } else { - list($leftChar, $midChar, $rightChar) = array($chars[7], $chars[6], $chars[5]); + list($horizontal, $leftChar, $midChar, $rightChar) = array($borders[0], $crossings[7], $crossings[6], $crossings[5]); } $markup = $leftChar; for ($column = 0; $column < $count; ++$column) { - $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->effectiveColumnWidths[$column]); + $markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]); $markup .= $column === $count - 1 ? $rightChar : $midChar; } @@ -378,9 +384,11 @@ private function renderRowSeparator(int $type = self::SEPARATOR_MID) /** * Renders vertical column separator. */ - private function renderColumnSeparator() + private function renderColumnSeparator($type = self::BORDER_OUTSIDE) { - return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()); + $borders = $this->style->getBorderChars(); + + return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]); } /** @@ -390,10 +398,12 @@ private function renderColumnSeparator() */ private function renderRow(array $row, string $cellFormat) { - $rowContent = $this->renderColumnSeparator(); - foreach ($this->getRowColumns($row) as $column) { + $rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE); + $columns = $this->getRowColumns($row); + $last = count($columns) - 1; + foreach ($columns as $i => $column) { $rowContent .= $this->renderCell($row, $column, $cellFormat); - $rowContent .= $this->renderColumnSeparator(); + $rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE); } $this->output->writeln($rowContent); } @@ -420,7 +430,7 @@ private function renderCell(array $row, int $column, string $cellFormat) $style = $this->getColumnStyle($column); if ($cell instanceof TableSeparator) { - return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)); + return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width)); } $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); @@ -648,7 +658,7 @@ private function calculateColumnsWidth(iterable $rows) private function getColumnSeparatorWidth(): int { - return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + return strlen(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3])); } private function getCellWidth(array $row, int $column): int @@ -678,39 +688,46 @@ private static function initStyles() { $borderless = new TableStyle(); $borderless - ->setHorizontalBorderChar('=') - ->setVerticalBorderChar(' ') + ->setHorizontalBorderChars('=') + ->setVerticalBorderChars(' ') ->setDefaultCrossingChar(' ') ; $compact = new TableStyle(); $compact - ->setHorizontalBorderChar('') - ->setVerticalBorderChar(' ') + ->setHorizontalBorderChars('') + ->setVerticalBorderChars(' ') ->setDefaultCrossingChar('') ->setCellRowContentFormat('%s') ; $styleGuide = new TableStyle(); $styleGuide - ->setHorizontalBorderChar('-') - ->setVerticalBorderChar(' ') + ->setHorizontalBorderChars('-') + ->setVerticalBorderChars(' ') ->setDefaultCrossingChar(' ') ->setCellHeaderFormat('%s') ; $box = (new TableStyle()) - ->setHorizontalBorderChar('─') - ->setVerticalBorderChar('│') + ->setHorizontalBorderChars('─') + ->setVerticalBorderChars('│') ->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├') ; + $boxDouble = (new TableStyle()) + ->setHorizontalBorderChars('═', '─') + ->setVerticalBorderChars('║', '│') + ->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣') + ; + return array( 'default' => new TableStyle(), 'borderless' => $borderless, 'compact' => $compact, 'symfony-style-guide' => $styleGuide, 'box' => $box, + 'box-double' => $boxDouble, ); } diff --git a/src/Symfony/Component/Console/Helper/TableStyle.php b/src/Symfony/Component/Console/Helper/TableStyle.php index 0c5d13e9f7dd7..a0d0b5b770737 100644 --- a/src/Symfony/Component/Console/Helper/TableStyle.php +++ b/src/Symfony/Component/Console/Helper/TableStyle.php @@ -24,8 +24,10 @@ class TableStyle { private $paddingChar = ' '; - private $horizontalBorderChar = '-'; - private $verticalBorderChar = '|'; + private $horizontalOutsideBorderChar = '-'; + private $horizontalInsideBorderChar = '-'; + private $verticalOutsideBorderChar = '|'; + private $verticalInsideBorderChar = '|'; private $crossingChar = '+'; private $crossingTopRightChar = '+'; private $crossingTopMidChar = '+'; @@ -35,6 +37,9 @@ class TableStyle private $crossingBottomMidChar = '+'; private $crossingBottomLeftChar = '+'; private $crossingMidLeftChar = '+'; + private $crossingTopLeftBottomChar = '+'; + private $crossingTopMidBottomChar = '+'; + private $crossingTopRightBottomChar = '+'; private $cellHeaderFormat = '%s'; private $cellRowFormat = '%s'; private $cellRowContentFormat = ' %s '; @@ -69,28 +74,85 @@ public function getPaddingChar() return $this->paddingChar; } + /** + * Sets horizontal border characters. + * + * + * ╔═══════════════╤══════════════════════════╤══════════════════╗ + * 1 ISBN 2 Title │ Author ║ + * ╠═══════════════╪══════════════════════════╪══════════════════╣ + * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ + * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ + * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ + * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ + * ╚═══════════════╧══════════════════════════╧══════════════════╝ + * + * + * @param string $outside Outside border char (see #1 of example) + * @param string|null $inside Inside border char (see #2 of example), equals $outside if null + */ + public function setHorizontalBorderChars(string $outside, string $inside = null): self + { + $this->horizontalOutsideBorderChar = $outside; + $this->horizontalInsideBorderChar = $inside ?? $outside; + + return $this; + } + /** * Sets horizontal border character. * * @param string $horizontalBorderChar * * @return $this + * + * @deprecated since Symfony 4.1, use {@link setHorizontalBorderChars()} instead. */ public function setHorizontalBorderChar($horizontalBorderChar) { - $this->horizontalBorderChar = $horizontalBorderChar; + @trigger_error(sprintf('Method %s() is deprecated since Symfony 4.1, use setHorizontalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); - return $this; + return $this->setHorizontalBorderChars($horizontalBorderChar, $horizontalBorderChar); } /** * Gets horizontal border character. * * @return string + * + * @deprecated since Symfony 4.1, use {@link getBorderChars()} instead. */ public function getHorizontalBorderChar() { - return $this->horizontalBorderChar; + @trigger_error(sprintf('Method %s() is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->horizontalOutsideBorderChar; + } + + /** + * Sets vertical border characters. + * + * + * ╔═══════════════╤══════════════════════════╤══════════════════╗ + * ║ ISBN │ Title │ Author ║ + * ╠═══════1═══════╪══════════════════════════╪══════════════════╣ + * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ + * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ + * ╟───────2───────┼──────────────────────────┼──────────────────╢ + * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ + * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ + * ╚═══════════════╧══════════════════════════╧══════════════════╝ + * + * + * @param string $outside Outside border char (see #1 of example) + * @param string|null $inside Inside border char (see #2 of example), equals $outside if null + */ + public function setVerticalBorderChars(string $outside, string $inside = null): self + { + $this->verticalOutsideBorderChar = $outside; + $this->verticalInsideBorderChar = $inside ?? $outside; + + return $this; } /** @@ -99,22 +161,43 @@ public function getHorizontalBorderChar() * @param string $verticalBorderChar * * @return $this + * + * @deprecated since Symfony 4.1, use {@link setVerticalBorderChars()} instead. */ public function setVerticalBorderChar($verticalBorderChar) { - $this->verticalBorderChar = $verticalBorderChar; + @trigger_error(sprintf('Method %s() is deprecated since Symfony 4.1, use setVerticalBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); - return $this; + return $this->setVerticalBorderChars($verticalBorderChar, $verticalBorderChar); } /** * Gets vertical border character. * * @return string + * + * @deprecated since Symfony 4.1, use {@link getBorderChars()} instead. */ public function getVerticalBorderChar() { - return $this->verticalBorderChar; + @trigger_error(sprintf('Method %s() is deprecated since Symfony 4.1, use getBorderChars() instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->verticalOutsideBorderChar; + } + + /** + * Gets border characters. + * + * @internal + */ + public function getBorderChars() + { + return array( + $this->horizontalOutsideBorderChar, + $this->verticalOutsideBorderChar, + $this->horizontalInsideBorderChar, + $this->verticalInsideBorderChar, + ); } /** @@ -122,26 +205,31 @@ public function getVerticalBorderChar() * * Example: * - * 1---------------2-----------------------2------------------3 - * | ISBN | Title | Author | - * 8---------------0-----------------------0------------------4 - * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | - * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | - * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | - * 7---------------6-----------------------6------------------5 + * 1═══════════════2══════════════════════════2══════════════════3 + * ║ ISBN │ Title │ Author ║ + * 8'══════════════0'═════════════════════════0'═════════════════4' + * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ + * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ + * 8───────────────0──────────────────────────0──────────────────4 + * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ + * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ + * 7═══════════════6══════════════════════════6══════════════════5 * * - * @param string $cross Crossing char (see #0 of example) - * @param string $topLeft Top left char (see #1 of example) - * @param string $topMid Top mid char (see #2 of example) - * @param string $topRight Top right char (see #3 of example) - * @param string $midRight Mid right char (see #4 of example) - * @param string $bottomRight Bottom right char (see #5 of example) - * @param string $bottomMid Bottom mid char (see #6 of example) - * @param string $bottomLeft Bottom left char (see #7 of example) - * @param string $midLeft Mid left char (see #8 of example) + * @param string $cross Crossing char (see #0 of example) + * @param string $topLeft Top left char (see #1 of example) + * @param string $topMid Top mid char (see #2 of example) + * @param string $topRight Top right char (see #3 of example) + * @param string $midRight Mid right char (see #4 of example) + * @param string $bottomRight Bottom right char (see #5 of example) + * @param string $bottomMid Bottom mid char (see #6 of example) + * @param string $bottomLeft Bottom left char (see #7 of example) + * @param string $midLeft Mid left char (see #8 of example) + * @param string|null $topLeftBottom Top left bottom char (see #8' of example), equals to $midLeft if null + * @param string|null $topMidBottom Top mid bottom char (see #0' of example), equals to $cross if null + * @param string|null $topRightBottom Top right bottom char (see #4' of example), equals to $midRight if null */ - public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft): self + public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft, string $topLeftBottom = null, string $topMidBottom = null, string $topRightBottom = null): self { $this->crossingChar = $cross; $this->crossingTopLeftChar = $topLeft; @@ -152,6 +240,9 @@ public function setCrossingChars(string $cross, string $topLeft, string $topMid, $this->crossingBottomMidChar = $bottomMid; $this->crossingBottomLeftChar = $bottomLeft; $this->crossingMidLeftChar = $midLeft; + $this->crossingTopLeftBottomChar = $topLeftBottom ?? $midLeft; + $this->crossingTopMidBottomChar = $topMidBottom ?? $cross; + $this->crossingTopRightBottomChar = $topRightBottom ?? $midRight; return $this; } @@ -209,6 +300,9 @@ public function getCrossingChars(): array $this->crossingBottomMidChar, $this->crossingBottomLeftChar, $this->crossingMidLeftChar, + $this->crossingTopLeftBottomChar, + $this->crossingTopMidBottomChar, + $this->crossingTopRightBottomChar, ); } diff --git a/src/Symfony/Component/Console/Tests/Helper/TableTest.php b/src/Symfony/Component/Console/Tests/Helper/TableTest.php index 2b46e8c63ec80..5d450e645bebe 100644 --- a/src/Symfony/Component/Console/Tests/Helper/TableTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/TableTest.php @@ -154,6 +154,29 @@ public function renderProvider() │ 80-902734-1-6 │ And Then There Were None │ Agatha Christie │ └───────────────┴──────────────────────────┴──────────────────┘ +TABLE + ), + array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + new TableSeparator(), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ), + 'box-double', + <<<'TABLE' +╔═══════════════╤══════════════════════════╤══════════════════╗ +║ ISBN │ Title │ Author ║ +╠═══════════════╪══════════════════════════╪══════════════════╣ +║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ +║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ +╟───────────────┼──────────────────────────┼──────────────────╢ +║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ +║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ +╚═══════════════╧══════════════════════════╧══════════════════╝ + TABLE ), array( @@ -628,8 +651,8 @@ public function testStyle() { $style = new TableStyle(); $style - ->setHorizontalBorderChar('.') - ->setVerticalBorderChar('.') + ->setHorizontalBorderChars('.') + ->setVerticalBorderChars('.') ->setDefaultCrossingChar('.') ;