Search by

Streaming validation for one-column email CSV imports with stable row cursors.

Package info

github.com/adebolaowolabi32/csv-kit

pkg:composer/cynthiaowolabi/csv-kit

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-10-09 11:14 UTC

This package is auto-updated.

Last update: 2026-10-11 21:11:12 UTC


README

Packagist version

Tests

A framework-independent PHP reader for one-column email CSV files, extracted from Fieldnotes Academy. It streams validated rows and exposes stable, one-based data-row cursors for applications that need resumable imports.

Install

Requires PHP 8.3 or newer. The library runs on PHP alone.

Install from Packagist:

composer require cynthiaowolabi/csv-kit:^1.0

Use

use Fieldnotes\CsvKit\Reader;

foreach ((new Reader)->rows('/private/learners.csv', after: $savedCursor) as $row => $data) {
    // In your database transaction: apply the row and persist $row together.
    enroll($data['email']);
}

Expected input:

email
alex@example.test
maya@example.test

With after: 1, the reader skips data row 1 and yields row 2 as 2 => ['email' => 'maya@example.test']. The calling application saves checkpoints.

Validation and normalization

  • Exactly one header named email, case-insensitive; UTF-8 BOM and surrounding header whitespace are accepted.
  • Standard CSV quoting and CRLF line endings are supported through PHP's fgetcsv.
  • Email values are trimmed, lowercased and validated with FILTER_VALIDATE_EMAIL, with a maximum of 254 bytes. Lowercasing the entire address is a deliberate import convention; this also normalizes the local part.
  • Empty files, wrong headers, extra columns, blank records and invalid addresses throw InvalidArgumentException. Data errors include the one-based row number.
  • Iteration stops at the first error.
  • The file handle closes when the generator finishes or is destroyed.

Catch errors around iteration, since generator execution starts when it is consumed:

try {
    foreach ((new Reader)->rows($path) as $row => $data) {
        // Process each validated row.
    }
} catch (\InvalidArgumentException $error) {
    // Report the validation error and keep the last committed cursor.
}

Resume contract and boundaries

Keep the source file immutable between attempts. Resume uses a saved row number: the reader scans past earlier rows and validates subsequent rows. Persist the cursor only after successfully committing that row's effects. The application must enforce idempotency and coordinate concurrent workers.

The calling application manages durable checkpoints, queues, database transactions, rollback, account lookup and idempotency. Fieldnotes Academy's cohort importer demonstrates those responsibilities in Laravel.

Memory usage scales with the largest row. The reader handles email CSV files using PHP's permissive CSV parser. Limit input size in the calling application when handling untrusted uploads.

Development

composer install
composer test
composer validate --strict

CI tests PHP 8.3, 8.4 and 8.5. PHPUnit 11 is used where required by the PHP version; compatible newer environments may resolve PHPUnit 12.

License and author

MIT © Cynthia Owolabi. Built independently with fictional test data.