How to output a simple ascii table in PHP?
Asked Answered
M

7

9

I have some data like:

Array
(
    [0] => Array
        (
            [a] => largeeeerrrrr
            [b] => 0
            [c] => 47
            [d] => 0
        )

    [1] => Array
        (
            [a] => bla
            [b] => 1
            [c] => 0
            [d] => 0
        )

    [2] => Array
        (
            [a] => bla3
            [b] => 0
            [c] => 0
            [d] => 0
        )

)

And I want to produce an output like:

title1        | title2 | title3 | title4
largeeeerrrrr | 0      | 47     | 0
bla           | 1      | 0      | 0
bla3          | 0      | 0      | 0

Which is the simples way to achieve this in PHP? I'd like to avoid using a library for such simple task.

Milan answered 22/2, 2011 at 18:27 Comment(2)
In steps: 1) find the longest column size you need for each column (you may need to use some of PHP's array functions) 2) print the headers, right-padding each header with spaces 3) add | 4) print a row, right-padding each value with spaces 5) repeat #4 for every rowSeng
Possible duplicate of PHP ASCII Table LibraryGlasshouse
Z
11

use printf

$i=0;
foreach( $itemlist as $items)
{
 foreach ($items as $key => $value)
 {
   if ($i++==0) // print header
   {
     printf("[%010s]|",   $key );
     echo "\n";
   }
   printf("[%010s]|",   $value);
 }
 echo "\n"; // don't forget the newline at the end of the row!
}

This uses 10 padding spaces. As BoltClock says, you probably want to check the length of the longest string first or your table will be jacked up on long strings.

Zoo answered 22/2, 2011 at 18:33 Comment(3)
How can I replace that 10 for a variable like $a?Milan
just like you replace any other variable in a string. printf("[%0{$a}s]|", $value);Zoo
Printf is useful but break asci sequences, like colors.Loma
J
7

Another library with auto column widths.

 <?php
 $renderer = new ArrayToTextTable($array);
 echo $renderer->getTable();
Johnajohnath answered 13/7, 2015 at 11:17 Comment(1)
Unfortunately, it doesn't support multibytes characters like "€".Ironhanded
H
5

I know this question is very old, but it appears in my google search and maybe it helps someone.

There's another Stackoverflow question with good answers, especially this one that points to a Zend Framework module called Zend/Text/Table.

Hope it help.


Docs introduction

Zend\Text\Table is a component for creating text-based tables on the fly using decorators. This can be helpful for sending structured data in text emails, or to display table information in a CLI application. Zend\Text\Table supports multi-line columns, column spans, and alignment.


Basic usage

$table = new Zend\Text\Table\Table(['columnWidths' => [10, 20]]);

// Either simple
$table->appendRow(['Zend', 'Framework']);

// Or verbose
$row = new Zend\Text\Table\Row();

$row->appendColumn(new Zend\Text\Table\Column('Zend'));
$row->appendColumn(new Zend\Text\Table\Column('Framework'));

$table->appendRow($row);

echo $table;
Output
┌──────────┬────────────────────┐
│Zend      │Framework           │
|──────────|────────────────────|
│Zend      │Framework           │
└──────────┴────────────────────┘
Hajji answered 23/1, 2015 at 20:28 Comment(0)
F
2

If you don't want to use any external libraries, this is a simple class that does the job

class StringTools
{
  public static function convertForLog($variable) {
    if ($variable === null) {
      return 'null';
    }
    if ($variable === false) {
      return 'false';
    }
    if ($variable === true) {
      return 'true';
    }
    if (is_array($variable)) {
      return json_encode($variable);
    }
    return $variable ? $variable : "";
  }

  public static function toAsciiTable($array, $fields, $wrapLength) {
    // get max length of fields
    $fieldLengthMap = [];
    foreach ($fields as $field) {
      $fieldMaxLength = 0;
      foreach ($array as $item) {
        $value = self::convertForLog($item[$field]);
        $length = strlen($value);
        $fieldMaxLength = $length > $fieldMaxLength ? $length : $fieldMaxLength;
      }
      $fieldMaxLength = $fieldMaxLength > $wrapLength ? $wrapLength : $fieldMaxLength;
      $fieldLengthMap[$field] = $fieldMaxLength;
    }

    // create table
    $asciiTable = "";
    $totalLength = 0;
    foreach ($array as $item) {
      // prepare next line
      $valuesToLog = [];
      foreach ($fieldLengthMap as $field => $maxLength) {
        $valuesToLog[$field] = self::convertForLog($item[$field]);
      }

      // write next line
      $lineIsWrapped = true;
      while ($lineIsWrapped) {
        $lineIsWrapped = false;
        foreach ($fieldLengthMap as $field => $maxLength) {
          $valueLeft = $valuesToLog[$field];
          $valuesToLog[$field] = "";
          if (strlen($valueLeft) > $maxLength) {
            $valuesToLog[$field] = substr($valueLeft, $maxLength);
            $valueLeft = substr($valueLeft, 0, $maxLength);
            $lineIsWrapped = true;
          }
          $asciiTable .= "| {$valueLeft} " . str_repeat(" ", $maxLength - strlen($valueLeft));
        }
        $totalLength = $totalLength === 0 ? strlen($asciiTable) + 1 : $totalLength;
        $asciiTable .= "|\n";
      }
    }

    // add lines before and after
    $horizontalLine = str_repeat("-", $totalLength);
    $asciiTable = "{$horizontalLine}\n{$asciiTable}{$horizontalLine}\n";
    return $asciiTable;
  }
}

This is an example of how to use it and below is the result on the terminal

public function handle() {
  $array = [
      ["name" => "something here", "description" => "a description here to see", "value" => 3],
      ["name" => "and a boolean", "description" => "this is another thing", "value" => true],
      ["name" => "a duck and a dog", "description" => "weird stuff is happening", "value" => "truly weird"],
      ["name" => "with rogue field", "description" => "should not show it", "value" => false, "rogue" => "nie"],
      ["name" => "some kind of array", "description" => "array i tell you", "value" => [3, 4, 'banana']],
      ["name" => "can i handle null?", "description" => "let's see", "value" => null],
  ];
  $table = StringTools::toAsciiTable($array, ["name", "value", "description"], 50);
  print_r($table);
}

table on terminal

Furlough answered 12/5, 2021 at 15:46 Comment(0)
I
2

For anyone interested in a more general solution, here's a function I'm using in my own code to format tabular data of the form:

$data = [
  ['Header 1', 'Header 2'],
  ['Row1Cell1', 'Row1Cell2'],
  ['Row2Cell1', 'Row2Cell2'],
];

To get:

┌───────────────────────┐
│ Header 1  │ Header 2  │
├───────────────────────┤
│ Row1Cell1 │ Row1Cell2 │
│ Row2Cell1 │ Row2Cell2 │
└───────────────────────┘

The code:

// needed because str_pad doesn't play nice with Unicode
// https://mcmap.net/q/605960/-php-str_pad-not-working-with-unicode-characters
// https://bugs.php.net/bug.php?id=21317
public static function pad(string $string, int $length, string $pad_string = " "): string
{
    return $string . str_repeat($pad_string, $length - mb_strlen($string));
}

public static function asciiTable(array $rows): string
{
    if (count($rows) === 0) {
        return '';
    }

    $widths = [];
    foreach ($rows as $cells) {
        foreach ($cells as $j => $cell) {
            if (($width = strlen($cell) + 2) >= ($widths[$j] ?? 0)) {
                $widths[$j] = $width;
            }
        }
    }

    $hBar = str_repeat('─', array_sum($widths) + count($widths) - 1);
    $topB = sprintf("┌%s┐", $hBar);
    $midB = sprintf("├%s┤", $hBar);
    $botB = sprintf("└%s┘", $hBar);

    $result[] = $topB;
    $fn = fn(string $c, int $w): string => self::pad(" {$c} ", $w);
    foreach ($rows as $i => $cells) {
        $result[] = sprintf("│%s│", implode('│', array_map($fn, $cells, $widths)));
        if ($i === 0) {
            $result[] = $midB;
        }
    }
    $result[] = $botB;

    return implode("\n", $result);
}
Inexperience answered 12/9, 2022 at 17:16 Comment(1)
this is nice, just a note: usually data comes out of the database with column data keyed by the column name, so in order to get this code to work with that you have to prepend with a header row: $data = array_prepend($data, array_combine(array_keys($data[0]), array_keys($data[0])));Missal
B
1

In addition to Byron Whitlock: You can use usort() with a callback to sort by longest array value. Example:

function lengthSort($a, $b){
    $a = strlen($a);
    $b = strlen($b);
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}
Bowery answered 22/2, 2011 at 18:37 Comment(0)
M
0

I liked Bryon's answer but I wanted each column to be length-aware so this is what I came up with with plain PHP/no package dependency.

// set up the data set you specified
$data = [
    [ 'title1' => 'largeeeerrrrr', 'title2' => 0, 'title3' => 47, 'title4' => 0 ],
    [ 'title1' => 'bla', 'title2' => 1, 'title3' => 0, 'title4' => 0 ],
    [ 'title1' => 'bla3', 'title2' => 0, 'title3' => 0, 'title4' => 0 ]
];

// parse array to make it easier to use
foreach ($data as $i => $lines)
{
    foreach ($lines as $key => $value) {
        $columns[$key][0] = $key;
        $columns[$key][] = $value;
        $rows[$i][] = $value;
    }
}
$rows = array_prepend($rows, array_keys($columns));
$lengths = array_values(array_map(fn($x) => max(array_map('strlen', $x)) , $columns));

// output ascii table
foreach ($rows as $row) {
    foreach ($row as $key => $data) {
        $length = $lengths[$key];
        $data = (is_bool($data)) ? (($data) ? 'true' : 'false') : $data;

        echo str_pad($data, $length, ' ', STR_PAD_RIGHT) . ' | ';
    }
    echo "\n";
}

output (exactly what op had wanted, but pretty easy to customize):

title1        | title2 | title3 | title4 | 
largeeeerrrrr | 0      | 47     | 0      | 
bla           | 1      | 0      | 0      | 
bla3          | 0      | 0      | 0      |
Missal answered 21/7, 2023 at 5:43 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.