Search by

alexskrypnyk / csvtable

alexdrevops

PHP class to parse and format CSV content.

Package info

github.com/AlexSkrypnyk/CsvTable

pkg:composer/alexskrypnyk/csvtable

Fund package maintenance!

alexskrypnyk

Patreon

Statistics

Installs: 36 456

Dependents: 3

Suggesters: 0

Stars: 3

Open Issues: 1

1.3.0 2026-09-05 01:22 UTC

This package is auto-updated.

Last update: 2026-09-10 01:43:52 UTC


README

CsvTable logo

PHP class to parse and format CSV content

GitHub Issues GitHub Pull Requests Test PHP codecov GitHub release (latest by date) LICENSE Renovate

✨ Features

  • Single-file class to manipulate CSV table.
  • Formatters for CSV, text table and Markdown table.
  • Support for a custom formatter.
  • Column manipulation: reorder, filter, and exclude columns.

📋 Requirements

PHP 8.3 or newer. The class uses only the PHP standard library, so CsvTable.php can also be dropped into a project that does not use Composer.

📦 Installation

composer require alexskrypnyk/csvtable

The class is AlexSkrypnyk\CsvTable\CsvTable.

🚀 Usage

Given a CSV file with the following content:

col11,col12,col13
col21,col22,col23
col31,col32,col33

From string

$csv = file_get_contents($csv_file);
print (new CsvTable($csv))->format();

will produce identical CSV content by default:

col11,col12,col13
col21,col22,col23
col31,col32,col33

From file

print (CsvTable::fromFile($file))->format();

will produce identical CSV content by default:

col11,col12,col13
col21,col22,col23
col31,col32,col33

fromFile() throws an Exception when the file is missing or cannot be read.

Reading the parsed data

parse() populates the header and the rows, which are then available through getHeader() and getRows(). Calling format() parses the content automatically, so parse() is only needed when reading the data directly.

$table = new CsvTable($csv);
$table->parse();

// ['col11', 'col12', 'col13']
print_r($table->getHeader());

// [['col21', 'col22', 'col23'], ['col31', 'col32', 'col33']]
print_r($table->getRows());

Custom separator, enclosure and escape characters

The constructor and fromFile() both accept the separator, enclosure and escape characters used to read the content. They default to ,, " and \.

// Parse semicolon-separated content.
$table = new CsvTable($csv_semicolon, ';');

// The same, reading from a file.
$table = CsvTable::fromFile($file_semicolon, ';');

These characters apply to parsing only. The csv formatter writes using its own separator, enclosure and escape options, so semicolon-separated input is written back as comma-separated content unless the formatter is told otherwise:

$table = new CsvTable($csv_semicolon, ';');

// Comma-separated output.
print $table->format();

// Semicolon-separated output.
print $table->format(NULL, ['separator' => ';']);

Using table formatter

print (CsvTable::fromFile($file))->format('table');

will produce table content:

col11|col12|col13
-----------------
col21|col22|col23
col31|col32|col33

Using table formatter without a header

print (CsvTable::fromFile($file))->withoutHeader()->format('table');

will produce table content:

col11|col12|col13
col21|col22|col23
col31|col32|col33

withHeader() restores the default behaviour of treating the first row as a header.

Using markdown_table formatter

print (CsvTable::fromFile($file))->format('markdown_table');

will produce Markdown table:

| col11 | col12 | col13 |
|-------|-------|-------|
| col21 | col22 | col23 |
| col31 | col32 | col33 |

Formatter options

Options are passed as the second argument to format() and are merged over the defaults below.

Formatter Option Default Description
csv separator , Character written between values.
csv enclosure " Character used to enclose values.
csv escape \ Character used to escape special characters.
table column_separator | String written between columns.
table row_separator \n String written between rows.
markdown_table column_separator | String written between columns.
markdown_table row_separator \n String written between rows.
markdown_table header_separator - Character filling the row under the header.
markdown_table value_row_separator <br/> Replaces newlines found within a value.
print (CsvTable::fromFile($file))
  ->format('markdown_table', ['header_separator' => '=']);

will produce:

| col11 | col12 | col13 |
|=======|=======|=======|
| col21 | col22 | col23 |
| col31 | col32 | col33 |

Custom formatter as an anonymous callback

A formatter receives the header columns, the rows and the options, and returns the formatted string.

print (CsvTable::fromFile($file))->format(function ($header, $rows, $options) {
  $output = '';

  if (count($header) > 0) {
    $output = implode('|', $header);
    $output .= "\n" . str_repeat('=', strlen($output)) . "\n";
  }

  return $output . implode("\n", array_map(static function ($row): string {
    return implode('|', $row);
  }, $rows));
});

will produce:

col11|col12|col13
=================
col21|col22|col23
col31|col32|col33

Custom formatter as a class with default format method

When a class name is passed, its static format() method is called.

print (CsvTable::fromFile($file))
  ->withoutHeader()
  ->format(CustomFormatter::class);

Custom formatter as a class with a custom method and options

$formatter_options = ['option1' => 'value1', 'option2' => 'value2'];

print (CsvTable::fromFile($file))
  ->withoutHeader()
  ->format([CustomFormatter::class, 'customFormat'], $formatter_options);

format() throws an Exception when the value passed to it cannot be resolved to a callable.

🔀 Column Manipulation

Given a CSV file with the following content:

Name,Age,City,Country
John,30,New York,USA
Jane,25,London,UK

Reorder columns with columnOrder()

Reorder columns by specifying the desired order. Columns not specified are appended in their original order.

print (CsvTable::fromFile($file))
  ->columnOrder(['City', 'Name'])
  ->format('markdown_table');

will produce:

| City     | Name | Age | Country |
|----------|------|-----|---------|
| New York | John | 30  | USA     |
| London   | Jane | 25  | UK      |

Filter to specific columns with onlyColumns()

Keep only the specified columns in the output, in the order specified.

print (CsvTable::fromFile($file))
  ->onlyColumns(['Name', 'City'])
  ->format('markdown_table');

will produce:

| Name | City     |
|------|----------|
| John | New York |
| Jane | London   |

Exclude columns with withoutColumns()

Exclude specified columns from the output. The remaining columns keep their original order.

print (CsvTable::fromFile($file))
  ->withoutColumns(['Age', 'Country'])
  ->format('markdown_table');

will produce:

| Name | City     |
|------|----------|
| John | New York |
| Jane | London   |

Using column indices

All column methods accept both column names (strings) and zero-based indices (integers). An unknown name or an out-of-range index throws an InvalidArgumentException.

// Using indices.
print (CsvTable::fromFile($file))->columnOrder([2, 0])->format();

// Using mixed names and indices.
print (CsvTable::fromFile($file))->onlyColumns(['Name', 2])->format();

Names are resolved against the header, so content parsed with withoutHeader() can only be addressed by index.

Combining column transformations

Column transformations are applied in order: onlyColumns()withoutColumns()columnOrder().

print (CsvTable::fromFile($file))
  ->withoutColumns(['Age'])
  ->columnOrder(['Country', 'City'])
  ->format('markdown_table');

will produce:

| Country | City     | Name |
|---------|----------|------|
| USA     | New York | John |
| UK      | London   | Jane |

Reset column transformations

resetColumns() clears all three transformations at once. resetColumnOrder(), resetOnlyColumns() and resetWithoutColumns() clear them individually.

$table = CsvTable::fromFile($file);

// Apply and use transformations.
$table->columnOrder(['City', 'Name']);
print $table->format();

// Reset and format without transformations.
print $table->resetColumns()->format();

🤝 Contributing

See CONTRIBUTING.md for local development setup and the linting and testing commands.

🔄 Updating

To pull the latest infrastructure from the template into this project, ask Claude Code to "update scaffold" - see AGENTS.md for details.

This repository was created using the Scaffold project template