RFC4180 Field compliance

This class is deprecated as of version 9.2.0. Please use the setEscape method directly with the empty escape character argument instead with the Reader or the Writer object.

The RFC4180Field class enables to work around the following bugs in PHP’s native CSV functions:

  • bug #43225: fputcsv incorrectly handles cells ending in \ followed by "
  • bug #55413: str_getcsv doesn’t remove escape characters
  • bug #74713: CSV cell split after fputcsv() + fgetcsv() round trip.
  • bug #38301: field enclosure behavior in fputcsv (since version 9.1.0)

When using this stream filter you can easily create or read a RFC4180 compliant CSV document using League\Csv connections objects.

Changing the CSV objects control characters after registering the stream filter may result in unexpected returned records.

Usage with League\CSV objects

public static RFC4180Field::addTo(AbstractCsv $csv, string $whitespace_replace = ''): AbstractCsv

The RFC4180Field::addTo method will register the stream filter if it is not already the case and add the stream filter to the CSV object using the following properties:

  • the CSV enclosure property;
  • the CSV escape property;
  • the CSV stream filter mode;
use League\Csv\RFC4180Field;
use League\Csv\Writer;

$writer = Writer::createFromPath('php://temp', 'r+');
$writer->setNewline("\r\n"); //RFC4180 Line feed
RFC4180Field::addTo($writer); //adding the stream filter to fix field formatting
$writer->insertAll($iterable_data);
$writer->output('mycsvfile.csv'); //outputting a RFC4180 compliant CSV Document

the $whitespace_replace argument is available since version 9.1.0

When the $whitespace_replace sequence is different from the empty space and does not contain:

  • one of the current CSV control characters;
  • a character that can trigger field enclosure;

its value will be used to:

  • prevent fputcsv default behavior of always using enclosure when a whitespace is found in a record field
use League\Csv\RFC4180Field;
use League\Csv\Writer;

$writer = Writer::createFromPath('php://temp', 'r+');
RFC4180Field::addTo($writer, "\0");
$writer->insertOne(['foo bar', 'bar']);
echo $writer->getContent(); //display 'foo bar,bar' instead of '"foo bar",bar'

The $whitespace_replace sequence should be a sequence not present in the inserted records, otherwise your CSV content will be affected by it.

use League\Csv\RFC4180Field;
use League\Csv\Writer;

$writer = Writer::createFromPath('php://temp', 'r+');
RFC4180Field::addTo($writer, 'fo'); //incorrect sequence this will alter your CSV
$writer->insertOne(['foo bar', 'bar']);
echo $writer->getContent(); //display ' o bar,baz' instead of 'foo bar,baz'

On records insertion

To fully comply with RFC4180 you will also need to use League\Csv\Writer::setNewline.

On records extraction

Conversely, to read a RFC4180 compliant CSV document, when using the League\Csv\Reader object, you just need to add the League\Csv\RFC4180Field stream filter as shown below:

use League\Csv\Reader;
use League\Csv\RFC4180Field;

//the current CSV is ISO-8859-15 encoded with a ";" delimiter
$csv = Reader::createFromPath('/path/to/rfc4180-compliant.csv', 'r');
RFC4180Field::addTo($csv); //adding the stream filter to fix field formatting

foreach ($csv as $record) {
    //do something meaningful here...
}

Usage with PHP stream resources

public static RFC4180Field::register(): void
public static RFC4180Field::getFiltername(): string

To use this stream filter outside League\Csv objects you need to:

  • register the stream filter using RFC4180Field::register.
  • use RFC4180Field::getFiltername with one of PHP’s attaching stream filter functions with the correct arguments as shown below:
use League\Csv\RFC4180Field;

RFC4180Field::register();

$resource = fopen('/path/to/my/file', 'r');
$filter = stream_filter_append($resource, RFC4180Field::getFiltername(), STREAM_FILTER_READ, [
    'enclosure' => '"',
    'escape' => '\\',
    'mode' => STREAM_FILTER_READ,
]);

while (false !== ($record = fgetcsv($resource))) {
    //$record is correctly parsed
}