Memory-efficient CSV reader and writer with header mapping and type casting

Maintainers

Package info

github.com/philiprehberger/php-csv

pkg:composer/philiprehberger/php-csv

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.1.1 2026-03-17 20:06 UTC

This package is auto-updated.

Last update: 2026-03-17 20:07:17 UTC


README

Tests Latest Version on Packagist License

Memory-efficient CSV reader and writer with header mapping and type casting.

Requirements

Dependency Version
PHP ^8.2

Installation

composer require philiprehberger/php-csv

Usage

Reading a CSV file

use PhilipRehberger\Csv\Csv;

// Read from file with headers
$rows = Csv::read('data.csv')->toArray();
// [['name' => 'Alice', 'age' => '30'], ...]

// Read from string
$rows = Csv::readString($csvContent)->toArray();

Generator-based iteration

The reader uses PHP generators for memory-efficient processing of large files:

foreach (Csv::read('large-file.csv') as $row) {
    // Process one row at a time — constant memory usage
}

Type casting

Automatically detect and cast value types:

$rows = Csv::read('data.csv')
    ->castTypes(true)
    ->toArray();
// "42" -> int, "3.14" -> float, "true"/"false" -> bool, "" -> null

Filtering and mapping

$rows = Csv::read('data.csv')
    ->castTypes(true)
    ->filter(fn (array $row) => $row['age'] >= 18)
    ->map(fn (array $row) => [...$row, 'label' => strtoupper($row['name'])])
    ->toArray();

Row Validation

Validate each row during reading. Invalid rows are skipped and errors are collected:

$reader = Csv::read('data.csv')
    ->validate(fn (array $row) => isset($row['email']) && str_contains($row['email'], '@'));

$rows = $reader->toArray();

// Inspect which rows failed validation
foreach ($reader->getValidationErrors() as $error) {
    echo "Row {$error['row']}: {$error['error']}\n";
}

The validator can also throw an exception to provide a specific error message:

$reader = Csv::read('data.csv')
    ->validate(function (array $row) {
        if (empty($row['name'])) {
            throw new \InvalidArgumentException('Name is required');
        }
        return true;
    });

Progress Tracking

Monitor processing progress with a callback invoked after each row:

Csv::read('large-file.csv')
    ->withProgress(function (int $rowNumber) {
        if ($rowNumber % 1000 === 0) {
            echo "Processed {$rowNumber} rows...\n";
        }
    })
    ->each(fn (array $row) => processRow($row));

Custom delimiters

$rows = Csv::readString($tsv)
    ->delimiter("\t")
    ->toArray();

Writing CSV

use PhilipRehberger\Csv\Csv;

Csv::write('output.csv')
    ->headers(['name', 'age', 'city'])
    ->row(['name' => 'Alice', 'age' => 30, 'city' => 'Berlin'])
    ->row(['name' => 'Bob', 'age' => 25, 'city' => 'Vienna'])
    ->save();

// Or get as string
$csv = Csv::write('')
    ->headers(['name', 'age'])
    ->rows($data)
    ->toString();

Streaming Writer

Write rows directly to disk without buffering, ideal for very large files:

use PhilipRehberger\Csv\Csv;

$writer = Csv::streamWrite('large-output.csv');
$writer->writeHeader(['id', 'name', 'value']);

foreach ($dataSource as $record) {
    $writer->writeRow([$record->id, $record->name, $record->value]);
}

$writer->close();

BOM for Excel

Prepend a UTF-8 BOM for Excel compatibility:

Csv::write('output.csv')
    ->headers(['name', 'age'])
    ->rows($data)
    ->bom(true)
    ->save();

API

Csv (static entry)

Method Description
Csv::read(string $path): CsvReader Create a reader from a file path
Csv::readString(string $content): CsvReader Create a reader from a string
Csv::write(string $path): CsvWriter Create a writer for a file path
Csv::streamWrite(string $path, string $delimiter = ','): StreamingWriter Create a streaming writer for a file path

CsvReader

Method Description
delimiter(string $char): self Set the field delimiter (default ,)
enclosure(string $char): self Set the field enclosure (default ")
hasHeader(bool $flag): self Whether the first row is a header (default true)
skipEmpty(bool $flag): self Skip empty rows (default true)
castTypes(bool $flag): self Auto-detect types: int, float, bool, null
filter(callable $fn): self Filter rows by a predicate
map(callable $fn): self Transform each row
validate(callable $fn): self Validate rows; invalid ones are skipped
withProgress(callable $fn): self Set a progress callback (receives row number)
getValidationErrors(): array Get errors from the last read
each(callable $fn): void Execute a callback for each row
toArray(): array Collect all rows into an array
count(): int Count the number of rows

CsvWriter

Method Description
headers(array $headers): self Set column headers
row(array $row): self Add a single row
rows(array $rows): self Add multiple rows
delimiter(string $char): self Set the field delimiter (default ,)
bom(bool $flag): self Prepend UTF-8 BOM for Excel
save(): void Write to the configured file path
toString(): string Return the CSV as a string

StreamingWriter

Method Description
writeHeader(array $headers): void Write the header row
writeRow(array $row): void Write a single data row
writeRows(array $rows): void Write multiple data rows
isHeaderWritten(): bool Whether the header has been written
close(): void Close the file handle

Development

composer install
vendor/bin/phpunit
vendor/bin/pint --test
vendor/bin/phpstan analyse

License

MIT