diff options
author | ignace nyamagana butera <nyamsprod@gmail.com> | 2015-11-23 10:39:59 +0100 |
---|---|---|
committer | ignace nyamagana butera <nyamsprod@gmail.com> | 2015-11-23 11:22:48 +0100 |
commit | 991e3b26fef334c2a21befb00a40f4c7e8fabf8b (patch) | |
tree | b1ab1808f4a608995baddb76e1b280f9662a8f51 | |
parent | 69bafa6ff924fbf9effe4275d6eb16be81a853ef (diff) | |
download | csv-991e3b26fef334c2a21befb00a40f4c7e8fabf8b.zip csv-991e3b26fef334c2a21befb00a40f4c7e8fabf8b.tar.gz csv-991e3b26fef334c2a21befb00a40f4c7e8fabf8b.tar.bz2 |
Introducing Reader::fetchPairs
In order to make League\Csv works better with huge
CSV files we introduce two modes where some Reader::fetch*
methods will return either an array or an iterator. The mode
is defined for a single query and is by default reset to returning
an array.
The mode are added with the introduction of Reader::fetchPairs to
returns data in an array of key-value pairs, as an associative array
with a single entry per row.
Adding Reader::fetchPairs requires dropping support for PHP5.4 as the
method requires the use of PHP 5.5+ generators.
The fetch* method callable function are also updated to ease their usage as
prior to Reader::fetchPairs all callable had the same signature. Now the signature
depends on the fetch* method.
-rw-r--r-- | .travis.yml | 3 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | composer.json | 2 | ||||
-rw-r--r-- | scrutinizer.yml | 2 | ||||
-rw-r--r-- | src/AbstractCsv.php | 18 | ||||
-rw-r--r-- | src/Config/Controls.php | 27 | ||||
-rw-r--r-- | src/Reader.php | 211 | ||||
-rw-r--r-- | test/ControlsTest.php | 19 | ||||
-rw-r--r-- | test/FactoryTest.php | 9 | ||||
-rw-r--r-- | test/ReaderTest.php | 175 |
10 files changed, 316 insertions, 152 deletions
diff --git a/.travis.yml b/.travis.yml index 5798f9d..4235445 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,6 @@ sudo: false matrix: include: - - php: 5.4 - env: - - COLLECT_COVERAGE=true - php: 5.5 env: - COLLECT_COVERAGE=true @@ -34,7 +34,7 @@ Full documentation can be found at [csv.thephpleague.com](http://csv.thephpleagu System Requirements ------- -You need **PHP >= 5.4.0** or **HHVM >= 3.2** and the `mbstring` extension to use `League\Csv` but the latest stable version of PHP/HHVM is recommended. +You need **PHP >= 5.5.0** and the `mbstring` extension to use `League\Csv` but the latest stable version of PHP/HHVM is recommended. Install ------- diff --git a/composer.json b/composer.json index 41cdf41..e23e011 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ } }, "scripts": { - "test": "vendor/bin/phpunit; vendor/bin/php-cs-fixer fix -v --diff --dry-run;" + "test": "phpunit; php-cs-fixer fix -v --diff --dry-run;" }, "extra": { "branch-alias": { diff --git a/scrutinizer.yml b/scrutinizer.yml index abd0a3d..a680ae9 100644 --- a/scrutinizer.yml +++ b/scrutinizer.yml @@ -20,4 +20,4 @@ checks: tools: external_code_coverage: timeout: 1200 - runs: 3 + runs: 2 diff --git a/src/AbstractCsv.php b/src/AbstractCsv.php index bd07537..54cb538 100644 --- a/src/AbstractCsv.php +++ b/src/AbstractCsv.php @@ -97,7 +97,7 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate * a path to a file * * @param SplFileObject|string $path The file path - * @param string $open_mode the file open mode flag + * @param string $open_mode The file open mode flag */ protected function __construct($path, $open_mode = 'r+') { @@ -172,7 +172,7 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate * @param mixed $path file path * @param string $open_mode the file open mode flag * - * @throws InvalidArgumentException If $path is a \SplTempFileObject object + * @throws InvalidArgumentException If $path is a SplTempFileObject object * * @return static */ @@ -203,7 +203,6 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate if (is_string($str) || (is_object($str) && method_exists($str, '__toString'))) { return (string) $str; } - throw new InvalidArgumentException('Expected data must be a string or stringable'); } @@ -237,8 +236,8 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate * The string must be an object that implements the `__toString` method, * or a string * - * @param string $str the string - * @param string $newline the newline character + * @param string|object $str the string + * @param string $newline the newline character * * @return static */ @@ -256,8 +255,8 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate /** * Creates a {@link AbstractCsv} instance from another {@link AbstractCsv} object * - * @param string $class_name the class to be instantiated - * @param string $open_mode the file open mode flag + * @param string $class the class to be instantiated + * @param string $open_mode the file open mode flag * * @return static */ @@ -285,7 +284,7 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate */ public function newWriter($open_mode = 'r+') { - return $this->newInstance('\League\Csv\Writer', $open_mode); + return $this->newInstance(Writer::class, $open_mode); } /** @@ -297,7 +296,7 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate */ public function newReader($open_mode = 'r+') { - return $this->newInstance('\League\Csv\Reader', $open_mode); + return $this->newInstance(Reader::class, $open_mode); } /** @@ -316,7 +315,6 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate if (false === ($int = filter_var($int, FILTER_VALIDATE_INT, ['options' => ['min_range' => $minValue]]))) { throw new InvalidArgumentException($errorMessage); } - return $int; } } diff --git a/src/Config/Controls.php b/src/Config/Controls.php index 5f2c362..3432974 100644 --- a/src/Config/Controls.php +++ b/src/Config/Controls.php @@ -103,33 +103,6 @@ trait Controls } /** - * Detects the CSV file delimiters - * - * Returns a associative array where each key represents - * the number of occurences and each value a delimiter with the - * given occurence - * - * This method returns incorrect informations when two delimiters - * have the same occurrence count - * - * DEPRECATION WARNING! This method will be removed in the next major point release - * - * @deprecated deprecated since version 7.2 - * - * @param int $nb_rows - * @param string[] $delimiters additional delimiters - * - * @return string[] - */ - public function detectDelimiterList($nb_rows = 1, array $delimiters = []) - { - $delimiters = array_merge([$this->delimiter, ',', ';', "\t"], $delimiters); - $stats = $this->fetchDelimitersOccurrence($delimiters, $nb_rows); - - return array_flip(array_filter($stats)); - } - - /** * Detect Delimiters occurences in the CSV * * Returns a associative array where each key represents diff --git a/src/Reader.php b/src/Reader.php index e422d31..23c0e90 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -12,7 +12,7 @@ */ namespace League\Csv; -use CallbackFilterIterator; +use Generator; use InvalidArgumentException; use Iterator; use League\Csv\Modifier\MapIterator; @@ -28,29 +28,52 @@ use SplFileObject; */ class Reader extends AbstractCsv { + const FETCH_ARRAY = 1; + + const FETCH_ITERATOR = 2; + /** * @inheritdoc */ protected $stream_filter_mode = STREAM_FILTER_READ; /** - * Returns a Filtered Iterator + * Reader fetch mode * - * DEPRECATION WARNING! This method will be removed in the next major point release + * @var int + */ + protected $fetchMode = self::FETCH_ARRAY; + + /** + * returns the current fetch mode * - * @deprecated deprecated since version 7.2 + * @return int + */ + public function getFetchMode() + { + return $this->fetchMode; + } + + /** + * Set the Reader fetch mode for the next call * - * @return Iterator + * @param static */ - public function query(callable $callable = null) + public function setFetchMode($mode) { - return $this->fetch($callable); + $modes = [static::FETCH_ARRAY => 1, static::FETCH_ITERATOR => 1]; + if (!isset($modes[$mode])) { + throw new InvalidArgumentException('Unknown return type mode'); + } + $this->fetchMode = $mode; + + return $this; } /** * Return a Filtered Iterator * - * @param callable $callable a callable function to be applied to each Iterator item + * @param callable|null $callable a callable function to be applied to each Iterator item * * @return Iterator */ @@ -72,6 +95,20 @@ class Reader extends AbstractCsv } /** + * Returns a sequential array of all CSV lines + * + * The callable function will be applied to each Iterator item + * + * @param callable $callable a callable function + * + * @return array + */ + public function fetchAll(callable $callable = null) + { + return iterator_to_array($this->fetch($callable), false); + } + + /** * Applies a callback function on the CSV * * The callback function must return TRUE in order to continue @@ -102,6 +139,8 @@ class Reader extends AbstractCsv /** * Returns a single row from the CSV * + * By default if no offset is provided the first row of the CSV is selected + * * @param int $offset * * @throws InvalidArgumentException If the $offset is not a valid Integer @@ -119,49 +158,120 @@ class Reader extends AbstractCsv } /** - * Returns a sequential array of all CSV lines + * Returns a single column from the CSV data * - * The callable function will be applied to each Iterator item + * The callable function will be applied to each value to be return * - * @param callable $callable a callable function + * By default if no column index is provided the first column of the CSV is selected * - * @return array + * @param int $column_index field Index + * @param callable|null $callable a callable function + * + * @throws InvalidArgumentException If the column index is not a positive integer or 0 + * + * @return Iterator|array */ - public function fetchAll(callable $callable = null) + public function fetchColumn($columnIndex = 0, callable $callable = null) { - return iterator_to_array($this->fetch($callable), false); + $this->assertValidColumnIndex($columnIndex); + + $filterColumn = function ($row) use ($columnIndex) { + return array_key_exists($columnIndex, $row); + }; + + $selectColumn = function ($row) use ($columnIndex) { + return $row[$columnIndex]; + }; + + $this->addFilter($filterColumn); + $iterator = $this->fetch($selectColumn); + if (!is_null($callable)) { + $iterator = new MapIterator($iterator, $callable); + } + + return $this->toArray($iterator, false); } /** - * Returns a single column from the CSV data + * Convert the Iterator into an array depending on the class fetchMode * - * The callable function will be applied to each value to be return + * @param Iterator $iterator + * @param bool $use_keys Whether to use the iterator element keys as index * - * @param int $column_index field Index - * @param callable $callable a callable function + * @return Iterator|array + */ + protected function toArray(Iterator $iterator, $use_keys = true) + { + if (static::FETCH_ARRAY == $this->fetchMode) { + return iterator_to_array($iterator, $use_keys); + } + $this->fetchMode = static::FETCH_ARRAY; + + return $iterator; + } + + /** + * Validate a CSV row index * - * @throws InvalidArgumentException If the column index is not a positive integer or 0 + * @param int $index * - * @return array + * @throws InvalidArgumentException If the column index is not a positive integer or 0 */ - public function fetchColumn($column_index = 0, callable $callable = null) + protected function assertValidColumnIndex($index) { - if (false === filter_var($column_index, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]])) { - throw new InvalidArgumentException( - 'the column index must be a positive integer or 0' - ); + if (false === filter_var($index, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]])) { + throw new InvalidArgumentException('the column index must be a positive integer or 0'); } - $filterColumn = function ($row) use ($column_index) { - return array_key_exists($column_index, $row); + } + + /** + * Retrive CSV data as pairs + * + * Fetches an associative array of all rows as key-value pairs (first + * column is the key, second column is the value). + * + * By default if no column index is provided: + * - the first CSV column is used to provide the keys + * - the second CSV column is used to provide the value + * + * @param int $offsetColumnIndex The column index to server as offset + * @param int $valueColumnIndex The column index to server as value + * @param callable|null $callable Callback function to run for each element in each array + * + * @return Generator|array + */ + public function fetchPairs($offsetColumnIndex = 0, $valueColumnIndex = 1, callable $callable = null) + { + $this->assertValidColumnIndex($offsetColumnIndex); + $this->assertValidColumnIndex($valueColumnIndex); + $filterPairs = function ($row) use ($offsetColumnIndex, $valueColumnIndex) { + return array_key_exists($offsetColumnIndex, $row) && array_key_exists($valueColumnIndex, $row); }; - $selectColumn = function ($row) use ($column_index) { - return $row[$column_index]; + $selectPairs = function ($row) use ($offsetColumnIndex, $valueColumnIndex) { + return [$row[$offsetColumnIndex], $row[$valueColumnIndex]]; }; + $this->addFilter($filterPairs); + $iterator = $this->fetch($selectPairs); - $iterator = $this->fetch($callable); - $iterator = new CallbackFilterIterator($iterator, $filterColumn); + if (!is_null($callable)) { + $iterator = new MapIterator($iterator, $callable); + } - return iterator_to_array(new MapIterator($iterator, $selectColumn), false); + return $this->toArray($this->fetchPairsGenerator($iterator), true); + } + + /** + * Return the key/pairs as a PHP generator + * + * @param Iterator $iterator + * + * @return Generator + */ + protected function fetchPairsGenerator(Iterator $iterator) + { + foreach ($iterator as $row) { + yield $row[0] => $row[1]; + } } /** @@ -177,7 +287,7 @@ class Reader extends AbstractCsv * * @throws InvalidArgumentException If the submitted keys are invalid * - * @return Iterator + * @return Iterator|array */ public function fetchAssoc($offset_or_keys = 0, callable $callable = null) { @@ -191,7 +301,7 @@ class Reader extends AbstractCsv return array_combine($keys, $row); }; - return iterator_to_array(new MapIterator($this->fetch($callable), $combineArray), false); + return $this->toArray(new MapIterator($this->fetch($callable), $combineArray), false); } /** @@ -207,7 +317,9 @@ class Reader extends AbstractCsv protected function getAssocKeys($offset_or_keys) { if (is_array($offset_or_keys)) { - return $this->validateAssocKeys($offset_or_keys); + $this->assertValidAssocKeys($offset_or_keys); + + return $offset_or_keys; } if (false === filter_var($offset_or_keys, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]])) { @@ -215,7 +327,7 @@ class Reader extends AbstractCsv } $keys = $this->getRow($offset_or_keys); - $keys = $this->validateAssocKeys($keys); + $this->assertValidAssocKeys($keys); $filterOutRow = function ($row, $rowIndex) use ($offset_or_keys) { return is_array($row) && $rowIndex != $offset_or_keys; }; @@ -231,22 +343,23 @@ class Reader extends AbstractCsv * * @throws InvalidArgumentException If the submitted array fails the assertion */ - protected function validateAssocKeys(array $keys) + protected function assertValidAssocKeys(array $keys) { - if (empty($keys)) { - throw new InvalidArgumentException('The array can not be empty'); - } - - foreach ($keys as &$str) { - $str = $this->validateString($str); - } - unset($str); - - if ($keys == array_unique($keys)) { - return $keys; + if (empty($keys) || $keys !== array_unique(array_filter($keys, [$this, 'isValidString']))) { + throw new InvalidArgumentException('Use a flat array with unique string values'); } + } - throw new InvalidArgumentException('The array must contain unique values'); + /** + * Returns whether the submitted value can be used as string + * + * @param mixed $value + * + * @return bool + */ + protected function isValidString($value) + { + return is_scalar($value) || (is_object($value) && method_exists($value, '__toString')); } /** diff --git a/test/ControlsTest.php b/test/ControlsTest.php index 4eebed7..35f6fe8 100644 --- a/test/ControlsTest.php +++ b/test/ControlsTest.php @@ -1,9 +1,10 @@ <?php -namespace League\Csv\Test; +namespace League\Csv\test; use League\Csv\Reader; use League\Csv\Writer; +use PHPUnit_Framework_TestCase; use SplFileObject; use SplTempFileObject; @@ -12,7 +13,7 @@ date_default_timezone_set('UTC'); /** * @group controls */ -class ControlsTest extends AbstractTestCase +class ControlsTest extends PHPUnit_Framework_TestCase { private $csv; @@ -93,7 +94,7 @@ class ControlsTest extends AbstractTestCase public function testDetectDelimiterList() { - $this->assertSame([4 => ','], $this->csv->detectDelimiterList()); + $this->assertSame([',' => 4], $this->csv->fetchDelimitersOccurrence([','])); } /** @@ -102,7 +103,7 @@ class ControlsTest extends AbstractTestCase */ public function testDetectDelimiterListWithInvalidRowLimit() { - $this->csv->detectDelimiterList(-4); + $this->csv->fetchDelimitersOccurrence([','], -4); } public function testDetectDelimiterListWithNoCSV() @@ -110,7 +111,7 @@ class ControlsTest extends AbstractTestCase $file = new SplTempFileObject(); $file->fwrite("How are you today ?\nI'm doing fine thanks!"); $csv = Writer::createFromFileObject($file); - $this->assertSame([], $csv->detectDelimiterList(5, ['toto', '|'])); + $this->assertSame(['|' => 0], $csv->fetchDelimitersOccurrence(['toto', '|'], 5)); } public function testDetectDelimiterListWithInconsistentCSV() @@ -124,7 +125,7 @@ class ControlsTest extends AbstractTestCase $data->fputcsv(['toto', 'tata', 'tutu']); $csv = Writer::createFromFileObject($data); - $this->assertSame([12 => '|', 4 => ';'], $csv->detectDelimiterList(5, ['|'])); + $this->assertSame(['|' => 12, ';' => 4], $csv->fetchDelimitersOccurrence(['|', ';'], 5)); } /** @@ -192,12 +193,14 @@ class ControlsTest extends AbstractTestCase * @param $flag * @param $line_count * @dataProvider appliedFlagsProvider - * @skipIfHHVM */ public function testAppliedFlags($flag, $line_count) { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM CSV parsing is different'); + } $path = __DIR__.'/data/tmp.txt'; - $obj = new SplFileObject($path, 'w+'); + $obj = new SplFileObject($path, 'w+'); $obj->fwrite("1st\n2nd\n"); $reader = Reader::createFromFileObject($obj); $reader->setFlags($flag); diff --git a/test/FactoryTest.php b/test/FactoryTest.php index e35b402..377b1b9 100644 --- a/test/FactoryTest.php +++ b/test/FactoryTest.php @@ -2,7 +2,6 @@ namespace League\Csv\Test; -use DateTime; use League\Csv\Reader; use SplFileInfo; use SplFileObject; @@ -42,14 +41,6 @@ class FactoryTest extends AbstractTestCase Reader::createFromPath(new SplTempFileObject()); } - /** - * @expectedException InvalidArgumentException - */ - public function testCreateFromPathWithUnStringableObject() - { - Reader::createFromPath(new DateTime()); - } - public function testCreateFromString() { $expected = 'john,doe,john.doe@example.com'.PHP_EOL diff --git a/test/ReaderTest.php b/test/ReaderTest.php index d650c51..4535dce 100644 --- a/test/ReaderTest.php +++ b/test/ReaderTest.php @@ -1,14 +1,16 @@ <?php -namespace League\Csv\Test; +namespace League\Csv\test; use League\Csv\Reader; +use League\Csv\Writer; +use PHPUnit_Framework_TestCase; use SplTempFileObject; /** * @group reader */ -class ReaderTest extends AbstractTestCase +class ReaderTest extends PHPUnit_Framework_TestCase { private $csv; @@ -151,10 +153,21 @@ class ReaderTest extends AbstractTestCase $this->assertContains(['JANE', 'DOE', 'JANE.DOE@EXAMPLE.COM'], $res); } - public function testFetchAssoc() + public function testFetchAssocReturnsArray() { $keys = ['firstname', 'lastname', 'email']; $res = $this->csv->fetchAssoc($keys); + $this->assertInternalType('array', $res); + foreach ($res as $offset => $row) { + $this->assertSame($keys, array_keys($row)); + } + } + + public function testFetchAssocReturnsIterator() + { + $keys = ['firstname', 'lastname', 'email']; + $res = $this->csv->setFetchMode(Reader::FETCH_ITERATOR)->fetchAssoc($keys); + $this->assertInstanceof('\Iterator', $res); foreach ($res as $offset => $row) { $this->assertSame($keys, array_keys($row)); } @@ -216,15 +229,6 @@ class ReaderTest extends AbstractTestCase } /** - * @expectedException \InvalidArgumentException - */ - public function testFetchAssocThrowsExceptionWithNonUniqueAssocKeys() - { - $keys = ['firstname', 'lastname', 'firstname']; - $this->csv->fetchAssoc($keys); - } - - /** * @param $expected * @dataProvider validBOMSequences */ @@ -364,21 +368,19 @@ class ReaderTest extends AbstractTestCase ]; } - public function testFetchCol() + public function testFetchColumn() { - $this->csv->addFilter(function ($row) { - return $row != [null]; - - }); - $this->assertSame(['john', 'jane'], $this->csv->fetchColumn(0)); - $this->csv->addFilter(function ($row) { - return $row != [null]; + $this->assertContains('john', $this->csv->fetchColumn(0)); + $this->assertContains('jane', $this->csv->fetchColumn()); + } - }); - $this->assertSame(['john', 'jane'], $this->csv->fetchColumn()); + public function testFetchColumnReturnsIterator() + { + $this->assertContains('john', $this->csv->setFetchMode(Reader::FETCH_ITERATOR)->fetchColumn(0)); + $this->assertContains('jane', $this->csv->setFetchMode(Reader::FETCH_ITERATOR)->fetchColumn()); } - public function testFetchColInconsistentColumnCSV() + public function testFetchColumnInconsistentColumnCSV() { $raw = [ ['john', 'doe'], @@ -390,16 +392,11 @@ class ReaderTest extends AbstractTestCase $file->fputcsv($row); } $csv = Reader::createFromFileObject($file); - $this->csv->addFilter(function ($row) { - return $row != [null]; - - }); $res = $csv->fetchColumn(2); - $this->assertInternalType('array', $res); $this->assertCount(1, $res); } - public function testFetchColEmptyCol() + public function testFetchColumnEmptyCol() { $raw = [ ['john', 'doe'], @@ -412,31 +409,23 @@ class ReaderTest extends AbstractTestCase } $csv = Reader::createFromFileObject($file); $res = $csv->fetchColumn(2); - $this->csv->addFilter(function ($row) { - return $row != [null]; - - }); - $this->assertInternalType('array', $res); $this->assertCount(0, $res); } - public function testFetchColCallback() + public function testFetchColumnCallback() { $func = function ($value) { - return array_map('strtoupper', $value); + return strtoupper($value); }; - $this->csv->addFilter(function ($row) { - return $row != [null]; - - }); - $this->assertSame(['JOHN', 'JANE'], $this->csv->fetchColumn(0, $func)); + $iterator = $this->csv->fetchColumn(0, $func); + $this->assertSame(['JOHN', 'JANE'], $iterator); } /** * @expectedException \InvalidArgumentException */ - public function testFetchColFailure() + public function testFetchColumnFailure() { $this->csv->fetchColumn('toto'); } @@ -482,6 +471,106 @@ class ReaderTest extends AbstractTestCase public function testGetWriter() { - $this->assertInstanceOf('\League\Csv\Writer', $this->csv->newWriter()); + $this->assertInstanceOf(Writer::class, $this->csv->newWriter()); + } + + /** + * @dataProvider fetchPairsDataProvider + */ + public function testFetchPairsIteratorMode($key, $value, $callable, $expected) + { + $iterator = $this->csv->setFetchMode(Reader::FETCH_ITERATOR)->fetchPairs($key, $value, $callable); + foreach ($iterator as $key => $value) { + $res = current($expected); + $this->assertSame($value, $res[$key]); + next($expected); + } + } + + public function fetchPairsDataProvider() + { + return [ + 'default values' => [ + 'key' => 0, + 'value' => 1, + 'callable' => null, + 'expected' => [ + ['john' => 'doe'], + ['jane' => 'doe'], + ], + ], + 'changed key order' => [ + 'key' => 1, + 'value' => 0, + 'callable' => null, + 'expected' => [ + ['doe' => 'john'], + ['doe' => 'jane'], + ], + ], + 'with callback' => [ + 'key' => 0, + 'value' => 1, + 'callable' => function ($row) { + return [ + strtoupper($row[0]), + strtoupper($row[1]), + ]; + }, + 'expected' => [ + ['JOHN' => 'DOE'], + ['JANE' => 'DOE'], + ], + ], + ]; + } + + /** + * @dataProvider fetchPairsArrayDataProvider + */ + public function testFetchPairsArrayMode($key, $value, $callable, $expected) + { + $array = $this->csv->fetchPairs($key, $value, $callable); + $this->assertSame($expected, $array); + } + + public function fetchPairsArrayDataProvider() + { + return [ + 'default values' => [ + 'key' => 0, + 'value' => 1, + 'callable' => null, + 'expected' => ['john' => 'doe', 'jane' => 'doe'], + ], + 'changed key order' => [ + 'key' => 1, + 'value' => 0, + 'callable' => null, + 'expected' => ['doe' => 'jane'], + ], + 'with callback' => [ + 'key' => 0, + 'value' => 1, + 'callable' => function ($row) { + return [ + strtoupper($row[0]), + strtoupper($row[1]), + ]; + }, + 'expected' => ['JOHN' => 'DOE', 'JANE' => 'DOE'], + ], + ]; + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFetchMode() + { + $this->assertSame(Reader::FETCH_ARRAY, $this->csv->getFetchMode()); + $this->csv->setFetchMode(Reader::FETCH_ITERATOR); + $this->assertSame(Reader::FETCH_ITERATOR, $this->csv->getFetchMode()); + $this->csv->setFetchMode('toto'); } } |