PHP 8.6 deprecated the CSV methods on SplFileObject (fgetcsv(), fputcsv(), setCsvControl() and getCsvControl()) without adding anything to replace them. This RFC by Damian Jóźwiak fills that gap with a new, always-enabled ext/csv extension in the Csv\ namespace, ported from Gina Peter Banyard's girgias/csv extension and extended with file and stream support.
With the default settings, it follows RFC 4180, the CSV standard other CSV implementations follow. That means no escape character (quotes are escaped by doubling them), parsing that doesn't change with setlocale(), and multibyte delimiters and enclosures.
What it looks like
Here is reading a file today with the deprecated API:
$file = new SplFileObject('data.csv'); $file->setFlags(SplFileObject::READ_CSV); $file->setCsvControl(',', '"', ''); foreach ($file as $row) { // ... }
And with ext/csv:
foreach (Csv\LazyLaxCollection::createFromFile('data.csv') as $row) { // ... }
LazyLaxCollection reads rows lazily, so memory use tracks the longest row rather than the whole file. Writing works the same way. Csv\collection_to_file() takes any iterable of arrays, generators included, and writes one row at a time:
Csv\collection_to_file('out.csv', $rows);
There are also string helpers. Csv\row_to_array() parses one row, and Csv\array_to_row() formats one, which finally gives str_getcsv() an inverse without going through a php://memory stream. Csv\collection_to_buffer() and Csv\buffer_to_collection() handle whole documents and throw a ValueError when rows have different widths. A _lax variant allows that.
Nothing returns false. Bad arguments throw ValueError, non-array rows throw TypeError, and I/O failures throw Error.
What it means for existing code
Nothing breaks. fgetcsv(), fputcsv(), str_getcsv() and the deprecated SplFileObject methods keep working as they do now. The only change is that Csv\ becomes a reserved namespace, so userland code declaring symbols there could conflict.
If you migrate, expect some behavior to differ from the old functions on purpose: an empty line parses as [''] instead of [null], a quote inside an unquoted field is an error, whitespace before a quoted field is kept, and the default line ending is "\r\n".
Where it stands
The RFC is under discussion and targets the next minor release after PHP 8.6. Two questions are still open: whether the extension can be disabled at build time, and what to call LazyLaxCollection, along with whether a strict lazy version should ship too. The proposed vote needs a 2/3 majority.