diff --git a/lib/cli/Table.php b/lib/cli/Table.php index 1ed18fc..70fa2c2 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. @@ -40,11 +42,12 @@ 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()) { + 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 @@ -62,6 +65,8 @@ public function __construct(array $headers = array(), array $rows = array(), arr $this->setRows($rows); } + $this->setAlignments($alignments); + if (!empty($footers)) { $this->setFooters($footers); } @@ -79,6 +84,7 @@ public function resetTable() $this->_width = array(); $this->_rows = array(); $this->_footers = array(); + $this->_alignments = array(); return $this; } @@ -137,6 +143,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 +208,23 @@ 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) { + $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; + } /** * Add a row to the table. diff --git a/lib/cli/table/Ascii.php b/lib/cli/table/Ascii.php index 113c092..814b17d 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 ) { @@ -198,8 +203,10 @@ public function row( array $row ) { } private function padColumn($content, $column) { + $column_name = isset( $this->_headers[$column] ) ? $this->_headers[$column] : ''; + $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 ) ) . $this->_characters['padding']; + return $this->_characters['padding'] . Colors::pad( $content, $this->_widths[ $column ], $this->isPreColorized( $column ), false, $alignment) . $this->_characters['padding']; } /** 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 @@ +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; } /** 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 ) ); + } }