From 4b6f1b1a8753a70c5f0a1b06d6baad86c01e6a1a Mon Sep 17 00:00:00 2001 From: Ignace Nyamagana Butera Date: Fri, 4 Aug 2017 14:58:43 +0200 Subject: [PATCH] Exceptions handling simplify - All CSV related exceptions are throw using League\Csv\Exception - All non related exceptions (Converters) are thrown using SPL Exceptions or DOM Exception --- .travis.yml | 4 + CHANGELOG.md | 8 +- docs/9.0/connections/controls.md | 2 +- docs/9.0/connections/exceptions.md | 123 ------------------ docs/9.0/connections/filters.md | 2 +- docs/9.0/connections/index.md | 19 +++ docs/9.0/connections/instantiation.md | 2 +- docs/9.0/converter/index.md | 6 +- docs/9.0/converter/xml.md | 2 +- docs/9.0/reader/index.md | 6 +- docs/9.0/reader/resultset.md | 12 +- docs/9.0/writer/helpers.md | 6 +- docs/9.0/writer/index.md | 37 +++++- docs/_data/menu.yml | 1 - src/AbstractCsv.php | 12 +- ...onException.php => CannotInsertRecord.php} | 8 +- src/CharsetConverter.php | 5 +- src/ColumnConsistency.php | 4 +- src/Exception.php | 2 +- src/Exception/LengthException.php | 29 ----- src/Exception/LogicException.php | 29 ----- src/Exception/OutOfRangeException.php | 29 ----- src/Exception/RuntimeException.php | 29 ----- src/Reader.php | 12 +- src/ResultSet.php | 21 ++- src/Statement.php | 9 +- src/Stream.php | 22 ++-- src/ValidatorTrait.php | 12 +- src/Writer.php | 9 +- tests/CharsetConverterTest.php | 6 +- tests/ColumnConsistencyTest.php | 16 +-- tests/CsvTest.php | 37 +++--- tests/DetectDelimiterTest.php | 4 +- tests/ReaderTest.php | 14 +- tests/ResultSetTest.php | 17 +-- tests/StreamTest.php | 13 +- tests/WriterTest.php | 12 +- 37 files changed, 180 insertions(+), 401 deletions(-) delete mode 100644 docs/9.0/connections/exceptions.md rename src/{Exception/InsertionException.php => CannotInsertRecord.php} (89%) delete mode 100644 src/Exception/LengthException.php delete mode 100644 src/Exception/LogicException.php delete mode 100644 src/Exception/OutOfRangeException.php delete mode 100644 src/Exception/RuntimeException.php diff --git a/.travis.yml b/.travis.yml index 62d7750b..66a67468 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,10 @@ matrix: env: COLLECT_COVERAGE=true VALIDATE_CODING_STYLE=false - php: 7.1 env: COLLECT_COVERAGE=true VALIDATE_CODING_STYLE=true + - php: master + env: COLLECT_COVERAGE=true VALIDATE_CODING_STYLE=false + allow_failures: + - php: master fast_finish: true cache: diff --git a/CHANGELOG.md b/CHANGELOG.md index dac7d0eb..708b3ed0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,12 +20,8 @@ All Notable changes to `Csv` will be documented in this file - `League\Csv\XMLConverter` converts CSV records into DOMDocument - `League\Csv\HTMLConverter` converts CSV records into HTML table. - Improved Exception handling - - `League\Csv\Exception` the default exception interface - - `League\Csv\Exception\InsertionException` - - `League\Csv\Exception\LengthException` - - `League\Csv\Exception\LogicException` - - `League\Csv\Exception\OutOfRangeException` - - `League\Csv\Exception\RuntimeException` + - `League\Csv\Exception` the default exception + - `League\Csv\Exception\CannotInsertRecord` - Improved CSV document output - `League\Csv\AbstractCsv::chunk` method to output the CSV document in chunk - `League\Csv\bom_match` function to detect BOM sequence in a given string diff --git a/docs/9.0/connections/controls.md b/docs/9.0/connections/controls.md index 1bf3eb41..5e2458f2 100644 --- a/docs/9.0/connections/controls.md +++ b/docs/9.0/connections/controls.md @@ -7,7 +7,7 @@ title: Csv character controls To correctly parse a CSV document you are required to set the character controls to be used by the `Reader` or the `Writer` object. -

On error the setter methods will throw a LengthException exception if the submitted string length is not equal to 1.

+

On error the setter methods will throw a Exception exception if the submitted string length is not equal to 1.

## The delimiter character. diff --git a/docs/9.0/connections/exceptions.md b/docs/9.0/connections/exceptions.md deleted file mode 100644 index ba9f2c47..00000000 --- a/docs/9.0/connections/exceptions.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -layout: default -title: Exceptions ---- - -# Exceptions - -Specific exceptions are thrown for errors occuring while using the library. - -## Default interface - -All exceptions thrown implements the `League\Csv\Exception` interface. - -~~~php -setDelimiter('toto'); -} catch (Exception $e) { - echo $e->getMessage(), PHP_EOL; -} -~~~ - -## Logic exceptions - -### While setting properties - -- A `LengthException` exception is triggered by the CSV character control methods if the submitted character length is not equal to `1`. -- A `OutOfRangeException` exception is triggered by the library if the submitted integer is not an acceptable value for a given method. -- A `RuntimeException` exception can also be triggered if the filename cannot be opened like [SplFileObject](http://php.net/manual/en/splfileobject.construct.php). - -~~~php -setDelimiter('toto'); //may trigger a LengthException -} catch (LengthException $e) { - echo $e->getMessage(), PHP_EOL; -} catch (RuntimeException $e) { - echo $e->getMessage(), PHP_EOL; -} - -// in PHP 7.1 - -try { - $csv = Reader::createFromPath('/path/to/file.csv'); - $csv->setDelimiter('toto'); -} catch (LengthException | RuntimeException $e) { - echo $e->getMessage(), PHP_EOL; -} -~~~ - -### While using PHP stream support - -If you try to use PHP stream filtering features on an CSV objects which can't use them, a `LogicException` is triggered. - -~~~php -addStreamFilter('string.toupper'); -} catch (LogicException $e) { - echo $e->getMessage(), PHP_EOL; -} -~~~ - -## Runtime exceptions - -### While manipulating records - -During reading or writing records `RuntimeException` exception can be thrown if an error occurs. - -~~~php -setHeaderOffset(1000); //valid offset but the CSV does not contain 1000 records - $header = $csv->getHeader(); //triggers a Exception -} catch (RuntimeException $e) { - echo $e->getMessage(), PHP_EOL; -} -~~~ - -### While inserting records - -On error when using the `Writer` class to add new records to your CSV document a `League\Csv\Exception\InsertionException` is thrown. This exception class - -- extends PHP SPL's `RuntimeException` -- implements `League\Csv\Exception` -- provides additional public methods: - - `InsertionException::getData` which returns the record responsible for triggering the exception. - - `InsertionException::getName` which returns the validator registered name which triggered the exception or an empty string otherwise. - -~~~php -insertOne(['john', ['doe'], 'john.doe@example.com']); -} catch (InsertionException $e) { - echo $e->getName(); //display '' - $e->getData();//will return the invalid data ['john', ['doe'], 'john.doe@example.com'] -} -~~~ \ No newline at end of file diff --git a/docs/9.0/connections/filters.md b/docs/9.0/connections/filters.md index 5eab1eb5..9d252f67 100644 --- a/docs/9.0/connections/filters.md +++ b/docs/9.0/connections/filters.md @@ -39,7 +39,7 @@ $writer->supportsStreamFilter(); //return false the API can not be use $writer->getStreamFilterMode(); //return STREAM_FILTER_WRITE ~~~ -

A LogicException exception will be thrown if you use the API on a object where supportsStreamFilter returns false.

+

A League\Csv\Exception exception will be thrown if you use the API on a object where supportsStreamFilter returns false.

### Cheat sheet diff --git a/docs/9.0/connections/index.md b/docs/9.0/connections/index.md index 7b1861ea..9bdda16b 100644 --- a/docs/9.0/connections/index.md +++ b/docs/9.0/connections/index.md @@ -61,3 +61,22 @@ if (!ini_get("auto_detect_line_endings")) { //the rest of the code continues here... ~~~ + +## Exceptions + +The default exception class thrown while using this library is `League\Csv\Exception` which extends PHP `Exception` class. + +~~~php +setDelimiter('toto'); +} catch (Exception $e) { + echo $e->getMessage(), PHP_EOL; +} +~~~ + diff --git a/docs/9.0/connections/instantiation.md b/docs/9.0/connections/instantiation.md index f4843631..8cd79eae 100644 --- a/docs/9.0/connections/instantiation.md +++ b/docs/9.0/connections/instantiation.md @@ -74,7 +74,7 @@ $reader = Reader::createFromStream(fopen('/path/to/the/file.csv', 'r+')); $writer = Writer::createFromStream(tmpfile()); ~~~ -

The resource stream MUST be seekable otherwise an RuntimeException exception is thrown.

+

The resource stream MUST be seekable otherwise an League\Csv\Exception exception is thrown.

## Loading from a SplFileObject object diff --git a/docs/9.0/converter/index.md b/docs/9.0/converter/index.md index 1bd40baa..3e71a309 100644 --- a/docs/9.0/converter/index.md +++ b/docs/9.0/converter/index.md @@ -19,4 +19,8 @@ The package provides classes which convert any collection of CSV records into: Before conversion, you may want to configure your converter object. Each provided converter exposes additional methods to correctly convert your records. -When building a converter object, the methods do not need to be called in any particular order, and may be called multiple times. Because all provided converters are immutable, each time their setter methods are called they will return a new object without modifying the current one. \ No newline at end of file +When building a converter object, the methods do not need to be called in any particular order, and may be called multiple times. Because all provided converters are immutable, each time their setter methods are called they will return a new object without modifying the current one. + +## Converters exceptions + +Because converters do not directly deals with CSV document but with their contents. On error theses classes trigger PHP's Exceptions instead of `League\Csv\Exception` exception. \ No newline at end of file diff --git a/docs/9.0/converter/xml.md b/docs/9.0/converter/xml.md index 2e91f005..f308cb06 100644 --- a/docs/9.0/converter/xml.md +++ b/docs/9.0/converter/xml.md @@ -23,7 +23,7 @@ class XMLConverter Prior to converting your records collection into XML, you may wish to configure the element and its associated attribute names. To do so `XMLConverter` provides methods to setup theses settings. -

Because we are building a DOMDocument object, the XMLConverter object throws DOMException exceptions that do not implements CsvException.

+

Because we are building a DOMDocument object, the XMLConverter object throws DOMException insted of League\Csv\Exception.

### XMLConverter::rootElement diff --git a/docs/9.0/reader/index.md b/docs/9.0/reader/index.md index 06eb1e8e..c4f4f35e 100644 --- a/docs/9.0/reader/index.md +++ b/docs/9.0/reader/index.md @@ -68,7 +68,7 @@ If no header offset is set:

By default no header offset is set.

-

Because the header is lazy loaded, if you provide a positive offset for an invalid record a RuntimeException will be triggered when trying to access the invalid record.

+

Because the header is lazy loaded, if you provide a positive offset for an invalid record a Exception exception will be triggered when trying to access the invalid record.

~~~php setHeaderOffset(1000); //valid offset but the CSV does not contain 1000 records $header_offset = $csv->getHeaderOffset(); //returns 1000 -$header = $csv->getHeader(); //triggers a RuntimeException exception +$header = $csv->getHeader(); //triggers a Exception exception ~~~ ## CSV records @@ -183,7 +183,7 @@ foreach ($records as $offset => $record) { //the first record will still be skip!! ~~~ -

In both cases, if the header record contains non unique string values, a RuntimeException exception is triggered.

+

In both cases, if the header record contains non unique string values, a Exception exception is triggered.

### Using the IteratorAggregate interface diff --git a/docs/9.0/reader/resultset.md b/docs/9.0/reader/resultset.md index 45d161a8..e1403c88 100644 --- a/docs/9.0/reader/resultset.md +++ b/docs/9.0/reader/resultset.md @@ -231,7 +231,7 @@ count(iterator_to_array($records->fetchColumn(2), false)); //returns 5 //5 records were skipped because the column value is null ~~~ -

If the ResultSet contains column names and the $columnIndex is not found a RuntimeException is thrown.

+

If the ResultSet contains column names and the $columnIndex is not found an Exception exception is thrown.

~~~php setHeaderOffset(0); $records = (new Statement())->process($reader); foreach ($records->fetchColumn('foobar') as $record) { - //throw an RuntimeException if + //throw an Exception exception if //no `foobar` column name is found //in $records->getHeader() result } @@ -308,18 +308,12 @@ foreach ($records->fetchPairs() as $firstname => $lastname) { - If no cell is found corresponding to `$offsetIndex` the row is skipped; - If no cell is found corresponding to `$valueIndex` the `null` value is used; -

If the ResultSet contains column names and the submitted arguments are not found a RuntimeException is thrown.

+

If the ResultSet contains column names and the submitted arguments are not found an Exception exception is thrown.

## Conversions ### Json serialization -~~~php -addValidator($validator, 'column_consistency'); $validator->getColumnCount(); //returns -1 $writer->insertOne(["foo", "bar", "baz"]); $validator->getColumnCount(); //returns 3 -$writer->insertOne(["foo", "bar"]); //will trigger a InsertionException exception +$writer->insertOne(["foo", "bar"]); //will trigger a CannotInsertRecord exception ~~~

The default column count is set to -1.

diff --git a/docs/9.0/writer/index.md b/docs/9.0/writer/index.md index 5b8027d7..900f6d44 100644 --- a/docs/9.0/writer/index.md +++ b/docs/9.0/writer/index.md @@ -34,7 +34,7 @@ public Writer::insertOne(array $record): int public Writer::insertAll(iterable $records): int ~~~ -`Writer::insertOne` inserts a single record into the CSV document while `Writer::insertAll` adds several records. Both methods returns the length of the written data or throw an [League\Csv\Exception\InsertionException](/9.0/connections/exceptions/#runtime-exceptions) on error. +`Writer::insertOne` inserts a single record into the CSV document while `Writer::insertAll` adds several records. Both methods returns the length of the written data. `Writer::insertOne` takes a single argument, an `array` which represents a single CSV record. `Writer::insertAll` takes a single argument a PHP iterable which contains a collection of CSV records. @@ -58,6 +58,28 @@ $writer->insertAll(new ArrayIterator($records)); //using a Traversable object In the above example, all CSV records are saved to `/path/to/saved/file.csv` +If the record can not be inserted into the CSV document a `League\Csv\CannotInsertRecord` exception is thrown. This exception extends `League\Csv\Exception` and adds the ability to get the record on which the insertion failed. + +~~~php +insertAll($records); +} catch (CannotInsertRecord $e) { + $e->getRecords(); //returns [1, 2, 3] +} +~~~ + ## Handling newline Because PHP's `fputcsv` implementation has a hardcoded `\n`, we need to be able to replace the last `LF` code with one supplied by the developper for more interoperability between CSV packages on different platforms. The newline sequence will be appended to each newly inserted CSV record. @@ -165,7 +187,7 @@ function(array $record): bool The validator **must** return `true` to validate the submitted record. -Any other expression, including thruthy ones like `yes`, `1`,... will make the `insertOne` method throw an `League\Csv\Exception\InsertionException`. +Any other expression, including thruthy ones like `yes`, `1`,... will make the `insertOne` method throw an `League\Csv\CannotInsertRecord`. #### Adding a Validator to a Writer object @@ -178,20 +200,25 @@ As with the formatter capabilities, you can attach as many validators as you wan - A validator `callable`; - A validator name. If another validator was already registered with the given name, it will be overriden. -On failure a [League\Csv\Exception\InsertionException](/9.0/connections/exceptions/#runtime-exceptions) exception is thrown by the `Writer` object. +On failure a `League\Csv\CannotInsertRecord` exception is thrown. +This exception will give access to: + +- the validator name; +- the record which failed the validation; ~~~php addValidator(function (array $row): bool { return 10 == count($row); }, 'row_must_contain_10_cells'); + try { $writer->insertOne(['john', 'doe', 'john.doe@example.com']); -} catch (InsertionException $e) { +} catch (CannotInsertRecord $e) { echo $e->getName(); //display 'row_must_contain_10_cells' $e->getData();//will return the invalid data ['john', 'doe', 'john.doe@example.com'] } diff --git a/docs/_data/menu.yml b/docs/_data/menu.yml index d4d3040c..a37fd25c 100644 --- a/docs/_data/menu.yml +++ b/docs/_data/menu.yml @@ -10,7 +10,6 @@ version: BOM Sequences: '/9.0/connections/bom/' Stream Filters: '/9.0/connections/filters/' Document output: '/9.0/connections/output/' - Exceptions: '/9.0/connections/exceptions/' Inserting Records: Writer Connection: '/9.0/writer/' Bundled Helpers: '/9.0/writer/helpers/' diff --git a/src/AbstractCsv.php b/src/AbstractCsv.php index d2a08449..dbfb79b9 100644 --- a/src/AbstractCsv.php +++ b/src/AbstractCsv.php @@ -15,8 +15,6 @@ namespace League\Csv; use Generator; -use League\Csv\Exception\LogicException; -use League\Csv\Exception\OutOfRangeException; use SplFileObject; use function League\Csv\bom_match; @@ -105,7 +103,7 @@ protected function __construct($document) */ public function __destruct() { - $this->document = null; + unset($this->document); } /** @@ -113,7 +111,7 @@ public function __destruct() */ public function __clone() { - throw new LogicException(sprintf('An object of class %s cannot be cloned', get_class($this))); + throw new Exception(sprintf('An object of class %s cannot be cloned', get_class($this))); } /** @@ -282,7 +280,7 @@ public function __toString(): string public function chunk(int $length): Generator { if ($length < 1) { - throw new OutOfRangeException(sprintf('%s() expects the length to be a positive integer %d given', __METHOD__, $length)); + throw new Exception(sprintf('%s() expects the length to be a positive integer %d given', __METHOD__, $length)); } $input_bom = $this->getInputBOM(); @@ -400,14 +398,14 @@ public function setOutputBOM(string $str): self * @param string $filtername a string or an object that implements the '__toString' method * @param mixed $params additional parameters for the filter * - * @throws LogicException If the stream filter API can not be used + * @throws Exception If the stream filter API can not be used * * @return static */ public function addStreamFilter(string $filtername, $params = null): self { if (!$this->document instanceof Stream) { - throw new LogicException('The stream filter API can not be used'); + throw new Exception('The stream filter API can not be used'); } $this->document->appendFilter($filtername, $this->stream_filter_mode, $params); diff --git a/src/Exception/InsertionException.php b/src/CannotInsertRecord.php similarity index 89% rename from src/Exception/InsertionException.php rename to src/CannotInsertRecord.php index 0552bfea..80dde1d5 100644 --- a/src/Exception/InsertionException.php +++ b/src/CannotInsertRecord.php @@ -12,7 +12,7 @@ */ declare(strict_types=1); -namespace League\Csv\Exception; +namespace League\Csv; /** * Thrown when a data is not added to the Csv Document @@ -22,7 +22,7 @@ * @author Ignace Nyamagana Butera * */ -class InsertionException extends RuntimeException +class CannotInsertRecord extends Exception { /** * The record submitted for insertion @@ -45,7 +45,7 @@ class InsertionException extends RuntimeException * * @return self */ - public static function createFromStream(array $record): self + public static function triggerOnInsertion(array $record): self { $exception = new static('Unable to write record to the CSV document'); $exception->record = $record; @@ -61,7 +61,7 @@ public static function createFromStream(array $record): self * * @return self */ - public static function createFromValidator(string $name, array $record): self + public static function triggerOnValidation(string $name, array $record): self { $exception = new static('Record validation failed'); $exception->name = $name; diff --git a/src/CharsetConverter.php b/src/CharsetConverter.php index c477f234..1bc665e5 100644 --- a/src/CharsetConverter.php +++ b/src/CharsetConverter.php @@ -14,9 +14,8 @@ namespace League\Csv; -use League\Csv\Exception\OutOfRangeException; +use OutOfRangeException; use php_user_filter; -use Throwable; use Traversable; use TypeError; @@ -149,7 +148,7 @@ public function onCreate() $this->input_encoding = $this->filterEncoding($matches['input']); $this->output_encoding = $this->filterEncoding($matches['output']); return true; - } catch (Throwable $e) { + } catch (OutOfRangeException $e) { return false; } } diff --git a/src/ColumnConsistency.php b/src/ColumnConsistency.php index 4a8b490a..4f8cd1cc 100644 --- a/src/ColumnConsistency.php +++ b/src/ColumnConsistency.php @@ -14,8 +14,6 @@ namespace League\Csv; -use League\Csv\Exception\OutOfRangeException; - /** * A class to manage column consistency on data insertion into a CSV * @@ -43,7 +41,7 @@ class ColumnConsistency public function __construct(int $columns_count = -1) { if ($columns_count < -1) { - throw new OutOfRangeException(sprintf('%s() expects the column count to be greater or equal to -1 %s given', __METHOD__, $columns_count)); + throw new Exception(sprintf('%s() expects the column count to be greater or equal to -1 %s given', __METHOD__, $columns_count)); } $this->columns_count = $columns_count; diff --git a/src/Exception.php b/src/Exception.php index 839aa169..91635215 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -22,6 +22,6 @@ * @author Ignace Nyamagana Butera * */ -interface Exception +class Exception extends \Exception { } diff --git a/src/Exception/LengthException.php b/src/Exception/LengthException.php deleted file mode 100644 index dd532322..00000000 --- a/src/Exception/LengthException.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - */ -class LengthException extends \LengthException implements Exception -{ -} diff --git a/src/Exception/LogicException.php b/src/Exception/LogicException.php deleted file mode 100644 index 602323f3..00000000 --- a/src/Exception/LogicException.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - */ -class LogicException extends \LogicException implements Exception -{ -} diff --git a/src/Exception/OutOfRangeException.php b/src/Exception/OutOfRangeException.php deleted file mode 100644 index 33c0de70..00000000 --- a/src/Exception/OutOfRangeException.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - */ -class OutOfRangeException extends \OutOfRangeException implements Exception -{ -} diff --git a/src/Exception/RuntimeException.php b/src/Exception/RuntimeException.php deleted file mode 100644 index cedf311a..00000000 --- a/src/Exception/RuntimeException.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - */ -class RuntimeException extends \RuntimeException implements Exception -{ -} diff --git a/src/Reader.php b/src/Reader.php index d2fbf3e7..694ca084 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -20,7 +20,6 @@ use Iterator; use IteratorAggregate; use JsonSerializable; -use League\Csv\Exception\RuntimeException; use SplFileObject; /** @@ -100,9 +99,8 @@ public function getHeader(): array * * @param int $offset * - * @throws RuntimeException If the header offset is an integer - * and the corresponding record is missing - * or is an empty array + * @throws Exception If the header offset is an integer and the corresponding record is missing + * or is an empty array * * @return string[] */ @@ -112,7 +110,7 @@ protected function setHeader(int $offset): array $this->document->setCsvControl($this->delimiter, $this->enclosure, $this->escape); $this->document->seek($offset); if (empty($header = $this->document->current())) { - throw new RuntimeException(sprintf('The header record does not exist or is empty at offset: `%s`', $offset)); + throw new Exception(sprintf('The header record does not exist or is empty at offset: `%s`', $offset)); } if (0 === $offset) { @@ -229,7 +227,7 @@ public function getRecords(array $header = []): Iterator * * @param string[] $header * - * @throws RuntimeException If the header contains non unique column name + * @throws Exception If the header contains non unique column name * * @return string[] */ @@ -243,7 +241,7 @@ protected function computeHeader(array $header) return $header; } - throw new RuntimeException('The header record must be empty or a flat array with unique string values'); + throw new Exception('The header record must be empty or a flat array with unique string values'); } /** diff --git a/src/ResultSet.php b/src/ResultSet.php index 4ed9b769..e3358bc9 100644 --- a/src/ResultSet.php +++ b/src/ResultSet.php @@ -20,8 +20,6 @@ use Iterator; use IteratorAggregate; use JsonSerializable; -use League\Csv\Exception\OutOfRangeException; -use League\Csv\Exception\RuntimeException; use LimitIterator; /** @@ -83,8 +81,8 @@ public function getHeader(): array */ public function getRecords(): Generator { - foreach ($this->records as $value) { - yield $value; + foreach ($this->records as $offset => $value) { + yield $offset => $value; } } @@ -119,14 +117,14 @@ public function jsonSerialize(): array * * @param int $nth_record the CSV record offset * - * @throws OutOfRangeException if argument is lesser than 0 + * @throws Exception if argument is lesser than 0 * * @return array */ public function fetchOne(int $nth_record = 0): array { if ($nth_record < 0) { - throw new OutOfRangeException(sprintf('%s() expects the submitted offset to be a positive integer or 0, %s given', __METHOD__, $nth_record)); + throw new Exception(sprintf('%s() expects the submitted offset to be a positive integer or 0, %s given', __METHOD__, $nth_record)); } $iterator = new LimitIterator($this->records, $nth_record, 1); @@ -185,7 +183,7 @@ protected function getColumnIndex($field, string $error_message) * @param string $value * @param string $error_message * - * @throws RuntimeException if the column is not found + * @throws Exception if the column is not found * * @return string */ @@ -195,7 +193,7 @@ protected function getColumnIndexByValue(string $value, string $error_message): return $value; } - throw new RuntimeException(sprintf($error_message, $value)); + throw new Exception(sprintf($error_message, $value)); } /** @@ -204,15 +202,14 @@ protected function getColumnIndexByValue(string $value, string $error_message): * @param int $index * @param string $error_message * - * @throws OutOfRangeException if the field index is invalid - * @throws RuntimeException if the field is invalid or not found + * @throws Exception if the field is invalid or not found * * @return int|string */ protected function getColumnIndexByKey(int $index, string $error_message) { if ($index < 0) { - throw new OutOfRangeException($error_message); + throw new Exception($error_message); } if (empty($this->header)) { @@ -224,7 +221,7 @@ protected function getColumnIndexByKey(int $index, string $error_message) return $value; } - throw new RuntimeException(sprintf($error_message, $index)); + throw new Exception(sprintf($error_message, $index)); } /** diff --git a/src/Statement.php b/src/Statement.php index 00f83e5e..df5fa6be 100644 --- a/src/Statement.php +++ b/src/Statement.php @@ -17,7 +17,6 @@ use ArrayIterator; use CallbackFilterIterator; use Iterator; -use League\Csv\Exception\OutOfRangeException; use LimitIterator; /** @@ -93,14 +92,14 @@ public function orderBy(callable $callable): self * * @param $offset * - * @throws OutOfRangeException if the offset is lesser than 0 + * @throws Exception if the offset is lesser than 0 * * @return self */ public function offset(int $offset): self { if (0 > $offset) { - throw new OutOfRangeException(sprintf('%s() expects the offset to be a positive integer or 0, %s given', __METHOD__, $offset)); + throw new Exception(sprintf('%s() expects the offset to be a positive integer or 0, %s given', __METHOD__, $offset)); } if ($offset === $this->offset) { @@ -118,14 +117,14 @@ public function offset(int $offset): self * * @param int $limit * - * @throws OutOfRangeException if the limit is lesser than -1 + * @throws Exception if the limit is lesser than -1 * * @return self */ public function limit(int $limit): self { if (-1 > $limit) { - throw new OutOfRangeException(sprintf('%s() expects the limit to be greater or equel to -1, %s given', __METHOD__, $limit)); + throw new Exception(sprintf('%s() expects the limit to be greater or equel to -1, %s given', __METHOD__, $limit)); } if ($limit === $this->limit) { diff --git a/src/Stream.php b/src/Stream.php index 48f24733..57eae3b8 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -14,9 +14,7 @@ namespace League\Csv; -use League\Csv\Exception\LogicException; -use League\Csv\Exception\OutOfRangeException; -use League\Csv\Exception\RuntimeException; +use RuntimeException; use SeekableIterator; use SplFileObject; use TypeError; @@ -115,7 +113,7 @@ public function __construct($resource) } if (!stream_get_meta_data($resource)['seekable']) { - throw new RuntimeException('Argument passed must be a seekable stream resource'); + throw new Exception('Argument passed must be a seekable stream resource'); } $this->stream = $resource; @@ -144,7 +142,7 @@ public function __destruct() */ public function __clone() { - throw new LogicException(sprintf('An object of class %s cannot be cloned', get_class($this))); + throw new Exception(sprintf('An object of class %s cannot be cloned', get_class($this))); } /** @@ -167,7 +165,7 @@ public function __debugInfo() * @param string $open_mode the file open mode flag * @param resource|null $context the resource context * - * @throws RuntimeException if the stream resource can not be created + * @throws Exception if the stream resource can not be created * * @return static */ @@ -180,7 +178,7 @@ public static function createFromPath(string $path, string $open_mode = 'r', $co } if (!$resource = @fopen(...$args)) { - throw new RuntimeException(error_get_last()['message']); + throw new Exception(error_get_last()['message']); } $instance = new static($resource); @@ -216,7 +214,7 @@ public static function createFromString(string $content): self * @param int $read_write * @param mixed $params * - * @throws RuntimeException if the filter can not be appended + * @throws Exception if the filter can not be appended */ public function appendFilter(string $filtername, int $read_write, $params = null) { @@ -226,7 +224,7 @@ public function appendFilter(string $filtername, int $read_write, $params = null return; } - throw new RuntimeException(error_get_last()['message']); + throw new Exception(error_get_last()['message']); } /** @@ -385,12 +383,14 @@ protected function getCurrentRecord() * * @see http://php.net/manual/en/splfileobject.seek.php * - * @param int $position + * + * @param int $position + * @throws Exception if the position is negative */ public function seek($position) { if ($position < 0) { - throw new OutOfRangeException(sprintf('%s() can\'t seek stream to negative line %d', __METHOD__, $position)); + throw new Exception(sprintf('%s() can\'t seek stream to negative line %d', __METHOD__, $position)); } $this->rewind(); diff --git a/src/ValidatorTrait.php b/src/ValidatorTrait.php index 36121286..2283015e 100644 --- a/src/ValidatorTrait.php +++ b/src/ValidatorTrait.php @@ -14,8 +14,6 @@ namespace League\Csv; -use League\Csv\Exception\LengthException; -use League\Csv\Exception\OutOfRangeException; use TypeError; /** @@ -35,7 +33,7 @@ trait ValidatorTrait * @param string $type Csv control character type * @param string $caller public API method calling the method * - * @throws LengthException If the Csv control character is not one character only. + * @throws Exception If the Csv control character is not one character only. * * @return string */ @@ -45,7 +43,7 @@ protected function filterControl(string $char, string $type, string $caller): st return $char; } - throw new LengthException(sprintf('%s() expects %s to be a single character %s given', $caller, $type, $char)); + throw new Exception(sprintf('%s() expects %s to be a single character %s given', $caller, $type, $char)); } /** @@ -57,8 +55,8 @@ protected function filterControl(string $char, string $type, string $caller): st * @param int $min_range * @param string $error_message * - * @throws TypError if value is not a integer or null - * @throws OutOfRangeException if value is not in a valid int range + * @throws TypError if value is not a integer or null + * @throws Exception if value is not in a valid int range */ protected function filterNullableInteger($value, int $min_range, string $error_message) { @@ -71,7 +69,7 @@ protected function filterNullableInteger($value, int $min_range, string $error_m } if ($value < $min_range) { - throw new OutOfRangeException($error_message); + throw new Exception($error_message); } } } diff --git a/src/Writer.php b/src/Writer.php index c29d96d1..58befd5a 100644 --- a/src/Writer.php +++ b/src/Writer.php @@ -14,7 +14,6 @@ namespace League\Csv; -use League\Csv\Exception\InsertionException; use Traversable; use TypeError; @@ -119,7 +118,7 @@ public function insertAll($records): int * * @param string[] $record an array * - * @throws InsertionException If the record can not be inserted + * @throws CannotInsertRecord If the record can not be inserted * * @return int */ @@ -129,7 +128,7 @@ public function insertOne(array $record): int $this->validateRecord($record); $bytes = $this->document->fputcsv($record, ...$this->document->getCsvControl()); if (!$bytes) { - throw InsertionException::createFromStream($record); + throw CannotInsertRecord::triggerOnInsertion($record); } return $bytes + $this->consolidate(); @@ -153,13 +152,13 @@ protected function formatRecord(array $record, callable $formatter): array * * @param string[] $record * - * @throws InsertionException If the validation failed + * @throws CannotInsertRecord If the validation failed */ protected function validateRecord(array $record) { foreach ($this->validators as $name => $validator) { if (true !== $validator($record)) { - throw InsertionException::createFromValidator($name, $record); + throw CannotInsertRecord::triggerOnValidation($name, $record); } } } diff --git a/tests/CharsetConverterTest.php b/tests/CharsetConverterTest.php index 98f22a36..41b06d69 100644 --- a/tests/CharsetConverterTest.php +++ b/tests/CharsetConverterTest.php @@ -5,9 +5,9 @@ use ArrayIterator; use Iterator; use League\Csv\CharsetConverter; -use League\Csv\Exception\OutOfRangeException; -use League\Csv\Exception\RuntimeException; +use League\Csv\Exception; use League\Csv\Reader; +use OutOfRangeException; use PHPUnit\Framework\TestCase; use TypeError; @@ -121,7 +121,7 @@ public function testCharsetConverterAsStreamFilter() */ public function testCharsetConverterAsStreamFilterFailed() { - $this->expectException(RuntimeException::class); + $this->expectException(Exception::class); stream_filter_register(CharsetConverter::FILTERNAME.'.*', CharsetConverter::class); $expected = 'Batman,Superman,Anaïs'; $raw = mb_convert_encoding($expected, 'iso-8859-15', 'utf-8'); diff --git a/tests/ColumnConsistencyTest.php b/tests/ColumnConsistencyTest.php index 2abfc258..be5ca233 100644 --- a/tests/ColumnConsistencyTest.php +++ b/tests/ColumnConsistencyTest.php @@ -2,9 +2,9 @@ namespace LeagueTest\Csv; +use League\Csv\CannotInsertRecord; use League\Csv\ColumnConsistency; -use League\Csv\Exception\InsertionException; -use League\Csv\Exception\OutOfRangeException; +use League\Csv\Exception; use League\Csv\Writer; use PHPUnit\Framework\TestCase; use SplFileObject; @@ -35,7 +35,7 @@ public function tearDown() * @covers ::__construct * @covers ::getColumnCount * @covers ::__invoke - * @covers League\Csv\Exception\InsertionException + * @covers League\Csv\CannotInsertRecord */ public function testAutoDetect() { @@ -47,7 +47,7 @@ public function testAutoDetect() $this->csv->insertOne(['john', 'doe', 'john.doe@example.com']); $this->assertSame(3, $validator->getColumnCount()); $this->csv->insertOne($expected); - } catch (InsertionException $e) { + } catch (CannotInsertRecord $e) { $this->assertSame($e->getName(), 'consistency'); $this->assertEquals($e->getRecord(), ['jane', 'jane.doe@example.com']); } @@ -56,11 +56,11 @@ public function testAutoDetect() /** * @covers ::__construct * @covers ::__invoke - * @covers League\Csv\Exception\InsertionException + * @covers League\Csv\CannotInsertRecord */ public function testColumnsCount() { - $this->expectException(InsertionException::class); + $this->expectException(CannotInsertRecord::class); $this->csv->addValidator(new ColumnConsistency(3), 'consistency'); $this->csv->insertOne(['john', 'doe', 'john.doe@example.com']); $this->csv->insertOne(['jane', 'jane.doe@example.com']); @@ -68,11 +68,11 @@ public function testColumnsCount() /** * @covers ::__construct - * @covers League\Csv\Exception\OutOfRangeException + * @covers League\Csv\Exception */ public function testColumsCountTriggersException() { - $this->expectException(OutOfRangeException::class); + $this->expectException(Exception::class); new ColumnConsistency(-2); } } diff --git a/tests/CsvTest.php b/tests/CsvTest.php index b9a4da3c..5e0c8660 100644 --- a/tests/CsvTest.php +++ b/tests/CsvTest.php @@ -2,15 +2,13 @@ namespace LeagueTest\Csv; -use League\Csv\Exception\LengthException; -use League\Csv\Exception\OutOfRangeException; -use League\Csv\Exception\RuntimeException; +use League\Csv\Exception; use League\Csv\Reader; use League\Csv\Writer; -use LogicException; use PHPUnit\Framework\TestCase; use SplTempFileObject; use TypeError; +use function League\Csv\is_iterable; /** * @group csv @@ -65,7 +63,7 @@ public function testCreateFromFileObjectPreserveFileObjectCsvControls() */ public function testCreateFromPathThrowsRuntimeException() { - $this->expectException(RuntimeException::class); + $this->expectException(Exception::class); Reader::createFromPath(__DIR__.'/foo/bar', 'r'); } @@ -115,7 +113,7 @@ public function bomProvider() */ public function testCloningIsForbidden() { - $this->expectException(LogicException::class); + $this->expectException(Exception::class); clone $this->csv; } @@ -167,7 +165,7 @@ public function testToString() */ public function testChunkTriggersException() { - $this->expectException(OutOfRangeException::class); + $this->expectException(Exception::class); $chunk = $this->csv->chunk(0); iterator_to_array($chunk); } @@ -200,7 +198,7 @@ public function testStreamFilterMode() */ public function testDelimeter() { - $this->expectException(LengthException::class); + $this->expectException(Exception::class); $this->csv->setDelimiter('o'); $this->assertSame('o', $this->csv->getDelimiter()); $this->csv->setDelimiter('foo'); @@ -250,7 +248,7 @@ public function testChangingBOMOnOutput() */ public function testEscape() { - $this->expectException(LengthException::class); + $this->expectException(Exception::class); $this->csv->setEscape('o'); $this->assertSame('o', $this->csv->getEscape()); @@ -263,7 +261,7 @@ public function testEscape() */ public function testEnclosure() { - $this->expectException(LengthException::class); + $this->expectException(Exception::class); $this->csv->setEnclosure('o'); $this->assertSame('o', $this->csv->getEnclosure()); @@ -288,11 +286,11 @@ public function testAddStreamFilter() /** * @covers ::supportsStreamFilter * @covers ::addStreamFilter - * @covers League\Csv\Exception\LogicException + * @covers League\Csv\Exception */ public function testFailedAddStreamFilter() { - $this->expectException(LogicException::class); + $this->expectException(Exception::class); $csv = Writer::createFromFileObject(new SplTempFileObject()); $this->assertFalse($csv->supportsStreamFilter()); $csv->addStreamFilter('string.toupper'); @@ -302,11 +300,10 @@ public function testFailedAddStreamFilter() * @covers ::supportsStreamFilter * @covers ::addStreamFilter * @covers League\Csv\Stream::appendFilter - * @covers League\Csv\Exception\RuntimeException */ public function testFailedAddStreamFilterWithWrongFilter() { - $this->expectException(RuntimeException::class); + $this->expectException(Exception::class); $csv = Writer::createFromStream(tmpfile()); $csv->addStreamFilter('foobar.toupper'); } @@ -373,13 +370,13 @@ public function testIsIterablePolyFill() $this->markTestSkipped('Polyfill for PHP7.0'); } - $this->assertTrue(\League\Csv\is_iterable(['foo'])); - $this->assertTrue(\League\Csv\is_iterable(Reader::createFromString(''))); - $this->assertTrue(\League\Csv\is_iterable((function () { + $this->assertTrue(is_iterable(['foo'])); + $this->assertTrue(is_iterable(Reader::createFromString(''))); + $this->assertTrue(is_iterable((function () { yield 1; })())); - $this->assertFalse(\League\Csv\is_iterable(1)); - $this->assertFalse(\League\Csv\is_iterable((object) ['foo'])); - $this->assertFalse(\League\Csv\is_iterable(Writer::createFromString(''))); + $this->assertFalse(is_iterable(1)); + $this->assertFalse(is_iterable((object) ['foo'])); + $this->assertFalse(is_iterable(Writer::createFromString(''))); } } diff --git a/tests/DetectDelimiterTest.php b/tests/DetectDelimiterTest.php index 9f88aede..4f752ae2 100644 --- a/tests/DetectDelimiterTest.php +++ b/tests/DetectDelimiterTest.php @@ -2,7 +2,7 @@ namespace LeagueTest\Csv; -use League\Csv\Exception\OutOfRangeException; +use League\Csv\Exception; use League\Csv\Reader; use PHPUnit\Framework\TestCase; use SplTempFileObject; @@ -19,7 +19,7 @@ public function testDetectDelimiterListWithInvalidRowLimit() $file = new SplTempFileObject(); $file->fwrite("How are you today ?\nI'm doing fine thanks!"); $csv = Reader::createFromFileObject($file); - $this->expectException(OutOfRangeException::class); + $this->expectException(Exception::class); delimiter_detect($csv, [','], -4); } diff --git a/tests/ReaderTest.php b/tests/ReaderTest.php index 599a407e..dabac958 100644 --- a/tests/ReaderTest.php +++ b/tests/ReaderTest.php @@ -3,8 +3,7 @@ namespace LeagueTest\Csv; use BadMethodCallException; -use League\Csv\Exception\OutOfRangeException; -use League\Csv\Exception\RuntimeException; +use League\Csv\Exception; use League\Csv\Reader; use League\Csv\Statement; use PHPUnit\Framework\TestCase; @@ -160,9 +159,11 @@ public function invalidMethodCallMethodProvider() * @covers ::computeHeader * @covers ::getRecords * @covers ::setHeader + * @covers League\Csv\Exception */ - public function testDuplicateHeaderValueTriggersException() + public function testHeaderThrowsExceptionOnError() { + $this->expectException(Exception::class); $csv = Reader::createFromString( 'field1,field1,field3 1,2,3 @@ -170,8 +171,7 @@ public function testDuplicateHeaderValueTriggersException() ); $csv->setHeaderOffset(0); $this->assertSame(['field1', 'field1', 'field3'], $csv->getHeader()); - $this->expectException(RuntimeException::class); - iterator_to_array($csv, true); + iterator_to_array($csv); } /** @@ -293,7 +293,7 @@ public function appliedFlagsProvider() */ public function testGetHeaderThrowsException() { - $this->expectException(RuntimeException::class); + $this->expectException(Exception::class); $this->csv->setHeaderOffset(23)->getRecords(); } @@ -313,7 +313,7 @@ public function testSetHeaderThrowsExceptionOnWrongInput() */ public function testSetHeaderThrowsExceptionOnWrongInputRange() { - $this->expectException(OutOfRangeException::class); + $this->expectException(Exception::class); $this->csv->setHeaderOffset(-1); } diff --git a/tests/ResultSetTest.php b/tests/ResultSetTest.php index 166180cb..081aeb57 100644 --- a/tests/ResultSetTest.php +++ b/tests/ResultSetTest.php @@ -3,8 +3,7 @@ namespace LeagueTest\Csv; use Generator; -use League\Csv\Exception\OutOfRangeException; -use League\Csv\Exception\RuntimeException; +use League\Csv\Exception; use League\Csv\Reader; use League\Csv\Statement; use OutOfBoundsException; @@ -58,7 +57,7 @@ public function testSetLimit() */ public function testSetOffsetThrowsException() { - $this->expectException(OutOfRangeException::class); + $this->expectException(Exception::class); $this->stmt->offset(-1); } @@ -89,11 +88,10 @@ public function testStatementSameInstance() /** * @covers League\Csv\Statement::limit - * @covers League\Csv\Exception\OutOfRangeException */ public function testSetLimitThrowException() { - $this->expectException(OutOfRangeException::class); + $this->expectException(Exception::class); $this->stmt->limit(-4); } @@ -146,7 +144,6 @@ public function intervalTest() * @covers League\Csv\Statement::limit * @covers League\Csv\Statement::offset * @covers League\Csv\Statement::process - * @covers League\Csv\Exception\OutOfRangeException */ public function testIntervalThrowException() { @@ -209,14 +206,13 @@ public function testOrderByWithEquity() * @covers ::getColumnIndexByValue * @covers ::getColumnIndexByKey * @covers ::__destruct - * @covers League\Csv\Exception\RuntimeException * @covers League\Csv\MapIterator * @dataProvider invalidFieldNameProvider * @param int|string $field */ public function testFetchColumnTriggersException($field) { - $this->expectException(RuntimeException::class); + $this->expectException(Exception::class); $this->csv->setHeaderOffset(0); $res = $this->stmt->process($this->csv)->fetchColumn($field); iterator_to_array($res, false); @@ -234,13 +230,12 @@ public function invalidFieldNameProvider() * @covers ::fetchColumn * @covers ::getColumnIndexByKey * @covers League\Csv\MapIterator - * @covers League\Csv\Exception\OutOfRangeException * * @param int|string $field */ public function testFetchColumnTriggersOutOfRangeException() { - $this->expectException(OutOfRangeException::class); + $this->expectException(Exception::class); $this->csv->setHeaderOffset(0); $res = $this->stmt->process($this->csv)->fetchColumn(-1); iterator_to_array($res, false); @@ -361,7 +356,7 @@ public function testfetchOne() */ public function testFetchOneTriggersException() { - $this->expectException(OutOfRangeException::class); + $this->expectException(Exception::class); $this->stmt->process($this->csv)->fetchOne(-5); } diff --git a/tests/StreamTest.php b/tests/StreamTest.php index c6d03238..ef75918c 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -2,11 +2,8 @@ namespace LeagueTest\Csv; -use League\Csv\Exception\LengthException; -use League\Csv\Exception\OutOfRangeException; -use League\Csv\Exception\RuntimeException; +use League\Csv\Exception; use League\Csv\Stream; -use LogicException; use PHPUnit\Framework\TestCase; use SplFileObject; use TypeError; @@ -32,7 +29,7 @@ public function tearDown() */ public function testCloningIsForbidden() { - $this->expectException(LogicException::class); + $this->expectException(Exception::class); $toto = clone new Stream(fopen('php://temp', 'r+')); } @@ -50,7 +47,7 @@ public function testCreateStreamWithInvalidParameter() */ public function testCreateStreamWithNonSeekableStream() { - $this->expectException(RuntimeException::class); + $this->expectException(Exception::class); new Stream(fopen('php://stdin', 'r')); } @@ -99,7 +96,7 @@ public function testCreateStreamFromPathWithContext() */ public function testfputcsv($delimiter, $enclosure, $escape) { - $this->expectException(LengthException::class); + $this->expectException(Exception::class); $stream = new Stream(fopen('php://temp', 'r+')); $stream->fputcsv(['john', 'doe', 'john.doe@example.com'], $delimiter, $enclosure, $escape); } @@ -127,7 +124,7 @@ public function testVarDump() */ public function testSeekThrowsException() { - $this->expectException(OutOfRangeException::class); + $this->expectException(Exception::class); $stream = new Stream(fopen('php://temp', 'r+')); $stream->seek(-1); } diff --git a/tests/WriterTest.php b/tests/WriterTest.php index 12831061..f07d526c 100644 --- a/tests/WriterTest.php +++ b/tests/WriterTest.php @@ -3,8 +3,8 @@ namespace LeagueTest\Csv; use ArrayIterator; -use League\Csv\Exception\InsertionException; -use League\Csv\Exception\OutOfRangeException; +use League\Csv\CannotInsertRecord; +use League\Csv\Exception; use League\Csv\Writer; use PHPUnit\Framework\TestCase; use SplFileObject; @@ -41,7 +41,7 @@ public function tearDown() */ public function testflushThreshold() { - $this->expectException(OutOfRangeException::class); + $this->expectException(Exception::class); $this->csv->setFlushThreshold(12); $this->assertSame(12, $this->csv->getFlushThreshold()); $this->csv->setFlushThreshold(12); @@ -102,7 +102,7 @@ public function testInsertNormalFile() /** * @covers ::insertOne - * @covers League\Csv\Exception\InsertionException + * @covers League\Csv\CannotInsertRecord */ public function testInsertThrowsExceptionOnError() { @@ -110,7 +110,7 @@ public function testInsertThrowsExceptionOnError() try { $csv = Writer::createFromPath(__DIR__.'/data/foo.csv', 'r'); $csv->insertOne($expected); - } catch (InsertionException $e) { + } catch (CannotInsertRecord $e) { $this->assertSame($e->getRecord(), $expected); } } @@ -193,7 +193,7 @@ public function testAddValidationRules() return false; }; - $this->expectException(InsertionException::class); + $this->expectException(CannotInsertRecord::class); $this->csv->addValidator($func, 'func1'); $this->csv->insertOne(['jane', 'doe']); }