Upgrading from 8.x to 9.x
League\Csv 9.0
is a new major version that comes with backward compatibility breaks.
This guide will help you migrate from a 8.x version to 9.0. It will only explain backward compatibility breaks, it will not present the new features, (read the documentation for that).
Installation
If you are using composer then you should update the require section of your composer.json
file.
composer require league/csv:^9.0
This will edit (or create) your composer.json
file.
PHP version requirement
League\Csv 9.0
requires a PHP version greater or equal to 7.0.0 (was previously 5.5.0).
The Writer class
Stricter argument type
The Writer::insertOne
and Writer::insertAll
methods no longer accept a string as possible CSV records.
Before:
use League\Csv\Writer;
$writer = Writer::createFromFileObject(new SplTempFileObject());
$str = 'john,doe,john.doe@example.com';
$writer->insertOne($str);
$writer->insertAll([$str]);
After:
use League\Csv\Reader;
use League\Csv\Writer;
$writer = Writer::createFromFileObject(new SplTempFileObject());
$reader = Reader::createFromString('john,doe,john.doe@example.com');
$writer->insertOne($reader->fetchOne());
$writer->insertAll($reader);
Reduced method chaining
The Writer::insertOne
and Writer::insertAll
methods are no longer chainable.
Before:
use League\Csv\Writer;
$writer = Writer::createFromFileObject(new SplTempFileObject());
$record = ['john', 'doe', 'john.doe@example.com'];
$writer
->insertOne($record)
->insertAll([$record])
->insertOne($record)
;
After:
use League\Csv\Writer;
$writer = Writer::createFromFileObject(new SplTempFileObject());
$record = ['john', 'doe', 'john.doe@example.com'];
$writer->insertOne($record);
$writer->insertAll([$record]);
$writer->insertOne($record);
Removed methods
Writer::removeFormatter
Writer::hasFormatter
Writer::clearFormatters
Writer::removeValidator
Writer::hasValidator
Writer::clearValidators
Validators and Formatters can only be removed on object destruction.
You can no longer iterate over a Writer
class, use the Reader
class instead.
The Reader class
Removed methods
Reader::each
Reader::fetch
Reader::fetchAll
Reader::fetchAssoc
Reader::fetchDelimitersOccurrence
Reader::fetchPairsWithoutDuplicates
Reader::fetchAssoc
The Reader::fetchAssoc
features are now accessible using the new Reader::getRecords
.
You are required to specify the CSV header using Reader::setHeaderOffset
.
Before:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
foreach ($reader->fetchAssoc() as $records) {
//The CSV first row is implicitly used as the CSV header
//and as the index of each found record
//the CSV header offset is removed from iteration
}
After:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$reader->setHeaderOffset(0); //explicitly sets the CSV document header record
foreach ($reader->getRecords() as $records) {
//The CSV first row is used as the CSV header
//and as the index of each found record
//the CSV header offset is removed from iteration
}
or you can use the optional $header
argument from the Reader::getRecords
method.
Before:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$records = $reader->fetchAssoc(['firstname', 'lastname', 'email']);
After:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$records = $reader->getRecords(['firstname', 'lastname', 'email']);
Last but not least if you are using query filters then use the optional $header
argument from the Statement::process
method.
Before:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$records = $reader
->limit(5)
->offset(3)
->fetchAssoc(['firstname', 'lastname', 'email'])
;
After:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$stmt = (new Statement())
->limit(5)
->offset(3)
;
$records = $stmt->process($reader, ['firstname', 'lastname', 'email']);
Reader::fetchAll, Reader::fetch, Reader::each
These methods are removed because the Reader
and the ResultSet
implements the IteratorAggregate
interface.
Before:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
foreach ($reader->fetchAll() as $key => $value) {
// do something here
}
After:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
foreach ($reader as $record) {
// do something here
}
Before:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$func = function (array $record) {
return array_map('strtoupper', $record);
};
$records = $reader
->setOffset(3)
->setLimit(2)
->fetch($func)
;
foreach ($records as $record) {
// do something here
}
After:
use League\Csv\Reader;
use League\Csv\Statement;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$stmt = (new Statement())
->offset(3)
->limit(2)
;
$records = $stmt->process($reader);
foreach ($records as $record) {
$res = array_map('strtoupper', $record);
// do something here
}
Reader::fetchPairsWithoutDuplicates
The Reader::fetchPairsWithoutDuplicates
is removed as it is redundant with the fetchPairs
method.
Before:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$pairs_without_duplicates = $reader
->setOffset(3)
->setLimit(2)
->fetchPairsWithoutDuplicates()
;
foreach ($pairs_without_duplicates as $key => $value) {
// do something here
}
After:
use League\Csv\Reader;
use League\Csv\Statement;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$stmt = (new Statement())
->offset(3)
->limit(2)
;
$records = $stmt->process($reader);
$pairs_without_duplicates = iterator_to_array($records->fetchPairs(), true);
foreach ($pairs_without_duplicates as $record) {
// do something here
}
Reader::fetchDelimitersOccurrence
Use the League\Csv\delimiter_detect
function instead with a Reader
object.
Before:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$stats = $reader->fetchDelimitersOccurrence([',', ';', "\t"], 10);
After:
use League\Csv\Reader;
use function League\Csv\delimiter_detect;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$stats = delimiter_detect($reader, [',', ';', "\t"], 10);
Optional callable arguments are removed
The following methods no longer accept an optional callable as argument because they return an iterable object.
Reader::fetchColumn
Reader::fetchPairs
Before:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
$records = $reader->fetchColumn(0, function ($value) {
return strtoupper($value);
});
After:
use League\Csv\Reader;
$reader = Reader::createFromPath('/path/to/file.csv', 'r');
foreach ($records->fetchColumn(0) as $value) {
$value = strtoupper($value);
}
Stream Filtering
Stream support detection
To detect if PHP stream filters are supported you need to call AbstractCsv::supportsStreamFilter
Before:
use League\Csv\Writer;
$csv = Writer::createFromPath('/path/to/file.csv');
$csv->isActiveStreamFilter(); //true
After:
use League\Csv\Writer;
$csv = Writer::createFromPath('/path/to/file.csv');
$csv->supportsStreamFilter(); //true
Stream mode
The filtering mode is fixed and can not be changed:
- a
Writer
class will only accept stream filters in writing mode only - a
Reader
class will only accept stream filters in reading mode only
Therefore AbstractCsv::setStreamFilterMode
is removed.
To add a stream filter you will only need the AbstractCsv::addStreamFilter
method.
Before:
use League\Csv\Writer;
$csv = Writer::createFromPath('/path/to/file.csv');
$csv->appendStreamFilter('string.toupper');
$csv->prependStreamFilter('string.rot13');
After:
use League\Csv\Writer;
$csv = Writer::createFromPath('/path/to/file.csv');
$csv->addStreamFilter('string.rot13');
$csv->addStreamFilter('string.toupper');
//the insertion order has changed
stream filter removal
PHP Stream filters will only be removed on CSV object destruction.
Before:
use League\Csv\Writer;
$csv = Writer::createFromPath('/path/to/file.csv');
$csv->appendStreamFilter('string.toupper');
$csv->prependStreamFilter('string.rot13');
$csv->removeStreamFilter('string.rot13');
$csv->clearStreamFilters();
After:
use League\Csv\Writer;
$csv = Writer::createFromPath('/path/to/file.csv');
$csv->addStreamFilter('string.rot13');
$csv->addStreamFilter('string.toupper');
$csv = null;
Conversion methods
All conversion methods are no longer attached to the Reader
or the Writer
classes, you need a Converter object to convert your CSV. The following methods are removed:
Writer::jsonSerialize
AbstractCsv::toHTML
AbstractCsv::toXML
And you can no longer convert a Writer
class.
Before:
use League\Csv\Writer;
$csv = Writer::createFromPath('/path/to/file.csv');
$dom = $csv->toXML(); //$dom is a DOMDocument
After:
use League\Csv\XMLConverter;
use League\Csv\Reader;
$csv = Reader::createFromPath('/path/to/file.csv', 'r');
$dom = (new XMLConverter())->convert($csv); //$dom is a DOMDocument
Miscellaneous
Switching between connections
AbstractCsv::newReader
AbstractCsv::newWriter
You can no longer switch between connection. You are required to explicitly load a new Reader
and/or Writer
object.
Columns consistency Validator
League\Csv\Plugin\ColumnConsistencyValidator
is renamedLeague\Csv\ColumnConsistency
and is now an immutable object.
Before:
use League\Csv\Plugin\ColumnConsistencyValidator;
use League\Csv\Writer;
$validator = new ColumnConsistencyValidator();
$validator->autodetectColumnCount();
$csv = Writer::createFromPath('/path/to/file.csv');
$csv->addValidator($validator, 'columns_consistency');
After:
use League\Csv\ColumnConsistency;
use League\Csv\Writer;
$csv = Writer::createFromPath('/path/to/file.csv');
$csv->addValidator(new ColumnConsistency(), 'columns_consistency');