From ef577a47410fec9dc121f467e2f9a4b467ff32de Mon Sep 17 00:00:00 2001 From: Muhammet SAFAK Date: Sat, 27 Aug 2022 00:36:35 +0300 Subject: [PATCH] First commit --- .gitattributes | 1 + .gitignore | 5 + Examples/basic.php | 33 ++++++ Examples/styled.php | 39 ++++++ README.md | 111 ++++++++++++++++- composer.json | 23 ++++ src/Table.php | 283 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 493 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Examples/basic.php create mode 100644 Examples/styled.php create mode 100644 composer.json create mode 100644 src/Table.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f7e27da --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +/Examples/ export-ignore \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8593f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/.idea/ +/.vscode/ +/.vs/ +/vendor/ +/composer.lock diff --git a/Examples/basic.php b/Examples/basic.php new file mode 100644 index 0000000..f2a0f88 --- /dev/null +++ b/Examples/basic.php @@ -0,0 +1,33 @@ +#!usr/bin/php +row([ + 'id' => 1, + 'name' => 'Matthew S.', + 'surname' => 'Kramer', + 'email' => 'matthew@example.com', + 'status' => true, +]); + +$table->row([ + 'id' => 2, + 'name' => 'Millie J.', + 'surname' => 'Koenig', + 'email' => 'millie@example.com', + 'status' => false, +]); + +$table->row([ + 'id' => 3, + 'name' => 'Regina G.', + 'surname' => 'Hart', + 'email' => 'regina@example.com', + 'status' => true, +]); + +echo $table; diff --git a/Examples/styled.php b/Examples/styled.php new file mode 100644 index 0000000..1d2f53a --- /dev/null +++ b/Examples/styled.php @@ -0,0 +1,39 @@ +#!usr/bin/php +setBorderStyle(Table::COLOR_BLUE); +$table->setCellStyle(Table::COLOR_GREEN); +$table->setHeaderStyle(Table::COLOR_RED, Table::BOLD); + +$table->setColumnCellStyle('id', Table::ITALIC, Table::COLOR_LIGHT_YELLOW); +$table->setColumnCellStyle('email', Table::BOLD, Table::ITALIC); + +$table->row([ + 'id' => 1, + 'name' => 'Matthew S.', + 'surname' => 'Kramer', + 'email' => 'matthew@example.com', + 'status' => true, +]); + +$table->row([ + 'id' => 2, + 'name' => 'Millie J.', + 'surname' => 'Koenig', + 'email' => 'millie@example.com', + 'status' => false, +]); + +$table->row([ + 'id' => 3, + 'name' => 'Regina G.', + 'surname' => 'Hart', + 'email' => 'regina@example.com', + 'status' => true, +]); + +echo $table; diff --git a/README.md b/README.md index 63afec6..58432b0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,109 @@ -# CLITable -PHP CLI Table +# InitPHP CLI Table Generator + +This library allows you to create nice looking tables in the CLI interface with PHP. + +_**Note** : Not required, but the **MB_String** extension is highly recommended._ + +## Installation + +``` +composer require initphp/cli-table +``` + +or include `src/Table.php`. + +## Usage + +```php +row([ + 'id' => 1, + 'name' => 'Matthew S.', + 'surname' => 'Kramer', + 'email' => 'matthew@example.com', + 'status' => true, +]); + +$table->row([ + 'id' => 2, + 'name' => 'Millie J.', + 'surname' => 'Koenig', + 'email' => 'millie@example.com', + 'status' => false, +]); + +$table->row([ + 'id' => 3, + 'name' => 'Regina G.', + 'surname' => 'Hart', + 'email' => 'regina@example.com', + 'status' => true, +]); + +echo $table; +``` + +Output : + +![basic-cli-table](https://user-images.githubusercontent.com/104234499/186993361-3917979a-0a40-4e7b-84e8-4dd5f51c1bd1.jpg) + +### Styled + +```php +setBorderStyle(Table::COLOR_BLUE); +$table->setCellStyle(Table::COLOR_GREEN); +$table->setHeaderStyle(Table::COLOR_RED, Table::BOLD); + +$table->setColumnCellStyle('id', Table::ITALIC, Table::COLOR_LIGHT_YELLOW); +$table->setColumnCellStyle('email', Table::BOLD, Table::ITALIC); + +$table->row([ + 'id' => 1, + 'name' => 'Matthew S.', + 'surname' => 'Kramer', + 'email' => 'matthew@example.com', + 'status' => true, +]); + +$table->row([ + 'id' => 2, + 'name' => 'Millie J.', + 'surname' => 'Koenig', + 'email' => 'millie@example.com', + 'status' => false, +]); + +$table->row([ + 'id' => 3, + 'name' => 'Regina G.', + 'surname' => 'Hart', + 'email' => 'regina@example.com', + 'status' => true, +]); + +echo $table; +``` + +Output : + +![styled-cli-table](https://user-images.githubusercontent.com/104234499/186993365-82c0e55d-d572-45d2-a89a-5cf60c5c9fbe.jpg) + + +## Credits + +- [Muhammet ŞAFAK](https://github.com/muhammetsafak) <> + +## License + +Copyright © 2022 [MIT License](./LICENSE) diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..90a33f9 --- /dev/null +++ b/composer.json @@ -0,0 +1,23 @@ +{ + "name": "initphp/cli-table", + "description": "PHP CLI Table Generator", + "type": "library", + "license": "MIT", + "autoload": { + "psr-4": { + "InitPHP\\CLITable\\": "src/" + } + }, + "authors": [ + { + "name": "Muhammet ŞAFAK", + "email": "info@muhammetsafak.com.tr", + "role": "Developer", + "homepage": "https://www.muhammetsafak.com.tr" + } + ], + "minimum-stability": "stable", + "require": { + "php": ">=7.2" + } +} diff --git a/src/Table.php b/src/Table.php new file mode 100644 index 0000000..876d285 --- /dev/null +++ b/src/Table.php @@ -0,0 +1,283 @@ + + * @copyright Copyright © 2022 Muhammet ŞAFAK + * @license ./LICENSE MIT + * @version 1.0 + * @link https://www.muhammetsafak.com.tr + */ + +declare(strict_types=1); + +namespace InitPHP\CLITable; + +class Table +{ + + public const COLOR_DEFAULT = 39; + public const COLOR_BLACK = 30; + public const COLOR_RED = 31; + public const COLOR_GREEN = 32; + public const COLOR_YELLOW = 33; + public const COLOR_BLUE = 34; + public const COLOR_MAGENTA = 35; + public const COLOR_CYAN = 36; + public const COLOR_LIGHT_GRAY = 37; + public const COLOR_DARK_GRAY = 90; + public const COLOR_LIGHT_RED = 91; + public const COLOR_LIGHT_GREEN = 92; + public const COLOR_LIGHT_YELLOW = 93; + public const COLOR_LIGHT_BLUE = 94; + public const COLOR_LIGHT_MAGENTA= 95; + public const COLOR_LIGHT_CYAN = 96; + public const COLOR_WHITE = 97; + + public const BACKGROUND_BLACK = 40; + public const BACKGROUND_RED = 41; + public const BACKGROUND_GREEN = 42; + public const BACKGROUND_YELLOW = 43; + public const BACKGROUND_BLUE = 44; + public const BACKGROUND_MAGENTA = 45; + public const BACKGROUND_CYAN = 46; + + public const ITALIC = 3; + public const BOLD = 1; + public const UNDERLINE = 4; + public const STRIKETHROUGH = 9; + + private $headerStyle = [ + self::BOLD, + ]; + + private $cellStyle = []; + + private $borderStyle = []; + + private $columnCellStyle = []; + + private $chars = [ + 'top' => '═', + 'top-mid' => '╤', + 'top-left' => '╔', + 'top-right' => '╗', + 'bottom' => '═', + 'bottom-mid' => '╧', + 'bottom-left' => '╚', + 'bottom-right' => '╝', + 'left' => '║', + 'left-mid' => '╟', + 'mid' => '─', + 'mid-mid' => '┼', + 'right' => '║', + 'right-mid' => '╢', + 'middle' => '│ ', + ]; + + /** @var array */ + private $rows = []; + + public function __construct() + { + } + + public function __toString() + { + return $this->getContent(); + } + + public static function create(): Table + { + return new self(); + } + + public function setHeaderStyle(int ...$format): self + { + $styles = []; + foreach ($format as $style) { + $styles[] = $style; + } + $this->headerStyle = $styles; + return $this; + } + + public function setCellStyle(int ...$format): self + { + $styles = []; + foreach ($format as $style) { + $styles[] = $style; + } + $this->cellStyle = $styles; + return $this; + } + + public function setBorderStyle(int ...$format): self + { + $styles = []; + foreach ($format as $style) { + $styles[] = $style; + } + $this->borderStyle = $styles; + return $this; + } + + public function setColumnCellStyle(string $column, int ...$format): self + { + $styles = []; + foreach ($format as $style) { + $styles[] = $style; + } + $this->columnCellStyle[$column] = $styles; + return $this; + } + + public function row(array $assoc): self + { + $row = []; + foreach ($assoc as $key => $value) { + if(!\is_string($value)){ + if(\is_object($value)){ + $value = \get_class($value); + }elseif (\is_resource($value)) { + $value = '[RESOURCE]'; + }elseif (\is_callable($value)){ + $value = '[CALLABLE]'; + }elseif(\is_null($value)){ + $value = '[NULL]'; + }elseif(\is_bool($value)){ + $value = $value === FALSE ? '[FALSE]' : '[TRUE]'; + }else{ + $value = (string)$value; + } + } + $key = \trim((string)$key); + $row[$key] = \trim($value); + } + $this->rows[] = $row; + return $this; + } + + public function getContent(): string + { + $columnLengths = []; + $headerData = []; + + foreach ($this->rows as $row) { + $keys = \array_keys($row); + foreach ($keys as $key) { + if(isset($headerData[$key])){ + continue; + } + $headerData[$key] = $key; + $columnLengths[$key] = $this->strlen($key); + } + } + + foreach ($this->rows as $row) { + foreach ($headerData as $column) { + $len = \max($columnLengths[$column], $this->strlen($row[$column])); + if($len % 2 !== 0){ + ++$len; + } + $columnLengths[$column] = $len; + } + } + foreach ($columnLengths as &$length) { + $length += 4; + } + + $res = $this->getTableTopContent($columnLengths) + . $this->getFormattedRowContent($headerData, $columnLengths, "\e[" . \implode(';', $this->headerStyle) . "m", true) + . $this->getTableSeparatorContent($columnLengths); + foreach ($this->rows as $row) { + foreach ($headerData as $column) { + if(!isset($row[$column])){ + $row[$column] = '[NULL]'; + } + } + $res .= $this->getFormattedRowContent($row, $columnLengths, "\e[" . \implode(';', $this->cellStyle) . "m"); + } + return $res . $this->getTableBottomContent($columnLengths); + } + + private function getFormattedRowContent($data, $lengths, string $format = '', bool $isHeader = false): string + { + $res = $this->getChar('left') . ' '; + $rows = []; + foreach ($data as $key => $value) { + $customFormat = ''; + $value = ' ' . $value; + $len = $this->strlen($value) - $lengths[$key] + 1; + if($isHeader === FALSE && isset($this->columnCellStyle[$key]) && !empty($this->columnCellStyle[$key])){ + $customFormat = "\e[" . \implode(";", $this->columnCellStyle[$key]) . "m"; + } + $rows[] = ($format !== '' ? $format : '') + . ($customFormat !== '' ? $customFormat : '') + . $value + . ($format !== '' || $customFormat !== '' ? "\e[0m" : '') + . \str_repeat(' ', (int)\abs($len)); + } + $res .= \implode($this->getChar('middle'), $rows); + return $res . $this->getChar('right') . \PHP_EOL; + } + + private function getTableTopContent($lengths): string + { + $res = $this->getChar('top-left'); + $rows = []; + foreach ($lengths as $length) { + $rows[] = $this->getChar('top', $length); + } + $res .= \implode($this->getChar('top-mid'), $rows); + return $res . $this->getChar('top-right') . \PHP_EOL; + } + + private function getTableBottomContent($lengths): string + { + $res = $this->getChar('bottom-left'); + $rows = []; + foreach ($lengths as $length) { + $rows[] = $this->getChar('bottom', $length); + } + $res .= \implode($this->getChar('bottom-mid'), $rows); + return $res . $this->getChar('bottom-right') . \PHP_EOL; + } + + private function getTableSeparatorContent($lengths): string + { + $res = $this->getChar('left-mid'); + $rows = []; + foreach ($lengths as $length) { + $rows[] = $this->getChar('mid', $length); + } + $res .= \implode($this->getChar('mid-mid'), $rows); + return $res . $this->getChar('right-mid') . \PHP_EOL; + } + + private function getChar(string $char, int $len = 1): string + { + if(!isset($this->chars[$char])){ + return ''; + } + $res = (empty($this->borderStyle) ? '' : "\e[" . \implode(";", $this->borderStyle) . "m"); + if($len === 1){ + $res .= $this->chars[$char];; + }else{ + $res .= \str_repeat($this->chars[$char], $len); + } + $res .= empty($this->borderStyle) ? '' : "\e[0m"; + return $res; + } + + private function strlen(string $str): int + { + if(!\function_exists('mb_strlen')){ + return \strlen($str); + } + return \mb_strlen($str); + } + +}