How to Create ASCII Tables in PHP

Creating a table of rows and columns in ASCII where each column may need resized as the data in each cell changes can be painful. So I wrote a simple snippet in PHP that can read a CSV file, and print out the table in plain ASCII for markdown. You can use this as just a <pre> block or for markdown tables.

First the function that takes in an array of rows and columns from a CSV and returns a string formatted to fit the ASCII table’s maximum column width.

<?php
function asciiTable(Array $rows, Int $padSize = 1, String $justify = "right", String $separator = "|", String $border = "-"): String {
    if ($justify === "center") {
        $justify = STR_PAD_BOTH;
    } elseif ($justify === "left") {
        $justify = STR_PAD_LEFT;
    } else {
        $justify = STR_PAD_RIGHT;
    }
    $body = "";
    $headers = reset($rows); // copy the header columns
    $sheet = $rows;
    $sizes = array_map(function($row) { return array_map(function($col) { return strlen($col); }, $row); }, $sheet);
    foreach ($headers as $column => $data) {
        $colSize[$column] = max(array_column($sizes, $column));
    }
    foreach($sheet as $row => $columns) {
        $last = count($columns) - 1;
        $line = "";
        foreach ($columns as $n => $column) {
            $line .= $separator .
                     str_pad(
                     str_repeat(" ", $padSize) .
                         $column . str_repeat(" ", $padSize),
                         $colSize[$n] + ($padSize * 2),
                         " ",
                         $justify
                     ) .
                     ($n === $last ? $separator : "");
        }
        $body .= "$line\n";
        if (!$row) {
            $line = "";
            foreach ($columns as $n => $column) {
                $column = str_repeat($border, $colSize[$n] + ($padSize * 2));
                $line .= $separator . str_pad($column, $colSize[$n] + ($padSize * 2)) . ($n === $last ? $separator : "");
            }
            $body .= "$line\n";
        }
    }
    
    return $body;
}

Here’s the part that reads in the CSV file and prints out the ASCII table.

<?php
if (($handle = fopen("test.csv", "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
        $rows[] = $data;
    }
    fclose($handle);
}


echo asciiTable($rows);

Here’s what the output would look like. I used some historical weather data for demonstration.

| ELEVATION | DATE    | PRCP  | PSUN | SNOW | TAVG | TMAX | TMIN |
|-----------|---------|-------|------|------|------|------|------|
| 149.4     | 2019-02 | 83.8  |      | 89   |      |      |      |
| 149.4     | 2019-03 | 93.0  |      | 356  |      |      |      |
| 149.4     | 2019-04 | 121.1 |      |      |      |      |      |
| 149.4     | 2019-05 | 216.3 |      |      |      |      |      |
| 149.4     | 2019-06 | 133.8 |      |      |      |      |      |
| 149.4     | 2019-07 | 246.3 |      |      |      |      |      |
| 149.4     | 2019-09 | 13.7  |      |      |      |      |      |
| 149.4     | 2019-10 | 186.8 |      |      |      |      |      |
| 149.4     | 2019-11 | 81.1  |      |      |      |      |      |
| 149.4     | 2019-12 | 171.7 |      | 267  |      |      |      |
| 85.3      | 2019-01 | 140.8 |      | 13   | -1.6 | 3.1  | -6.2 |
| 85.3      | 2019-02 | 81.9  |      | 102  | -0.2 | 4.3  | -4.7 |
| 85.3      | 2019-03 | 92.8  |      | 140  | 2.3  | 7.6  | -3.0 |
| 85.3      | 2019-04 | 129.8 |      | 0    | 11.2 | 16.7 | 5.8  |
| 85.3      | 2019-05 | 239.2 |      | 0    | 15.8 | 20.8 | 10.9 |
| 85.3      | 2019-06 | 162.0 |      | 0    | 20.5 | 26.2 | 14.9 |
| 85.3      | 2019-07 | 182.0 |      | 0    | 24.9 | 30.6 | 19.3 |

Which gives you perfect markdown that looks good both in markdown and markup!

ELEVATION DATE PRCP PSUN SNOW TAVG TMAX TMIN
149.4 2019-02 83.8 89
149.4 2019-03 93.0 356
149.4 2019-04 121.1
149.4 2019-05 216.3
149.4 2019-06 133.8
149.4 2019-07 246.3
149.4 2019-09 13.7
149.4 2019-10 186.8
149.4 2019-11 81.1
149.4 2019-12 171.7 267
85.3 2019-01 140.8 13 -1.6 3.1 -6.2
85.3 2019-02 81.9 102 -0.2 4.3 -4.7
85.3 2019-03 92.8 140 2.3 7.6 -3.0
85.3 2019-04 129.8 0 11.2 16.7 5.8
85.3 2019-05 239.2 0 15.8 20.8 10.9
85.3 2019-06 162.0 0 20.5 26.2 14.9
85.3 2019-07 182.0 0 24.9 30.6 19.3