Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions examples/table-alignment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/env php
<?php
/**
* Table Alignment Examples
*
* This example demonstrates the table column alignment feature.
* You can align columns to the left, right, or center.
*/

if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
} elseif (file_exists(__DIR__ . '/../../../autoload.php')) {
require_once __DIR__ . '/../../../autoload.php';
} else {
throw new Exception('Unable to locate autoloader; please run "composer install"');
}

cli\line();
cli\line('%G===%n %CTable Column Alignment Examples%n %G===%n');
cli\line();

// Example 1: Default (Left) Alignment
cli\line('%Y## Example 1: Default Left Alignment%n');
cli\line();
$table = new cli\Table();
$table->setHeaders(['Product', 'Price', 'Stock']);
$table->addRow(['Widget', '$19.99', '150']);
$table->addRow(['Gadget', '$29.99', '75']);
$table->addRow(['Tool', '$9.99', '200']);
$table->display();
cli\line();

// Example 2: Right Alignment for Numeric Columns
cli\line('%Y## Example 2: Right Alignment for Numeric Columns%n');
cli\line('Notice how the numeric values are much easier to compare when right-aligned.');
cli\line();
$table = new cli\Table();
$table->setHeaders(['Product', 'Price', 'Stock']);
$table->setAlignments([
'Product' => cli\table\Column::ALIGN_LEFT,
'Price' => cli\table\Column::ALIGN_RIGHT,
'Stock' => cli\table\Column::ALIGN_RIGHT,
]);
$table->addRow(['Widget', '$19.99', '150']);
$table->addRow(['Gadget', '$29.99', '75']);
$table->addRow(['Tool', '$9.99', '200']);
$table->display();
cli\line();

// Example 3: Center Alignment
cli\line('%Y## Example 3: Center Alignment%n');
cli\line();
$table = new cli\Table();
$table->setHeaders(['Left', 'Center', 'Right']);
$table->setAlignments([
'Left' => cli\table\Column::ALIGN_LEFT,
'Center' => cli\table\Column::ALIGN_CENTER,
'Right' => cli\table\Column::ALIGN_RIGHT,
]);
$table->addRow(['Text', 'Centered', 'More']);
$table->addRow(['Data', 'Values', 'Here']);
$table->addRow(['A', 'B', 'C']);
$table->display();
cli\line();

// Example 4: Real-world Database Table Sizes
cli\line('%Y## Example 4: Database Table Sizes (Real-world Use Case)%n');
cli\line('This example shows how the alignment feature makes database');
cli\line('statistics much more readable and easier to compare.');
cli\line();
$table = new cli\Table();
$table->setHeaders(['Table Name', 'Rows', 'Data Size', 'Index Size']);
$table->setAlignments([
'Table Name' => cli\table\Column::ALIGN_LEFT,
'Rows' => cli\table\Column::ALIGN_RIGHT,
'Data Size' => cli\table\Column::ALIGN_RIGHT,
'Index Size' => cli\table\Column::ALIGN_RIGHT,
]);
$table->addRow(['wp_posts', '1,234', '5.2 MB', '1.1 MB']);
$table->addRow(['wp_postmeta', '45,678', '23.4 MB', '8.7 MB']);
$table->addRow(['wp_comments', '9,012', '2.3 MB', '0.8 MB']);
$table->addRow(['wp_options', '456', '1.5 MB', '0.2 MB']);
$table->addRow(['wp_users', '89', '0.1 MB', '0.05 MB']);
$table->display();
cli\line();

// Example 5: Alignment Constants
cli\line('%Y## Alignment Constants%n');
cli\line();
cli\line('You can use the following constants from %Ccli\table\Column%n:');
cli\line(' %G*%n %CALIGN_LEFT%n - Left align (default)');
cli\line(' %G*%n %CALIGN_RIGHT%n - Right align (good for numbers)');
cli\line(' %G*%n %CALIGN_CENTER%n - Center align');
cli\line();
cli\line('Example usage:');
cli\line(' %c$table->setAlignments([%n');
cli\line(' %c\'Column1\' => cli\table\Column::ALIGN_LEFT,%n');
cli\line(' %c\'Column2\' => cli\table\Column::ALIGN_RIGHT,%n');
cli\line(' %c]);%n');
cli\line();

cli\line('%GDone!%n');
cli\line();
48 changes: 44 additions & 4 deletions lib/cli/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -27,6 +28,14 @@ class Table {
protected $_footers = array();
protected $_width = array();
protected $_rows = array();
protected $_alignments = array();

/**
* Cached map of valid alignment constants.
*
* @var array|null
*/
private static $_valid_alignments_map = null;

/**
* Initializes the `Table` class.
Expand All @@ -40,11 +49,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
Expand All @@ -66,6 +76,10 @@ public function __construct(array $headers = array(), array $rows = array(), arr
$this->setFooters($footers);
}

if (!empty($alignments)) {
$this->setAlignments($alignments);
}

if (Shell::isPiped()) {
$this->setRenderer(new Tabular());
} else {
Expand All @@ -79,6 +93,7 @@ public function resetTable()
$this->_width = array();
$this->_rows = array();
$this->_footers = array();
$this->_alignments = array();
return $this;
}

Expand Down Expand Up @@ -175,6 +190,8 @@ public function displayRow(array $row) {
*/
public function getDisplayLines() {
$this->_renderer->setWidths($this->_width, $fallback = true);
$this->_renderer->setHeaders($this->_headers);
$this->_renderer->setAlignments($this->_alignments);
$border = $this->_renderer->border();

$out = array();
Expand Down Expand Up @@ -240,6 +257,29 @@ public function setFooters(array $footers) {
$this->_footers = $this->checkRow($footers);
}

/**
* Set the alignments of the table.
*
* @param array $alignments An array of alignment constants keyed by column name.
*/
public function setAlignments(array $alignments) {
// Initialize the cached valid alignments map on first use
if ( null === self::$_valid_alignments_map ) {
self::$_valid_alignments_map = array_flip( array( Column::ALIGN_LEFT, Column::ALIGN_RIGHT, Column::ALIGN_CENTER ) );
}

$headers_map = ! empty( $this->_headers ) ? array_flip( $this->_headers ) : null;
foreach ( $alignments as $column => $alignment ) {
if ( ! isset( self::$_valid_alignments_map[ $alignment ] ) ) {
throw new \InvalidArgumentException( "Invalid alignment value '$alignment' for column '$column'." );
}
// Only validate column names if headers are already set
if ( $headers_map !== null && ! isset( $headers_map[ $column ] ) ) {
throw new \InvalidArgumentException( "Column '$column' does not exist in table headers." );
}
}
$this->_alignments = $alignments;
}

/**
* Add a row to the table.
Expand Down
17 changes: 16 additions & 1 deletion lib/cli/table/Ascii.php
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,24 @@ public function row( array $row ) {
return $ret;
}

/**
* Get the alignment for a column.
*
* @param int $column Column index.
* @return int Alignment constant (STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH).
*/
private function getColumnAlignment( $column ) {
$column_name = isset( $this->_headers[ $column ] ) ? $this->_headers[ $column ] : '';
if ( $column_name !== '' && array_key_exists( $column_name, $this->_alignments ) ) {
return $this->_alignments[ $column_name ];
}
return Column::ALIGN_LEFT;
}

private function padColumn($content, $column) {
$alignment = $this->getColumnAlignment( $column );
$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'];
}

/**
Expand Down
22 changes: 22 additions & 0 deletions lib/cli/table/Column.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
/**
* PHP Command Line Tools
*
* This source file is subject to the MIT license that is bundled
* with this package in the file LICENSE.
*
* @author James Logsdon <dwarf@girsbrain.org>
* @copyright 2010 James Logsdom (http://girsbrain.org)
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/

namespace cli\table;

/**
* Column alignment constants for table rendering.
*/
interface Column {
const ALIGN_LEFT = STR_PAD_RIGHT;
const ALIGN_RIGHT = STR_PAD_LEFT;
const ALIGN_CENTER = STR_PAD_BOTH;
}
23 changes: 22 additions & 1 deletion lib/cli/table/Renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,30 @@
*/
abstract class Renderer {
protected $_widths = array();
protected $_alignments = array();
protected $_headers = 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;
}

/**
* Set the headers of the table.
*
* @param array $headers The headers of the table.
*/
public function setHeaders(array $headers) {
$this->_headers = $headers;
}

/**
Expand Down
Loading