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('.')
;