Php CLI how to draw table
The goal
Php shine whenever you work with a lot of text data. So php-cli applications are quite demandable. But for cli (command line interface) we do not have all this fancy html tags and css3 styles. Cli applications are intendent for advanced users, who often want output as stream, which can be directed to next tool in chain. So, in most cases writing results with simple echo works just fine.. unless you need to add a table to the output.
Solution
My first idea was to use ncurses, but.. there is much simpler solution.
All you need is
str_repeat()
It’s also possible to use str_pad()
in some cases. But, unfortunately, str_pad()
does not support unicode.
The idea is to calculate max width for every column, and than fill all cells to this width with spaces.
After that, just concatenate array with implode
to display each table row.
Example of the following drawTable() function output:
Source code:
<?php
/**
@param Array data - array of table rows
@return String representing the table
@example echo drawTable([
[ "id"=>1, "name"=>"Test A", "description"=>"short text" ],
[ "id"=>2, "name"=>"Test B", "description"=>"long text, describing something" ],
[ "id"=>3, "name"=>"Test C", "description"=>"long text with multibyte string ä ë ï ö ü" ],
[ "id"=>4, "name"=>"Test ö", "description"=>"myltybyte in name column" ],
[ "id"=>5, "name"=>"Test E", "description"=>"weeeee!!! äëïöü äëïöü äëïöü äëïöü äëïöü äëïöü" ],
]);
*/
function drawTable(Array $data):String {
// sanity check
if(empty($data)) { return ""; }
// get column names
$keys = array_keys( reset($data) );
// calculate maximum width for each column
$columnWidths = [];
foreach($keys as $key) {
$columnWidths[$key] = mb_strlen($key);
foreach($data as $item) {
$width = mb_strlen($item[$key]);
if( $width > $columnWidths[$key] ) {
$columnWidths[$key] = $width;
}
}
}
$columnNames = $horizontalLines = [];
foreach( $keys as $key ) {
// fill column titles with spaces
$columnNames[$key] = $key.str_repeat(" ",$columnWidths[$key] - mb_strlen($key));
// prepare horisontal lines
$horizontalLines[$key] = str_repeat("─",$columnWidths[$key]);
}
// fill every cell to max column width
$data = array_map(function($row) use ($columnWidths) {
array_walk($row, function(&$cell, $key) use ($columnWidths){
$cell .= str_repeat(" ", $columnWidths[$key] - mb_strlen($cell));
});
return $row;
}, $data);
ob_start();
// draw table header
echo "╭─".implode("─┬─", $horizontalLines)."─╮\n";
echo "│ ".implode(" │ ", $columnNames )." │\n";
echo "├─".implode("─┼─", $horizontalLines)."─┤\n";
// draw lines
foreach( $data as $row ) {
echo "│ ".implode(" │ ", $row)." │\n";
}
// draw bottom line
echo "╰─".implode("─┴─", $horizontalLines)."─╯\n";
return ob_get_clean();
}
Note: This solution draw to string buffer, not stdout. For actual display use
echo drawTable(...)
Of cause, you can just print it to stdout directly, without ob_start()
, ob_get_clean()
and use different symbols for table borders.
Here, take some:
─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏
┐ ┑ ┒ ┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟
┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯
┰ ┱ ┲ ┳ ┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻ ┼ ┽ ┾ ┿
╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╌ ╍ ╎ ╏
═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟
╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ ╭ ╮ ╯
╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿