├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .php-cs-fixer.php ├── README.md ├── composer.json ├── docs ├── _config.yml └── index.md ├── img.png ├── phpunit.xml ├── src ├── ArrayToTextTable.php ├── ArrayToTextTableException.php └── formatters │ ├── AlignFormatter.php │ ├── BaseColumnFormatter.php │ ├── ColorFormatter.php │ └── SprintfFormatter.php ├── test.php └── tests ├── AlignFormatterTest.php ├── ColorTest.php ├── CombinedAlignSprintfFormatterTest.php └── SimpleTest.php /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'src/**' 7 | 8 | env: 9 | COMPOSER_ROOT_VERSION: 1.99 10 | 11 | jobs: 12 | tests: 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | php-version: [5.6, 8.2] 18 | 19 | dependencies: 20 | - "highest" 21 | 22 | name: P${{ matrix.php-version }} - ${{ matrix.dependencies }} 23 | 24 | steps: 25 | - name: Checkout project 26 | uses: actions/checkout@v3 27 | 28 | - name: Install PHP 29 | uses: shivammathur/setup-php@2.24.0 30 | with: 31 | php-version: "${{ matrix.php-version }}" 32 | extensions: xdebug 33 | ini-values: zend.assertions=1 34 | 35 | - name: Get composer cache directory 36 | id: composercache 37 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT 38 | 39 | - name: Cache dependencies 40 | uses: actions/cache@v3 41 | with: 42 | path: ${{ steps.composercache.outputs.dir }} 43 | key: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-${{ hashFiles('**/composer.json') }} 44 | restore-keys: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer- 45 | 46 | - name: Install lowest dependencies 47 | if: ${{ matrix.dependencies == 'lowest' }} 48 | run: composer update --no-interaction --no-progress --prefer-lowest 49 | 50 | - name: Install highest dependencies 51 | if: ${{ matrix.dependencies == 'highest' }} 52 | run: composer update --no-interaction --no-progress 53 | 54 | - name: Run tests 55 | timeout-minutes: 3 56 | run: vendor/bin/phpunit --coverage-clover clover.xml 57 | 58 | - name: Get lines of code (more sophisticated) 59 | id: scc 60 | uses: iryanbell/scc-docker-action@v1.0.2 61 | with: 62 | args: ${{ env.workspace }} -i php --exclude-dir vendor --format json src 63 | 64 | - name: Make lines of code badge 65 | uses: emibcn/badge-action@v2.0.2 66 | with: 67 | label: Lines of Code 68 | status: ${{ fromJson(steps.scc.outputs.scc)[0].Code }} 69 | color: 'blue' 70 | path: .github/lines.svg 71 | 72 | - name: Make code coverage badge 73 | uses: timkrase/phpunit-coverage-badge@v1.2.1 74 | with: 75 | coverage_badge_path: .github/coverage.svg 76 | # push badge later on 77 | push_badge: false 78 | 79 | - name: Git push badges to image-data branch 80 | uses: peaceiris/actions-gh-pages@v3 81 | with: 82 | publish_dir: .github 83 | publish_branch: image-data 84 | github_token: ${{ secrets.GITHUB_TOKEN }} 85 | user_name: 'github-actions[bot]' 86 | user_email: 'github-actions[bot]@users.noreply.github.com' 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | phpunit.phar 3 | phpunit 4 | .phpunit.result.cache 5 | coverage/ 6 | .idea 7 | vendor 8 | .php-cs-fixer.cache 9 | -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | exclude('vendor/') 5 | ->in('.'); 6 | 7 | $fixer = (new PhpCsFixer\Config('', '')) 8 | ->setRules([ 9 | '@PhpCsFixer' => true, 10 | 'concat_space' => [ 11 | 'spacing' => 'one', 12 | ], 13 | 'global_namespace_import' => [ 14 | 'import_classes' => false, 15 | 'import_constants' => false, 16 | 'import_functions' => true, 17 | ], 18 | 'no_superfluous_phpdoc_tags' => false, 19 | 'phpdoc_add_missing_param_annotation' => true, 20 | 'phpdoc_no_empty_return' => false, 21 | 'phpdoc_summary' => false, 22 | 'phpdoc_trim' => true, 23 | 'phpdoc_trim_consecutive_blank_line_separation' => true, 24 | 'yoda_style' => false, 25 | ]) 26 | ->setFinder($finder); 27 | 28 | if (isset($GLOBALS['argv']) && in_array('--allow-risky=yes', $GLOBALS['argv'], true)) { 29 | echo 'Risky rules enabled' . PHP_EOL; 30 | 31 | $fixer 32 | ->setRules([ 33 | '@PhpCsFixer:risky' => true, 34 | ]); 35 | } 36 | 37 | return $fixer; 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Array To Text Table 2 | 3 | [![packagist](https://poser.pugx.org/dekor/php-array-table/v)](https://packagist.org/packages/dekor/php-array-table) 4 | [![downloads](https://poser.pugx.org/dekor/php-array-table/downloads)](https://packagist.org/packages/dekor/php-array-table) 5 | [![min php version](https://poser.pugx.org/dekor/php-array-table/require/php)](https://packagist.org/packages/dekor/php-array-table) 6 | [![license](https://poser.pugx.org/dekor/php-array-table/license)](https://packagist.org/packages/dekor/php-array-table) 7 | [![tests](https://github.com/deniskoronets/php-array-table/actions/workflows/tests.yml/badge.svg)](https://github.com/deniskoronets/php-array-table/actions/workflows/tests.yml) 8 | ![code coverage badge](https://raw.githubusercontent.com/deniskoronets/php-array-table/image-data/coverage.svg) 9 | ![lines of code](https://raw.githubusercontent.com/deniskoronets/php-array-table/image-data/lines.svg) 10 | 11 | PHP-class, which allows to transform php associative arrays to cool ASCII tables. 12 | 13 | ![Blue](https://placehold.co/15x15/005BBB/005BBB.png)![Yellow](https://placehold.co/15x15/FFD500/FFD500.png) [Ukraine ❤](https://woo.zp.ua/en/support-ukraine/) 14 | 15 | # Quick doc: 16 | https://deniskoronets.github.io/php-array-table/ 17 | 18 | ## Installation 19 | Simply run composer require: 20 |
composer require dekor/php-array-table
21 | 22 | or add to composer.json: 23 |
"dekor/php-array-table": "^2.0"
24 | 25 | ## Usage 26 | 27 | ```php 28 | use dekor\ArrayToTextTable; 29 | 30 | $data = [ 31 | [ 32 | 'id' => 1, 33 | 'name' => 'Denis Koronets', 34 | 'role' => 'php developer', 35 | ], 36 | [ 37 | 'id' => 2, 38 | 'name' => 'Maxim Ambroskin', 39 | 'role' => 'java developer', 40 | ], 41 | [ 42 | 'id' => 3, 43 | 'name' => 'Andrew Sikorsky', 44 | 'role' => 'php developer', 45 | ] 46 | ]; 47 | 48 | echo (new ArrayToTextTable($data))->render(); 49 | ``` 50 | 51 | Will draw the next output: 52 | 53 |
 54 | +----+-----------------+----------------+
 55 | | id | name            | role           |
 56 | +----+-----------------+----------------+
 57 | | 1  | Denis Koronets  | php developer  |
 58 | | 2  | Maxim Ambroskin | java developer |
 59 | | 3  | Andrew Sikorsky | php developer  |
 60 | +----+-----------------+----------------+
 61 | 
62 | 63 | ## Formatters (since v2) 64 | 65 | Version 2 introduces a new feature that allows to pre and postprocess column data by applying filters. 66 | 67 | You're able to develop your own formatters by extending `BaseColumnFormatter` and implementing abstract methods. 68 | 69 | List of formatters out of the box: 70 | - `AlignFormatter` - allows to set text align for inner column (useful for numbers): 71 | 72 | ```php 73 | use dekor\ArrayToTextTable; 74 | use dekor\formatters\AlignFormatter; 75 | 76 | $data = [ 77 | [ 78 | 'left' => 2, 79 | 'center' => 'Dummy one', 80 | 'right' => 14.33, 81 | ], 82 | [ 83 | 'left' => 3, 84 | 'center' => 'Another great day for a great inventers!', 85 | 'right' => 1, 86 | ], 87 | ]; 88 | 89 | $builder = new ArrayToTextTable($data); 90 | $builder->applyFormatter(new AlignFormatter(['center' => 'center', 'right' => 'right'])); 91 | 92 | echo $builder->render(); 93 | ``` 94 | 95 | outputs: 96 |
 97 | +------+------------------------------------------+-------+
 98 | | left | center                                   | right |
 99 | +------+------------------------------------------+-------+
100 | | 2    |                Dummy one                 | 14.33 |
101 | | 3    | Another great day for a great inventers! |     1 |
102 | +------+------------------------------------------+-------+
103 | 
104 | 105 | - `SprintfFormatter` - allows to format column value before rendering using sprintf function (ex: %01.3f) 106 | ```php 107 | use dekor\ArrayToTextTable; 108 | use dekor\formatters\SprintfFormatter; 109 | 110 | $data = [ 111 | [ 112 | 'left' => 1, 113 | 'right' => 2.89, 114 | ] 115 | ]; 116 | 117 | $builder = new ArrayToTextTable($data); 118 | $builder->applyFormatter(new SprintfFormatter(['left' => '%01.3f', 'right' => '%03.3f'])); 119 | 120 | echo $builder->render(); 121 | ``` 122 | 123 | outputs: 124 |
125 | +-------+-------+
126 | | left  | right |
127 | +-------+-------+
128 | | 1.000 | 2.890 |
129 | +-------+-------+
130 | 
131 | 132 | - `ColorFormatter` - allows to highlight text with specific color (only works in terminal): 133 | ```php 134 | use dekor\ArrayToTextTable; 135 | use dekor\formatters\ColorFormatter; 136 | 137 | $data = [ 138 | ['test' => 1], 139 | ['test' => -1], 140 | ]; 141 | 142 | $builder = new ArrayToTextTable($data); 143 | $builder->applyFormatter(new ColorFormatter(['test' => fn ($value) => $value > 0 ? 'Red' : 'Green'])); 144 | 145 | echo $builder->render() . PHP_EOL; 146 | ``` 147 | 148 | outputs: 149 | 150 | ![img.png](img.png) 151 | 152 | Allowed colors list (see `ColorFormatter::$colors`) 153 | - Black 154 | - Dark Grey 155 | - Red 156 | - Light Red 157 | - Green 158 | - Light Green 159 | - Brown 160 | - Yellow 161 | - Blue 162 | - Light Blue 163 | - Magenta 164 | - Light Magenta 165 | - Cyan 166 | - Light Cyan 167 | - Light Grey 168 | - White 169 | 170 | ## Our sponsors list: 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dekor/php-array-table", 3 | "description": "PHP Library for printing associative arrays as text table (similar to mysql terminal console)", 4 | "keywords": [ 5 | "php", 6 | "library" 7 | ], 8 | "license": "BSD-3-Clause", 9 | "authors": [ 10 | { 11 | "name": "Denis Koronets", 12 | "email": "deniskoronets@woo.zp.ua", 13 | "homepage": "https://woo.zp.ua/" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.6.0", 18 | "ext-mbstring": "*" 19 | }, 20 | "require-dev": { 21 | "phpunit/phpunit": "^5|^6|^7|^8|^9|^10", 22 | "friendsofphp/php-cs-fixer": "^2|^3" 23 | }, 24 | "autoload": { 25 | "psr-4" : { 26 | "dekor\\" : "src" 27 | } 28 | }, 29 | "scripts": { 30 | "test": "phpunit --coverage-html=coverage", 31 | "fix": "php-cs-fixer fix --verbose", 32 | "fix-risky": "php-cs-fixer fix --allow-risky=yes --verbose" 33 | } 34 | } -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # PHP Array To Text Table 2 | PHP-class, which allows to transform php associative arrays to cool ASCII tables. 3 | 4 | ![Blue](https://placehold.co/15x15/005BBB/005BBB.png)![Yellow](https://placehold.co/15x15/FFD500/FFD500.png) [Ukraine ❤](https://woo.zp.ua/en/support-ukraine/) 5 | 6 | ## Installation 7 | Simply run composer require: 8 |
composer require dekor/php-array-table
9 | 10 | or add to composer.json: 11 |
"dekor/php-array-table": "^2.0"
12 | 13 | or try out using it in a sandbox: 14 | https://play.phpsandbox.io/dekor/php-array-table/6WdN1pkx8NK43yOV 15 | 16 | ## Usage 17 | 18 | ```php 19 | use dekor\ArrayToTextTable; 20 | 21 | $data = [ 22 | [ 23 | 'id' => 1, 24 | 'name' => 'Denis Koronets', 25 | 'role' => 'php developer', 26 | ], 27 | [ 28 | 'id' => 2, 29 | 'name' => 'Maxim Ambroskin', 30 | 'role' => 'java developer', 31 | ], 32 | [ 33 | 'id' => 3, 34 | 'name' => 'Andrew Sikorsky', 35 | 'role' => 'php developer', 36 | ] 37 | ]; 38 | 39 | echo (new ArrayToTextTable($data))->render(); 40 | ``` 41 | 42 | Will draw the next output: 43 | 44 |
 45 | +----+-----------------+----------------+
 46 | | id | name            | role           |
 47 | +----+-----------------+----------------+
 48 | | 1  | Denis Koronets  | php developer  |
 49 | | 2  | Maxim Ambroskin | java developer |
 50 | | 3  | Andrew Sikorsky | php developer  |
 51 | +----+-----------------+----------------+
 52 | 
53 | 54 | ## Formatters (since v2) 55 | 56 | Version 2 introduces a new feature that allows to pre and postprocess column data by applying filters. 57 | 58 | You're able to develop your own formatters by extending `BaseColumnFormatter` and implementing abstract methods. 59 | 60 | List of formatters out of the box: 61 | - `AlignFormatter` - allows to set text align for inner column (useful for numbers): 62 | 63 | ```php 64 | use dekor\ArrayToTextTable; 65 | use dekor\formatters\AlignFormatter; 66 | 67 | $data = [ 68 | [ 69 | 'left' => 2, 70 | 'center' => 'Dummy one', 71 | 'right' => 14.33, 72 | ], 73 | [ 74 | 'left' => 3, 75 | 'center' => 'Another great day for a great inventers!', 76 | 'right' => 1, 77 | ], 78 | ]; 79 | 80 | $builder = new ArrayToTextTable($data); 81 | $builder->applyFormatter(new AlignFormatter(['center' => 'center', 'right' => 'right'])); 82 | 83 | echo $builder->render(); 84 | ``` 85 | 86 | outputs: 87 |
 88 | +------+------------------------------------------+-------+
 89 | | left | center                                   | right |
 90 | +------+------------------------------------------+-------+
 91 | | 2    |                Dummy one                 | 14.33 |
 92 | | 3    | Another great day for a great inventers! |     1 |
 93 | +------+------------------------------------------+-------+
 94 | 
95 | 96 | - `SprintfFormatter` - allows to format column value before rendering using sprintf function (ex: %01.3f) 97 | ```php 98 | use dekor\ArrayToTextTable; 99 | use dekor\formatters\SprintfFormatter; 100 | 101 | $data = [ 102 | [ 103 | 'left' => 1, 104 | 'right' => 2.89, 105 | ] 106 | ]; 107 | 108 | $builder = new ArrayToTextTable($data); 109 | $builder->applyFormatter(new SprintfFormatter(['left' => '%01.3f', 'right' => '%03.3f'])); 110 | 111 | echo $builder->render(); 112 | ``` 113 | 114 | outputs: 115 |
116 | +-------+-------+
117 | | left  | right |
118 | +-------+-------+
119 | | 1.000 | 2.890 |
120 | +-------+-------+
121 | 
122 | 123 | - `ColorFormatter` - allows to highlight text with specific color (only works in terminal): 124 | ```php 125 | use dekor\ArrayToTextTable; 126 | use dekor\formatters\ColorFormatter; 127 | 128 | $data = [ 129 | ['test' => 1], 130 | ['test' => -1], 131 | ]; 132 | 133 | $builder = new ArrayToTextTable($data); 134 | $builder->applyFormatter(new ColorFormatter(['test' => fn ($value) => $value > 0 ? 'Red' : 'Green'])); 135 | 136 | echo $builder->render() . PHP_EOL; 137 | ``` 138 | 139 | outputs: 140 | 141 | ![img.png](img.png) 142 | 143 | Allowed colors list (see `ColorFormatter::$colors`) 144 | - Black 145 | - Dark Grey 146 | - Red 147 | - Light Red 148 | - Green 149 | - Light Green 150 | - Brown 151 | - Yellow 152 | - Blue 153 | - Light Blue 154 | - Magenta 155 | - Light Magenta 156 | - Cyan 157 | - Light Cyan 158 | - Light Grey 159 | - White 160 | 161 | ## Our sponsors list: 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deniskoronets/php-array-table/1f8ac1fbdfe5ff49b28d50d29f31244bcb36ac72/img.png -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | ./tests/ 18 | 19 | 20 | 21 | 22 | ./src 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/ArrayToTextTable.php: -------------------------------------------------------------------------------- 1 | data = $data; 56 | } 57 | 58 | public function applyFormatter(BaseColumnFormatter $formatter) 59 | { 60 | $this->columnFormatters[] = $formatter; 61 | 62 | return $this; 63 | } 64 | 65 | /** 66 | * Set custom charset for columns values 67 | * 68 | * @param $charset 69 | * 70 | * @return \dekor\ArrayToTextTable 71 | * 72 | * @throws \Exception 73 | */ 74 | public function charset($charset) 75 | { 76 | if (!in_array($charset, mb_list_encodings())) { 77 | throw new \Exception( 78 | 'This charset `' . $charset . '` is not supported by mbstring.' . 79 | 'Please check it: http://php.net/manual/ru/function.mb-list-encodings.php' 80 | ); 81 | } 82 | 83 | $this->charset = $charset; 84 | 85 | return $this; 86 | } 87 | 88 | /** 89 | * Set mode to print no header in the table 90 | * 91 | * @return self 92 | */ 93 | public function noHeader() 94 | { 95 | $this->renderHeader = false; 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * Build your ascii table and return the result 102 | * 103 | * @return string 104 | */ 105 | public function render() 106 | { 107 | if (empty($this->data)) { 108 | return 'Empty'; 109 | } 110 | 111 | $this->validateData(); 112 | 113 | $this->applyBeforeFormatters(); 114 | $this->calcColumnsList(); 115 | $this->calcColumnsLength(); 116 | 117 | // render section 118 | $this->renderHeader(); 119 | $this->renderBody(); 120 | $this->lineSeparator(); 121 | // end render section 122 | 123 | return str_replace( 124 | ['++', '||'], 125 | ['+', '|'], 126 | implode(PHP_EOL, $this->result) 127 | ); 128 | } 129 | 130 | protected function validateData() 131 | { 132 | foreach ($this->data as &$row) { 133 | foreach ($row as &$column) { 134 | if (!is_scalar($column)) { 135 | if (is_null($column)) { 136 | $column = 'NULL'; 137 | continue; 138 | } elseif (is_object($column) && method_exists($column, '__toString')) { 139 | continue; 140 | } 141 | 142 | throw new ArrayToTextTableException( 143 | 'Tried to render invalid data: ' . var_export($column, 1) . '. Only scalars allowed' 144 | ); 145 | } 146 | } 147 | } 148 | } 149 | 150 | /** 151 | * Apply formatters to data before calculating length 152 | * 153 | * @return void 154 | */ 155 | protected function applyBeforeFormatters() 156 | { 157 | foreach ($this->data as $key => $row) { 158 | foreach ($row as $columnKey => $value) { 159 | foreach ($this->columnFormatters as $formatter) { 160 | $this->data[$key][$columnKey] = $formatter->process($columnKey, $value, true); 161 | } 162 | } 163 | } 164 | } 165 | 166 | /** 167 | * Calculates list of columns in data 168 | */ 169 | protected function calcColumnsList() 170 | { 171 | $this->columnsList = array_keys((array) reset($this->data)); 172 | } 173 | 174 | /** 175 | * Calculates length for string 176 | * 177 | * @param $str 178 | * 179 | * @return int 180 | */ 181 | protected function length($str) 182 | { 183 | return mb_strlen($str, $this->charset); 184 | } 185 | 186 | /** 187 | * Calculate maximum string length for each column 188 | */ 189 | private function calcColumnsLength() 190 | { 191 | foreach ($this->data as $row) { 192 | if ($row === '---') { 193 | continue; 194 | } 195 | 196 | foreach ($this->columnsList as $column) { 197 | $this->columnsLength[$column] = max( 198 | isset($this->columnsLength[$column]) 199 | ? $this->columnsLength[$column] 200 | : 0, 201 | $this->length($row[$column]), 202 | $this->length($column) 203 | ); 204 | } 205 | } 206 | } 207 | 208 | /** 209 | * Append a line separator to result 210 | */ 211 | private function lineSeparator() 212 | { 213 | $tmp = []; 214 | 215 | foreach ($this->columnsList as $column) { 216 | $tmp[] = str_repeat(self::H_LINE_CHAR, $this->columnsLength[$column] + 2); 217 | } 218 | 219 | $this->result[] = self::INTERSECT_CHAR . implode(self::INTERSECT_CHAR, $tmp) . self::INTERSECT_CHAR; 220 | } 221 | 222 | /** 223 | * @param $columnKey 224 | * @param $value 225 | * 226 | * @return string 227 | */ 228 | private function column($columnKey, $value) 229 | { 230 | return ' ' . $value . str_repeat( 231 | ' ', 232 | $this->columnsLength[$columnKey] - $this->length($value) 233 | ) . ' '; 234 | } 235 | 236 | /** 237 | * Render header part 238 | * 239 | * @return void 240 | */ 241 | private function renderHeader() 242 | { 243 | $this->lineSeparator(); 244 | 245 | if (!$this->renderHeader) { 246 | return; 247 | } 248 | 249 | $tmp = []; 250 | 251 | foreach ($this->columnsList as $column) { 252 | $tmp[] = $this->column($column, $column); 253 | } 254 | 255 | $this->result[] = self::V_LINE_CHAR . implode(self::V_LINE_CHAR, $tmp) . self::V_LINE_CHAR; 256 | 257 | $this->lineSeparator(); 258 | } 259 | 260 | /** 261 | * Render body section of table 262 | * 263 | * @return void 264 | */ 265 | private function renderBody() 266 | { 267 | foreach ($this->data as $row) { 268 | if ($row === '---') { 269 | $this->lineSeparator(); 270 | 271 | continue; 272 | } 273 | 274 | $tmp = []; 275 | 276 | foreach ($this->columnsList as $column) { 277 | $value = $this->column($column, $row[$column]); 278 | 279 | foreach ($this->columnFormatters as $formatter) { 280 | $value = $formatter->process($column, $value, false); 281 | } 282 | 283 | $tmp[] = $value; 284 | } 285 | 286 | $this->result[] = self::V_LINE_CHAR . implode(self::V_LINE_CHAR, $tmp) . self::V_LINE_CHAR; 287 | } 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/ArrayToTextTableException.php: -------------------------------------------------------------------------------- 1 | config = $config; 22 | } 23 | 24 | /** 25 | * @param string $columnName 26 | * @param string $value 27 | * @param bool $isBefore 28 | * 29 | * @return string 30 | */ 31 | public function process($columnName, $value, $isBefore) 32 | { 33 | if (!isset($this->config[$columnName])) { 34 | return $value; 35 | } 36 | 37 | $formatterValue = $this->config[$columnName]; 38 | 39 | // compute formatter value in case we accepted closure 40 | if (is_callable($this->config[$columnName])) { 41 | $formatterValue = call_user_func($formatterValue, $value); 42 | } 43 | 44 | if ($isBefore) { 45 | return $this->applyBefore($value, $formatterValue); 46 | } 47 | 48 | return $this->applyAfter($value, $formatterValue); 49 | } 50 | 51 | /** 52 | * Allows to apply some formatting to column value before calculating columns length. 53 | * Just return $value in case you don't want to do anything with the column at this stage 54 | * 55 | * @param $value 56 | * @param string $formatterValue 57 | * 58 | * @return string 59 | */ 60 | abstract protected function applyBefore($value, $formatterValue); 61 | 62 | /** 63 | * Allows to apply some formatting to column value after adding spaces to column value 64 | * Just return $value in case you don't want to do anything with the column at this stage 65 | * 66 | * @param $value 67 | * @param string $formatterValue 68 | * 69 | * @return string 70 | */ 71 | abstract protected function applyAfter($value, $formatterValue); 72 | } 73 | -------------------------------------------------------------------------------- /src/formatters/ColorFormatter.php: -------------------------------------------------------------------------------- 1 | self::DEFAULT_COLOR, 12 | 13 | 'Black' => '0;30', 14 | 'Dark Grey' => '1;30', 15 | 'Red' => '0;31', 16 | 'Light Red' => '1;31', 17 | 'Green' => '0;32', 18 | 'Light Green' => '1;32', 19 | 'Brown' => '0;33', 20 | 'Yellow' => '1;33', 21 | 'Blue' => '0;34', 22 | 'Light Blue' => '1;34', 23 | 'Magenta' => '0;35', 24 | 'Light Magenta' => '1;35', 25 | 'Cyan' => '0;36', 26 | 'Light Cyan' => '1;36', 27 | 'Light Grey' => '0;37', 28 | 'White' => '1;37', 29 | ]; 30 | 31 | protected function applyBefore($value, $formatterValue) 32 | { 33 | return $value; 34 | } 35 | 36 | protected function applyAfter($value, $formatterValue) 37 | { 38 | if ($formatterValue == 'Default') { 39 | return $value; 40 | } 41 | 42 | if (!isset($this->colors[$formatterValue])) { 43 | throw new ArrayToTextTableException('Unknown color to apply: ' . $formatterValue); 44 | } 45 | 46 | $color = $this->colors[$formatterValue]; 47 | 48 | return "\e[" . $color . 'm' . $value . "\e[" . self::DEFAULT_COLOR; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/formatters/SprintfFormatter.php: -------------------------------------------------------------------------------- 1 | 1], 13 | ['test' => -1], 14 | ]; 15 | 16 | $builder = new ArrayToTextTable($data); 17 | $builder->applyFormatter(new ColorFormatter(['test' => fn ($value) => $value > 0 ? 'Red' : 'Green'])); 18 | 19 | echo $builder->render() . PHP_EOL; 20 | -------------------------------------------------------------------------------- /tests/AlignFormatterTest.php: -------------------------------------------------------------------------------- 1 | applyFormatter(new AlignFormatter(['center' => 'center', 'right' => 'right'])); 25 | 26 | $this->assertEquals($expectResult, $builder->render()); 27 | } 28 | 29 | public static function getCases() 30 | { 31 | return [ 32 | [ 33 | 'data' => [ 34 | [ 35 | 'left' => 1, 36 | 'center' => 'Denis Koronets', 37 | 'right' => 2.89, 38 | ], 39 | [ 40 | 'left' => 2, 41 | 'center' => 'Dummy one', 42 | 'right' => 14.33, 43 | ], 44 | ], 45 | 'expected' => 46 | '+------+----------------+-------+' . PHP_EOL . 47 | '| left | center | right |' . PHP_EOL . 48 | '+------+----------------+-------+' . PHP_EOL . 49 | '| 1 | Denis Koronets | 2.89 |' . PHP_EOL . 50 | '| 2 | Dummy one | 14.33 |' . PHP_EOL . 51 | '+------+----------------+-------+', 52 | ], 53 | ]; 54 | } 55 | 56 | public function testInCorrectBuilding() 57 | { 58 | $data = [['test' => 1]]; 59 | 60 | $builder = new ArrayToTextTable($data); 61 | $builder->applyFormatter(new AlignFormatter(['test' => 'imposible'])); 62 | 63 | $this->expectException(ArrayToTextTableException::class); 64 | $builder->render(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/ColorTest.php: -------------------------------------------------------------------------------- 1 | applyFormatter(new ColorFormatter([ 26 | 'test' => function ($value) { 27 | return $value > 0 ? 'Red' : 'Green'; 28 | } 29 | ])); 30 | 31 | $this->assertEquals($expectResult, $builder->render()); 32 | } 33 | 34 | public static function getCases() 35 | { 36 | return [ 37 | [ 38 | 'data' => [ 39 | ['test' => 1], 40 | ['test' => -1], 41 | ], 42 | 'expected' => 43 | '+------+' . PHP_EOL . 44 | '| test |' . PHP_EOL . 45 | '+------+' . PHP_EOL . 46 | '| 1 |' . PHP_EOL . 47 | '| -1 |' . PHP_EOL . 48 | '+------+', 49 | ], 50 | ]; 51 | } 52 | 53 | public function testInCorrectBuilding() 54 | { 55 | $data = [['test' => 1]]; 56 | 57 | $builder = new ArrayToTextTable($data); 58 | $builder->applyFormatter(new AlignFormatter(['test' => 'imposible'])); 59 | 60 | $this->expectException(ArrayToTextTableException::class); 61 | $builder->render(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/CombinedAlignSprintfFormatterTest.php: -------------------------------------------------------------------------------- 1 | applyFormatter(new AlignFormatter(['center' => 'center', 'right' => 'right'])); 26 | $builder->applyFormatter(new SprintfFormatter(['right' => '%01.3f'])); 27 | 28 | $this->assertEquals($expectResult, $builder->render()); 29 | } 30 | 31 | public static function getCases() 32 | { 33 | return [ 34 | [ 35 | 'data' => [ 36 | [ 37 | 'left' => 1, 38 | 'center' => 'Denis Koronets', 39 | 'right' => 2.89, 40 | ], 41 | [ 42 | 'left' => 2, 43 | 'center' => 'Dummy one', 44 | 'right' => 14.33, 45 | ], 46 | [ 47 | 'left' => 3, 48 | 'center' => 'Another great day for a great inventors!', 49 | 'right' => 1, 50 | ], 51 | ], 52 | 'expected' => 53 | '+------+------------------------------------------+--------+' . PHP_EOL . 54 | '| left | center | right |' . PHP_EOL . 55 | '+------+------------------------------------------+--------+' . PHP_EOL . 56 | '| 1 | Denis Koronets | 2.890 |' . PHP_EOL . 57 | '| 2 | Dummy one | 14.330 |' . PHP_EOL . 58 | '| 3 | Another great day for a great inventors! | 1.000 |' . PHP_EOL . 59 | '+------+------------------------------------------+--------+', 60 | ], 61 | ]; 62 | } 63 | 64 | public function testInCorrectBuilding() 65 | { 66 | $data = [['test' => 1]]; 67 | 68 | $builder = new ArrayToTextTable($data); 69 | $builder->applyFormatter(new AlignFormatter(['test' => 'imposible'])); 70 | 71 | $this->expectException(ArrayToTextTableException::class); 72 | $builder->render(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/SimpleTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($expectResult, $builder->render()); 25 | } 26 | 27 | public static function getCases() 28 | { 29 | return [ 30 | [ 31 | 'data' => [ 32 | [ 33 | 'id' => 1, 34 | 'name' => 'Denis Koronets', 35 | 'role' => 'php developer', 36 | ], 37 | [ 38 | 'id' => 2, 39 | 'name' => 'Maxim Ambroskin', 40 | 'role' => 'java developer', 41 | ], 42 | [ 43 | 'id' => 3, 44 | 'name' => 'Andrew Sikorsky', 45 | 'role' => 'php developer', 46 | ], 47 | ], 48 | 'expected' => 49 | '+----+-----------------+----------------+' . PHP_EOL . 50 | '| id | name | role |' . PHP_EOL . 51 | '+----+-----------------+----------------+' . PHP_EOL . 52 | '| 1 | Denis Koronets | php developer |' . PHP_EOL . 53 | '| 2 | Maxim Ambroskin | java developer |' . PHP_EOL . 54 | '| 3 | Andrew Sikorsky | php developer |' . PHP_EOL . 55 | '+----+-----------------+----------------+', 56 | ], 57 | [ 58 | 'data' => [ 59 | [ 60 | 'singleColumn' => 'test value', 61 | ], 62 | ], 63 | 'expected' => 64 | '+--------------+' . PHP_EOL . 65 | '| singleColumn |' . PHP_EOL . 66 | '+--------------+' . PHP_EOL . 67 | '| test value |' . PHP_EOL . 68 | '+--------------+', 69 | ], 70 | [ 71 | 'data' => [ 72 | [ 73 | 'id' => 1, 74 | 'name' => 'Денис Коронец', 75 | 'role' => 'Тест кириллических символов', 76 | ], 77 | ], 78 | 'expected' => 79 | '+----+---------------+-----------------------------+' . PHP_EOL . 80 | '| id | name | role |' . PHP_EOL . 81 | '+----+---------------+-----------------------------+' . PHP_EOL . 82 | '| 1 | Денис Коронец | Тест кириллических символов |' . PHP_EOL . 83 | '+----+---------------+-----------------------------+', 84 | ], 85 | [ 86 | 'data' => [ 87 | [ 88 | 'id' => 1, 89 | 'name' => 'Денис Коронец', 90 | 'role' => 'Тест кириллических символов', 91 | ], 92 | '---', 93 | [ 94 | 'id' => 2, 95 | 'name' => 'Артем Малеев', 96 | 'role' => 'Тест кириллических символов 2', 97 | ], 98 | ], 99 | 'expected' => 100 | '+----+---------------+-------------------------------+' . PHP_EOL . 101 | '| id | name | role |' . PHP_EOL . 102 | '+----+---------------+-------------------------------+' . PHP_EOL . 103 | '| 1 | Денис Коронец | Тест кириллических символов |' . PHP_EOL . 104 | '+----+---------------+-------------------------------+' . PHP_EOL . 105 | '| 2 | Артем Малеев | Тест кириллических символов 2 |' . PHP_EOL . 106 | '+----+---------------+-------------------------------+', 107 | ], 108 | [ 109 | 'data' => [ 110 | ['test' => 1], 111 | ['test' => -1], 112 | ['test' => null], 113 | ], 114 | 'expected' => 115 | '+------+' . PHP_EOL . 116 | '| test |' . PHP_EOL . 117 | '+------+' . PHP_EOL . 118 | '| 1 |' . PHP_EOL . 119 | '| -1 |' . PHP_EOL . 120 | '| NULL |' . PHP_EOL . 121 | '+------+', 122 | ], 123 | ]; 124 | } 125 | 126 | public function testInCorrectDataBuilding() 127 | { 128 | $data = [['test' => []]]; 129 | 130 | $builder = new ArrayToTextTable($data); 131 | 132 | $this->expectException(ArrayToTextTableException::class); 133 | $builder->render(); 134 | } 135 | } 136 | --------------------------------------------------------------------------------