From e3cbc1d54778b59195e5fd031300f57cf1f07f3d Mon Sep 17 00:00:00 2001 From: Rolf <62988563+rolf-yoast@users.noreply.github.com> Date: Fri, 19 Apr 2024 08:22:20 +0200 Subject: [PATCH 1/9] Introduce Column interface for the right alignment names --- lib/cli/table/Column.php | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 lib/cli/table/Column.php diff --git a/lib/cli/table/Column.php b/lib/cli/table/Column.php new file mode 100644 index 0000000..ba0ee36 --- /dev/null +++ b/lib/cli/table/Column.php @@ -0,0 +1,10 @@ + Date: Fri, 19 Apr 2024 08:23:04 +0200 Subject: [PATCH 2/9] Add the alignment functionality --- lib/cli/Table.php | 17 ++++++++++++++++- lib/cli/table/Ascii.php | 10 +++++++++- lib/cli/table/Renderer.php | 13 ++++++++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/cli/Table.php b/lib/cli/Table.php index 8b0cf93..89eb9ee 100644 --- a/lib/cli/Table.php +++ b/lib/cli/Table.php @@ -15,6 +15,7 @@ use cli\Shell; use cli\Streams; use cli\table\Ascii; +use cli\table\Column; use cli\table\Renderer; use cli\table\Tabular; @@ -27,6 +28,7 @@ class Table { protected $_footers = array(); protected $_width = array(); protected $_rows = array(); + protected $_alignments = array(); /** * Initializes the `Table` class. @@ -44,7 +46,7 @@ class Table { * @param array $rows The rows of data for this table. Optional. * @param array $footers Footers used in this table. Optional. */ - public function __construct(array $headers = null, array $rows = null, array $footers = null) { + public function __construct(array $headers = null, array $rows = null, array $footers = null, $alignments = []) { if (!empty($headers)) { // If all the rows is given in $headers we use the keys from the // first row for the header values @@ -62,6 +64,8 @@ public function __construct(array $headers = null, array $rows = null, array $fo $this->setRows($rows); } + $this->setAlignments($alignments); + if (!empty($footers)) { $this->setFooters($footers); } @@ -79,6 +83,7 @@ public function resetTable() $this->_width = array(); $this->_rows = array(); $this->_footers = array(); + $this->_alignments = array(); return $this; } @@ -137,6 +142,7 @@ public function display() { */ public function getDisplayLines() { $this->_renderer->setWidths($this->_width, $fallback = true); + $this->_renderer->setAlignments($this->_alignments); $border = $this->_renderer->border(); $out = array(); @@ -201,6 +207,15 @@ public function setFooters(array $footers) { $this->_footers = $this->checkRow($footers); } + /** + * Set the alignments of the table. + * + * @param array $alignments An array of strings containing column alignments. + */ + public function setAlignments(array $alignments) { + $this->_alignments = $alignments; + //TODO: Implement error if alignment is not valid and field is not valid + } /** * Add a row to the table. diff --git a/lib/cli/table/Ascii.php b/lib/cli/table/Ascii.php index 27e94e9..995562d 100644 --- a/lib/cli/table/Ascii.php +++ b/lib/cli/table/Ascii.php @@ -28,6 +28,7 @@ class Ascii extends Renderer { protected $_border = null; protected $_constraintWidth = null; protected $_pre_colorized = false; + protected $_headers = null; /** * Set the widths of each column in the table. @@ -131,6 +132,10 @@ public function border() { */ public function row( array $row ) { + if($this->_headers === null) { + $this->_headers = array_values($row); + } + $extra_row_count = 0; if ( count( $row ) > 0 ) { @@ -197,7 +202,10 @@ public function row( array $row ) { } private function padColumn($content, $column) { - return $this->_characters['padding'] . Colors::pad( $content, $this->_widths[ $column ], $this->isPreColorized( $column ) ) . $this->_characters['padding']; + $column_name = $this->_headers[$column]; + $alignment = array_key_exists($column_name, $this->_alignments) ? $this->_alignments[$column_name] : Column::ALIGN_LEFT; + + return $this->_characters['padding'] . Colors::pad( $content, $this->_widths[ $column ], $this->isPreColorized( $column ), false, $alignment) . $this->_characters['padding']; } /** diff --git a/lib/cli/table/Renderer.php b/lib/cli/table/Renderer.php index 3ac7be5..8cda719 100644 --- a/lib/cli/table/Renderer.php +++ b/lib/cli/table/Renderer.php @@ -17,9 +17,20 @@ */ abstract class Renderer { protected $_widths = array(); + protected $_alignments = array(); - public function __construct(array $widths = array()) { + public function __construct(array $widths = array(), array $alignments = array()) { $this->setWidths($widths); + $this->setAlignments($alignments); + } + + /** + * Set the alignments of each column in the table. + * + * @param array $alignments The alignments of the columns. + */ + public function setAlignments(array $alignments) { + $this->_alignments = $alignments; } /** From a9b10b788375d160d983fd0ee0fb0b40552abb3f Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 2 Nov 2025 15:21:57 +0100 Subject: [PATCH 3/9] Add validation Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/cli/Table.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/cli/Table.php b/lib/cli/Table.php index 5072fc7..8562053 100644 --- a/lib/cli/Table.php +++ b/lib/cli/Table.php @@ -213,8 +213,16 @@ public function setFooters(array $footers) { * @param array $alignments An array of strings containing column alignments. */ public function setAlignments(array $alignments) { + $validAlignments = array(Column::ALIGN_LEFT, Column::ALIGN_RIGHT, Column::ALIGN_CENTER); + foreach ($alignments as $column => $alignment) { + if (!in_array($alignment, $validAlignments, true)) { + throw new \InvalidArgumentException("Invalid alignment value '$alignment' for column '$column'."); + } + if (!in_array($column, $this->_headers, true)) { + throw new \InvalidArgumentException("Column '$column' does not exist in table headers."); + } + } $this->_alignments = $alignments; - //TODO: Implement error if alignment is not valid and field is not valid } /** From c0071e858315404bf572b9163a4da0f3590f5de0 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 2 Nov 2025 15:22:08 +0100 Subject: [PATCH 4/9] Add type declaration Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/cli/Table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/Table.php b/lib/cli/Table.php index 8562053..801bc7d 100644 --- a/lib/cli/Table.php +++ b/lib/cli/Table.php @@ -46,7 +46,7 @@ class Table { * @param array $rows The rows of data for this table. Optional. * @param array $footers Footers used in this table. Optional. */ - public function __construct(array $headers = array(), array $rows = array(), array $footers = array(), $alignments = array()) { + public function __construct(array $headers = array(), array $rows = array(), array $footers = array(), array $alignments = array()) { if (!empty($headers)) { // If all the rows is given in $headers we use the keys from the // first row for the header values From 9c1524b03ba410f87bcbd1aeac07f82119de15df Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 2 Nov 2025 15:22:20 +0100 Subject: [PATCH 5/9] Add missing space Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/cli/table/Ascii.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/table/Ascii.php b/lib/cli/table/Ascii.php index dfa0955..4f82ae3 100644 --- a/lib/cli/table/Ascii.php +++ b/lib/cli/table/Ascii.php @@ -132,7 +132,7 @@ public function border() { */ public function row( array $row ) { - if($this->_headers === null) { + if ($this->_headers === null) { $this->_headers = array_values($row); } From 0fc32ba564960f46fe7f41f7fc481c352cf9c734 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 2 Nov 2025 15:25:30 +0100 Subject: [PATCH 6/9] Update docblock --- lib/cli/Table.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/cli/Table.php b/lib/cli/Table.php index 801bc7d..70fa2c2 100644 --- a/lib/cli/Table.php +++ b/lib/cli/Table.php @@ -42,9 +42,10 @@ class Table { * table are used as the header values. * 3. Pass nothing and use `setHeaders()` and `addRow()` or `setRows()`. * - * @param array $headers Headers used in this table. Optional. - * @param array $rows The rows of data for this table. Optional. - * @param array $footers Footers used in this table. Optional. + * @param array $headers Headers used in this table. Optional. + * @param array $rows The rows of data for this table. Optional. + * @param array $footers Footers used in this table. Optional. + * @param array $alignments Column alignments. Optional. */ public function __construct(array $headers = array(), array $rows = array(), array $footers = array(), array $alignments = array()) { if (!empty($headers)) { From a3d6ddb98342c3dd45713577c6b4914cd97785eb Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 2 Nov 2025 15:26:40 +0100 Subject: [PATCH 7/9] Prevent potential undefined index error --- lib/cli/table/Ascii.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/table/Ascii.php b/lib/cli/table/Ascii.php index 4f82ae3..88b962e 100644 --- a/lib/cli/table/Ascii.php +++ b/lib/cli/table/Ascii.php @@ -203,7 +203,7 @@ public function row( array $row ) { } private function padColumn($content, $column) { - $column_name = $this->_headers[$column]; + $column_name = isset( $this->_headers[$column] ) ? $this->_headers[$column] : ''; $alignment = array_key_exists($column_name, $this->_alignments) ? $this->_alignments[$column_name] : Column::ALIGN_LEFT; $content = str_replace( "\t", ' ', (string) $content ); return $this->_characters['padding'] . Colors::pad( $content, $this->_widths[ $column ], $this->isPreColorized( $column ), false, $alignment) . $this->_characters['padding']; From 95d08e8e46286ddea2d36d066791fcc75c3524a4 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 2 Nov 2025 17:08:03 +0100 Subject: [PATCH 8/9] Add some tests --- tests/Test_Table.php | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/Test_Table.php b/tests/Test_Table.php index 26db650..b59ce08 100644 --- a/tests/Test_Table.php +++ b/tests/Test_Table.php @@ -289,4 +289,70 @@ public function test_null_values_are_handled() { ]; $this->assertSame( $expected, $out, 'Null values should be safely converted to empty strings in table output.' ); } + + public function test_default_alignment() { + $table = new cli\Table(); + $table->setHeaders( array( 'Header1', 'Header2' ) ); + $table->addRow( array( 'Row1Col1', 'Row1Col2' ) ); + + $out = $table->getDisplayLines(); + + // By default, columns should be left-aligned. + $this->assertStringContainsString( '| Header1 | Header2 |', $out[1] ); + $this->assertStringContainsString( '| Row1Col1 | Row1Col2 |', $out[3] ); + } + + public function test_right_alignment() { + $table = new cli\Table(); + $table->setHeaders( array( 'Header1', 'Header2' ) ); + $table->setAlignments( array( 'Header1' => \cli\table\Column::ALIGN_RIGHT, 'Header2' => \cli\table\Column::ALIGN_RIGHT ) ); + $table->addRow( array( 'R1C1', 'R1C2' ) ); + + $out = $table->getDisplayLines(); + + $this->assertStringContainsString( '| Header1 | Header2 |', $out[1] ); + $this->assertStringContainsString( '| R1C1 | R1C2 |', $out[3] ); + } + + public function test_center_alignment() { + $table = new cli\Table(); + $table->setHeaders( array( 'Header1', 'Header2' ) ); + $table->setAlignments( array( 'Header1' => \cli\table\Column::ALIGN_CENTER, 'Header2' => \cli\table\Column::ALIGN_CENTER ) ); + $table->addRow( array( 'R1C1', 'R1C2' ) ); + + $out = $table->getDisplayLines(); + + $this->assertStringContainsString( '| Header1 | Header2 |', $out[1] ); + $this->assertStringContainsString( '| R1C1 | R1C2 |', $out[3] ); + } + + public function test_mixed_alignments() { + $table = new cli\Table(); + $table->setHeaders( array( 'Left', 'Right', 'Center' ) ); + $table->setAlignments( array( + 'Left' => \cli\table\Column::ALIGN_LEFT, + 'Right' => \cli\table\Column::ALIGN_RIGHT, + 'Center' => \cli\table\Column::ALIGN_CENTER, + ) ); + $table->addRow( array( 'l', 'r', 'c' ) ); + + $out = $table->getDisplayLines(); + + $this->assertStringContainsString( '| Left | Right | Center |', $out[1] ); + $this->assertStringContainsString( '| l | r | c |', $out[3] ); + } + + public function test_invalid_alignment_value() { + $this->expectException( \InvalidArgumentException::class ); + $table = new cli\Table(); + $table->setHeaders( array( 'Header1' ) ); + $table->setAlignments( array( 'Header1' => 'invalid-alignment' ) ); + } + + public function test_invalid_alignment_column() { + $this->expectException( \InvalidArgumentException::class ); + $table = new cli\Table(); + $table->setHeaders( array( 'Header1' ) ); + $table->setAlignments( array( 'NonExistent' => \cli\table\Column::ALIGN_LEFT ) ); + } } From be3ff099fc8ab915469d4a9e60286d54fe8fd753 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 2 Nov 2025 17:48:14 +0100 Subject: [PATCH 9/9] Update lib/cli/table/Ascii.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/cli/table/Ascii.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/table/Ascii.php b/lib/cli/table/Ascii.php index 88b962e..814b17d 100644 --- a/lib/cli/table/Ascii.php +++ b/lib/cli/table/Ascii.php @@ -204,7 +204,7 @@ public function row( array $row ) { private function padColumn($content, $column) { $column_name = isset( $this->_headers[$column] ) ? $this->_headers[$column] : ''; - $alignment = array_key_exists($column_name, $this->_alignments) ? $this->_alignments[$column_name] : Column::ALIGN_LEFT; + $alignment = ($column_name !== '' && array_key_exists($column_name, $this->_alignments)) ? $this->_alignments[$column_name] : Column::ALIGN_LEFT; $content = str_replace( "\t", ' ', (string) $content ); return $this->_characters['padding'] . Colors::pad( $content, $this->_widths[ $column ], $this->isPreColorized( $column ), false, $alignment) . $this->_characters['padding']; }