Hi internals,
I'd like to open the discussion on an RFC proposing a dedicated CSV
extension (ext/csv) for PHP core.
RFC: https://wiki.php.net/rfc/csv_extension
Implementation: https://github.com/php/php-src/pull/24199
The proposal builds upon Gina Peter Banyard's girgias/csv extension
(BSD-3-Clause), with additional functionality for file and stream handling.
The motivation includes the deprecation of the SplFileObject CSV methods in
PHP 8.6, which leaves certain use cases without a direct replacement.
The proposed extension provides:
Six functions and one class in the Csv\ namespace.
RFC 4180-compatible parsing and serialization by default.
Locale-independent processing without the proprietary escape mechanism.
Multibyte delimiters, enclosures, and EOL sequences.
Strict and lax collection parsing.
Lazy iteration over CSV files and streaming output.
The implementation includes PHPT tests covering parsing, serialization,
stream handling, resource lifetime, and various edge cases.
The pull request's CI checks are currently passing across the tested
platforms, including debug, ZTS, and AddressSanitizer configurations.
The initial API is deliberately kept small. A more comprehensive
object-oriented Reader/Writer API, including capability-oriented
interfaces, is left for potential future discussion.
I'd particularly appreciate feedback on the public API design, the naming
of LazyLaxCollection, and whether the extension should be always enabled or
optional at build time.
For transparency, Gina has not reviewed or endorsed this RFC. Parts of the
implementation were developed with LLM assistance; I have reviewed the code
and take full responsibility for the contribution.
This is an invitation for discussion, not a call for votes.
Thanks in advance for your feedback.
Best regards,
Damian Jóźwiak
On Fri, Oct 9, 2026 at 8:22 AM Damian Jóźwiak damian.jozwiak.lodz@gmail.com
wrote:
Hi internals,
I'd like to open the discussion on an RFC proposing a dedicated CSV
extension (ext/csv) for PHP core.RFC: https://wiki.php.net/rfc/csv_extension
Implementation: https://github.com/php/php-src/pull/24199
The proposal builds upon Gina Peter Banyard's girgias/csv extension
(BSD-3-Clause), with additional functionality for file and stream handling.The motivation includes the deprecation of the SplFileObject CSV methods
in PHP 8.6, which leaves certain use cases without a direct replacement.The proposed extension provides:
Six functions and one class in the Csv\ namespace.
RFC 4180-compatible parsing and serialization by default.
Locale-independent processing without the proprietary escape mechanism.
Multibyte delimiters, enclosures, and EOL sequences.
Strict and lax collection parsing.
Lazy iteration over CSV files and streaming output.
The implementation includes PHPT tests covering parsing, serialization,
stream handling, resource lifetime, and various edge cases.The pull request's CI checks are currently passing across the tested
platforms, including debug, ZTS, and AddressSanitizer configurations.The initial API is deliberately kept small. A more comprehensive
object-oriented Reader/Writer API, including capability-oriented
interfaces, is left for potential future discussion.I'd particularly appreciate feedback on the public API design, the naming
of LazyLaxCollection, and whether the extension should be always enabled
or optional at build time.For transparency, Gina has not reviewed or endorsed this RFC. Parts of the
implementation were developed with LLM assistance; I have reviewed the code
and take full responsibility for the contribution.This is an invitation for discussion, not a call for votes.
Thanks in advance for your feedback.
Best regards,
Damian Jóźwiak
I see that there's no support for open streams/resources, would that be
something worth adding? I don't want to have to write things to a file
before I can start processing them if I already have an open stream. Same
goes for reading, I don't want to have to read everything before passing it
somewhere, often having it as a resource is more useful to me.
About LazyLaxCollection, what does Lax mean here?
Hi,
Thanks for the feedback! Both are good points.
On streams/resources:
I agree that supporting already-open streams would be a valuable addition,
particularly for reading. For writing, it is already possible to stream
rows individually without buffering the entire collection:
foreach ($rows as $row) {
fwrite($stream, Csv\array_to_row($row));
}
However, a dedicated collection_to_stream() would provide a more convenient
API, consistent row-width validation, and symmetry with the existing
file-based function. For reading, the gap is more significant. Splitting a
stream into lines and passing them to row_to_array() is not reliable
because enclosed CSV fields may contain line breaks. The parser used by
createFromFile() already operates on PHP streams internally, so exposing
this functionality for existing stream resources should be relatively
straightforward. I'm considering two additional entry points:
Csv\collection_to_stream($stream, iterable $collection, ...): void
Csv\LazyLaxCollection::createFromStream($stream, ...): LazyLaxCollection
I would prefer separate entry points rather than overloading the existing
$file parameter to accept both paths and resources. Since PHP does not
support resource as a declared parameter type, the stream parameter would
be untyped, documented as @param resource, and validated at runtime, with
invalid arguments resulting in a TypeError.
The intended semantics for createFromStream() would be:
- Parsing starts at the stream's current position, allowing callers to skip
a BOM or preamble beforehand. - The extension borrows the stream and never closes it.
- Repeated iteration seeks back to the position recorded when the
collection was created. Non-seekable streams support only a single
iteration. - If the caller closes the stream during iteration, subsequent reads should
throw an error rather than access freed memory. - Since the parser reads ahead in chunks, the underlying stream's position
after partial iteration is not guaranteed to match the end of the last
returned row. Mixing reads from the collection with direct reads from the
same stream would therefore not be supported.
Would these semantics work for your use cases, particularly the read-ahead
behavior and separate entry points?
On LazyLaxCollection:
"Lax" means that rows are not required to contain the same number of
fields. This mirrors the distinction between buffer_to_collection(), which
throws a ValueError when row widths differ, and buffer_to_collection_lax(),
which accepts rows of varying widths. The name comes from the original
girgias/csv implementation by Gina Peter Banyard. To make this behavior
explicit, I've also added a dedicated PHPT test:
ext/csv/tests/LazyCollection/fromBuffer/lax_varying_field_count.phpt. The
test verifies that LazyLaxCollection::createFromBuffer() accepts
consecutive rows containing 3, 1, 2, and 4 fields, while
buffer_to_collection() throws a ValueError for the same input. Naming is
already listed as an open issue in the RFC, along with whether a strict
lazy variant should be included. Your question reinforces that the current
name may not communicate its purpose clearly enough. I'd welcome
suggestions here. For example, LazyCollection with configurable strictness
could be worth considering. I'll update the RFC once we've had a chance to
discuss the API details.
Thanks again!
Best regards,
Damian
On Fri, Oct 9, 2026 at 8:22 AM Damian Jóźwiak <
damian.jozwiak.lodz@gmail.com> wrote:Hi internals,
I'd like to open the discussion on an RFC proposing a dedicated CSV
extension (ext/csv) for PHP core.RFC: https://wiki.php.net/rfc/csv_extension
Implementation: https://github.com/php/php-src/pull/24199
The proposal builds upon Gina Peter Banyard's girgias/csv extension
(BSD-3-Clause), with additional functionality for file and stream handling.The motivation includes the deprecation of the SplFileObject CSV methods
in PHP 8.6, which leaves certain use cases without a direct replacement.The proposed extension provides:
Six functions and one class in the Csv\ namespace.
RFC 4180-compatible parsing and serialization by default.
Locale-independent processing without the proprietary escape
mechanism.Multibyte delimiters, enclosures, and EOL sequences.
Strict and lax collection parsing.
Lazy iteration over CSV files and streaming output.
The implementation includes PHPT tests covering parsing, serialization,
stream handling, resource lifetime, and various edge cases.The pull request's CI checks are currently passing across the tested
platforms, including debug, ZTS, and AddressSanitizer configurations.The initial API is deliberately kept small. A more comprehensive
object-oriented Reader/Writer API, including capability-oriented
interfaces, is left for potential future discussion.I'd particularly appreciate feedback on the public API design, the naming
of LazyLaxCollection, and whether the extension should be always enabled
or optional at build time.For transparency, Gina has not reviewed or endorsed this RFC. Parts of
the implementation were developed with LLM assistance; I have reviewed the
code and take full responsibility for the contribution.This is an invitation for discussion, not a call for votes.
Thanks in advance for your feedback.
Best regards,
Damian JóźwiakI see that there's no support for open streams/resources, would that be
something worth adding? I don't want to have to write things to a file
before I can start processing them if I already have an open stream. Same
goes for reading, I don't want to have to read everything before passing it
somewhere, often having it as a resource is more useful to me.About LazyLaxCollection, what does Lax mean here?