goodby/csv
Goodby CSV is a memory-efficient, flexible PHP library for importing and exporting CSV files. It processes rows line by line via callbacks without loading everything into memory, supports multibyte encodings like SJIS-win, EUC-JP, and UTF-8, and is fully unit-tested for enterprise use.
$lexer->parse($file, $interpreter) with addObserver(fn(array $row) => ...)) is a natural fit for "upload a customer/prospect/product/inventory list" flows, where each row is validated and persisted individually rather than buffered. This directly supports decisions like "do we build bulk CSV ingestion in-house or ship a narrower one-row-at-a-time admin form?"fgetcsv glue plus a mb_convert_encoding call is the realistic build alternative. Choosing this package is a decision to buy a thin, tested primitive rather than own edge cases (quoted delimiters, embedded newlines, BOM, encoding detection).Consider it when:
CsvImporter interface.Look elsewhere when:
league/csv (actively maintained, streaming reader, PHP 8 support) or maatwebsite/excel if you already live in the Laravel Excel ecosystem.=, +, -, @ prefixes), zip-bomb/row-count guards, or CVE turnaround. There is no evidence of maintenance since 2016; treat it as vendored, frozen code you own.Storage disks, UploadedFile validation rules, queue batching, chunked job dispatch, progress events. You will write all of that glue yourself; that glue is the real cost, not the parser.To executives (30 seconds): We need reliable CSV in/out for bulk data — customer imports, legacy system feeds, and reporting exports — including Japanese-encoded files from partner systems. This is a small, MIT-licensed, fully unit-tested library that does exactly that with very low memory use, so we can accept large files without scaling up servers. It is free, adds no licensing or vendor-negotiation risk, and is deliberately narrow, so it stays out of the critical path. The trade-off is that it is no longer actively developed; we mitigate that by isolating it behind our own interface so it can be swapped in days, not months, and by owning the security-relevant input validation ourselves. Recommendation: adopt as a contained implementation detail, not as a platform commitment.
To engineering: This is a thin streaming lexer/interpreter with a callback observer API and built-in multibyte conversion. It is not Laravel-aware — no facades, no service provider, no queue integration. The pitch is low surface area: wire it into one CsvImportService behind an interface, do validation, encoding allow-listing, row caps, and formula-injection sanitization in your layer, and version-pin it. Budget explicitly for the wrapper (validation, chunked job dispatch, progress, error-report rows) — that's where the real work and the real product value live, and it's also the seam that makes the dependency replaceable. If we're greenfield on PHP 8, benchmark league/csv first and only take this one where PHP-version constraints or SJIS-win handling justify it.
To the product/roadmap conversation: Fund the capability, not the library. The decision worth recording is "we will support large, encoding-diverse CSV ingestion/export with bounded memory," and this package is the cheapest credible way to prove that capability now, with a known and inexpensive exit path later.
How can I help you explore Laravel packages today?