Code Snippet: Generate A Plaintext Data Table

Inspired by some awesome-as-always code written by my co-worker Mike Adams, here’s a helper function to help create plaintext data tables in PHP. This is particularly useful for PHP-based CLI scripts (we use quite a few at Automattic) or for plaintext e-mails.

But first, here’s an example output with the optional dividers enabled:

   ID | First Name     | Last Name   
-------------------------------------
    1 | Alex           | Mills       
    2 | John           | Smith       
    3 | Barack         | Obama       
    4 | Fred           | Flinstone   
12345 | ReallyLongName | IsReallyLong

This is the code I used to create the above example:

$data = array(
	'labels' => array(
		'ID' => 'ID',
		'firstname' => 'First Name',
		'lastname' => 'Last Name',
	),
	'align' => array(
		'ID' => 'right',
	),
	array(
		'ID' => 1,
		'firstname' => 'Alex',
		'lastname' => 'Mills',
	),
	array(
		'ID' => 2,
		'firstname' => 'John',
		'lastname' => 'Smith',
	),
	array(
		'ID' => 3,
		'firstname' => 'Barack',
		'lastname' => 'Obama',
	),
	// Array order doesn't matter as it's associative
	array(
		'ID' => 4,
		'lastname' => 'Flinstone',
		'firstname' => 'Fred',
	),
	array(
		'lastname' => 'IsReallyLong',
		'firstname' => 'ReallyLongName',
		'ID' => 12345,
	),
);

echo '<pre>';
table_output( $data );
echo '</pre>';

You can easily loop through your data and generate an array in that format. The arrays are associative so order doesn’t matter.

The labels array is optional and just allows you assign pretty labels for the columns. If this array is missing, then the data keys will be used.

The align array is also optional and allows you to pass a value of right in order to right-align the data in a column. Defaults to left align.

Now, to the functions that generate the table!

// Output a formatted table
function table_output( $data, $dividers = true ) {
	$rows = table_get_rows( $data, $dividers );

	echo implode( "\n", $rows );
}

// Return an array of table rows
function table_get_rows( $data, $dividers = true ) {
	// Find the column keys to use (use the first data row)
	$columns = array_keys( $data[0] );

	// Create the labels if they're missing
	if ( empty( $data['labels'] ) )
		$data['labels'] = array_combine( $columns, $columns );

	// Calculate column widths
	foreach ( $data as $key => $item ) {
		if ( 'align' === $key )
			continue;

		foreach ( $item as $column => $value ) {
			$length = strlen( $value );

			if ( empty( $widths[$column] ) || $widths[$column] < $length )
				$widths[$column] = $length;
		}
	}

	// Create the row sprintf() format
	foreach ( $columns as $column ) {
		$align = ( isset( $data['align'] ) && isset( $data['align'][$column] ) && 'right' == $data['align'][$column] ) ? '' : '-';
		$formats[] = '%' . $align . $widths[$column] . 's';
	}
	$divider = ( $dividers ) ? ' | ' : ' ';
	$format = implode( $divider, $formats );

	// Generate the label row
	$rows['labels'] = vsprintf( $format, _table_get_line_data( $columns, $data['labels'] ) );

	// Should there be a divider row?
	if ( $dividers )
		$rows['divider'] = str_repeat( '-', strlen( $rows['labels'] ) );

	// Generate the data rows
	foreach ( $data as $key => $values ) {
		if ( 'labels' === $key || 'align' === $key )
			continue;

		$rows[$key] = vsprintf( $format, _table_get_line_data( $columns, $values ) );
	}

	return $rows;
}

// Helper function to get the array of data for a table row in the correct order
function _table_get_line_data( $columns, $item ) {
	foreach ( $columns as $column ) {
		$data[$column] = ( isset( $item[$column] ) ) ? $item[$column] : '';
	}

	return $data;
}

Hope you find it useful!

4 thoughts on “Code Snippet: Generate A Plaintext Data Table

  1. Calculating column widths should use mb_strlen not strlen. It will almost inevitably be wrong on real data otherwise. Try Rémi Gonçalves for example.

Comments are closed.