The League of Extraordinary Packages

Our Packages:

Presented by The League of Extraordinary Packages

Getting Started

The API

Upgrading Guide

Stream Filtering

To ease performing operations on the CSV as it is being read from or written to, the Reader and Writer classes now include methods to ease PHP stream filtering usage.

Stream Filter API

While in PHP the stream filter mode is attached to its associated filter, in League\Csv the filter mode is attached to the CSV object. This means that when you change the filter mode, you also clear all previously attached stream filters.

To be able to use the stream filtering mechanism you need to:

Detecting if the API is active

To be sure that the Stream Filter API is available it is recommend to use the method isActiveStreamFilter.

<?php

public AbstractCsv::isActiveStreamFilter(void): bool

isActiveStreamFilter returns true if you can safely use the API:

<?php

use League\Csv\Reader;
use League\Csv\Writer;

$reader = Reader::createFromPath('/path/to/my/file.csv');
$reader->isActiveStreamFilter(); //return true

$writer = Writer::createFromFileObject(new SplTempFileObject());
$writer->isActiveStreamFilter(); //return false the API can not be use

Warning: A LogicException exception may be thrown if you try to use the API under certain circumstances without prior validation using isActiveStreamFilter

Setting and getting the object stream filter mode

The stream filter mode property is set using PHP internal stream filter constant STREAM_FILTER_*.

<?php

public AbstractCsv::setStreamFilterMode(int $mode): AbstractCsv
public AbstractCsv::getStreamFilterMode(void): int

Unlike fopen, the mode is attached to the object and not to a specific stream filter.

By default:

<?php

use League\Csv\Reader;

$reader = Reader::createFromPath('/path/to/my/file.csv');
if ($reader->isActiveStreamFilter()) {
	$current_mode = $reader->getStreamFilterMode(); //returns STREAM_FILTER_READ
	$reader->setStreamFilterMode(STREAM_FILTER_WRITE);
	//this means that any filter you will set will have no effect when reading the CSV
	//all previously attached stream filters if they existed have been removed
	$current_mode = $reader->getStreamFilterMode(); //returns STREAM_FILTER_WRITE
}

Managing Stream filter

To manage your registered stream filter collection you can use the following methods:

<?php

public AbstractCsv::appendStreamFilter(string $filtername): AbstractCsv
public AbstractCsv::prependStreamFilter(string $filtername): AbstractCsv
public AbstractCsv::removeStreamFilter(string $filtername): AbstractCsv
public AbstractCsv::hasStreamFilter(string $filtername): bool
public AbstractCsv::clearStreamFilter(void): AbstractCsv

The $filtername parameter is a string that represents the filter as registered using php stream_filter_register function or one of PHP internal stream filter.

Since the stream filters are attached to the CSV object:

The filters are automatically applied when the stream filter mode matches the method you are using.

See below an example using League\Csv\Reader to illustrate:

<?php

use League\Csv\Reader;

stream_filter_register('convert.utf8decode', 'MyLib\Transcode');
// 'MyLib\Transcode' is a class that extends PHP's php_user_filter class

$reader = Reader::createFromPath('/path/to/my/chinese.csv');
if ($reader->isActiveStreamFilter()) {
	$reader->appendStreamFilter('string.toupper');
	$reader->appendStreamFilter('string.rot13');
	$reader->prependStreamFilter('convert.utf8decode');
	$reader->removeStreamFilter('string.rot13');
}
foreach ($reader as $row) {
	// each row cell now contains strings that have been:
	// first UTF8 decoded and then uppercased
}

Starting with version 8.1.0 you no longer need to URL encode your filter prior to attach it to the CSV object.

<?php

use League\Csv\Reader;

$reader = Reader::createFromPath('/path/to/my/chinese.csv');
$reader->appendStreamFilter('convert.iconv.UTF-8/ASCII//TRANSLIT');
var_dump($reader->fetchAll());

Limitations

Writer class on Editing Mode

Warning: To preserve file cursor position during editing the stream filter mode and the stream filter collection are frozen after the first insert is made using any of the insert* method. Any attempt to modify the stream filter status will fail silently.

<?php

use League\Csv\Writer;

$writer = Writer::createFromPath('/path/to/my/file.csv');
$writer->setDelimiter(',');
if ($writer->isActiveStreamFilter()) {
	$writer->addStreamFilter('string.toupper');
}
//first insert -> file.csv will contain uppercased data.
$writer->insertOne(['bill', 'gates', 'bill@microsoft.com']);
if ($writer->isActiveStreamFilter()) {
	//isActiveStreamFilter returns false so this code is never executed
	$writer->addStreamFilter('string.rot13');
}
//this filter is added to the collection but will never be applied!!
$writer->addStreamFilter('string.rot13');
//The inserted array will only be uppercased!!
$writer->insertOne('steve,job,job@apple.com');

echo $writer; //the newly added rows are all uppercased

Example

Please review the stream filtering example and the attached FilterTranscode Class to understand how to use the filtering mechanism to convert a CSV into another charset.

The FilterTranscode class is not attached to the Library because converting your CSV may depend on the extension you choose, in PHP you can use the following extensions :