diff options
author | Ignace Nyamagana Butera <nyamsprod@gmail.com> | 2015-02-16 11:58:56 +0100 |
---|---|---|
committer | Ignace Nyamagana Butera <nyamsprod@gmail.com> | 2015-02-16 11:58:56 +0100 |
commit | d5712a9a310112e4eff47675061b081b903f4c3f (patch) | |
tree | bb17a3cbf0ced18f5b11bf8b8269a023592aac52 /src | |
parent | 2d7246457b4ac81169bf4a50ec9c7ba0165785d2 (diff) | |
download | csv-d5712a9a310112e4eff47675061b081b903f4c3f.zip csv-d5712a9a310112e4eff47675061b081b903f4c3f.tar.gz csv-d5712a9a310112e4eff47675061b081b903f4c3f.tar.bz2 |
improve Writer validation capability
Diffstat (limited to 'src')
-rw-r--r-- | src/Validators/ColumnConsistency.php | 123 | ||||
-rw-r--r-- | src/Validators/NullHandling.php | 134 | ||||
-rw-r--r-- | src/Writer.php | 228 |
3 files changed, 308 insertions, 177 deletions
diff --git a/src/Validators/ColumnConsistency.php b/src/Validators/ColumnConsistency.php new file mode 100644 index 0000000..06d0bfd --- /dev/null +++ b/src/Validators/ColumnConsistency.php @@ -0,0 +1,123 @@ +<?php +/** +* This file is part of the League.csv library +* +* @license http://opensource.org/licenses/MIT +* @link https://github.com/thephpleague/csv/ +* @version 7.0.0 +* @package League.csv +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ +namespace League\Csv\Validators; + +use InvalidArgumentException; +use RuntimeException; + +/** + * A class to manage data insertion into a CSV + * + * @package League.csv + * @since 7.0.0 + * + */ +class ColumnConsistency +{ + /** + * The number of column per row + * + * @var int + */ + private $columns_count = -1; + + /** + * should the class detect the column count based the inserted row + * + * @var bool + */ + private $detect_columns_count = false; + + /** + * Set Inserted row column count + * + * @param int $value + * + * @throws \InvalidArgumentException If $value is lesser than -1 + * + * @return static + */ + public function setColumnsCount($value) + { + if (false === filter_var($value, FILTER_VALIDATE_INT, ['options' => ['min_range' => -1]])) { + throw new InvalidArgumentException('the column count must an integer greater or equals to -1'); + } + $this->detect_columns_count = false; + $this->columns_count = $value; + + return $this; + } + + /** + * Column count getter + * + * @return int + */ + public function getColumnsCount() + { + return $this->columns_count; + } + + /** + * The method will set the $columns_count property according to the next inserted row + * and therefore will also validate the next line whatever length it has no matter + * the current $columns_count property value. + * + * @return static + */ + public function autodetectColumnsCount() + { + $this->detect_columns_count = true; + + return $this; + } + + /** + * Is the submitted row valid + * + * @param array $row + * + * @throws \RuntimeException If the given $row does not contain valid column count + * + * @return array + */ + public function __invoke(array $row) + { + if (! $this->isColumnsCountConsistent($row)) { + throw new RuntimeException('Adding '.count($row).' cells on a {$this->columns_count} cells per row CSV.'); + } + + return $row; + } + + /** + * Check column count consistency + * + * @param array $row the row to be added to the CSV + * + * @return bool + */ + private function isColumnsCountConsistent(array $row) + { + if ($this->detect_columns_count) { + $this->columns_count = count($row); + $this->detect_columns_count = false; + + return true; + } elseif (-1 == $this->columns_count) { + return true; + } + + return count($row) == $this->columns_count; + } +} diff --git a/src/Validators/NullHandling.php b/src/Validators/NullHandling.php new file mode 100644 index 0000000..a685d84 --- /dev/null +++ b/src/Validators/NullHandling.php @@ -0,0 +1,134 @@ +<?php +/** +* This file is part of the League.csv library +* +* @license http://opensource.org/licenses/MIT +* @link https://github.com/thephpleague/csv/ +* @version 7.0.0 +* @package League.csv +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ +namespace League\Csv\Validators; + +use InvalidArgumentException; +use OutOfBoundsException; + +/** + * A class to manage data insertion into a CSV + * + * @package League.csv + * @since 7.0.0 + * + */ +class NullHandling +{ + /** + * set null handling mode to throw exception + */ + const NULL_AS_EXCEPTION = 1; + + /** + * set null handling mode to remove cell + */ + const NULL_AS_SKIP_CELL = 2; + + /** + * set null handling mode to convert null into empty string + */ + const NULL_AS_EMPTY = 3; + + /** + * disable null handling + */ + const NULL_HANDLING_DISABLED = 4; + + /** + * the object current null handling mode + * + * @var int + */ + private $null_handling_mode = self::NULL_AS_EXCEPTION; + + /** + * Tell the class how to handle null value + * + * @param int $value a Writer null behavior constant + * + * @throws \OutOfBoundsException If the Integer is not valid + * + * @return static + */ + public function setNullHandlingMode($value) + { + if (! in_array($value, [ + self::NULL_AS_SKIP_CELL, + self::NULL_AS_EXCEPTION, + self::NULL_AS_EMPTY, + self::NULL_HANDLING_DISABLED, + ])) { + throw new OutOfBoundsException('invalid value for null handling'); + } + $this->null_handling_mode = $value; + + return $this; + } + + /** + * null handling getter + * + * @return int + */ + public function getNullHandlingMode() + { + return $this->null_handling_mode; + } + + + /** + * Is the submitted row valid + * + * @param array $row + * + * @throws \InvalidArgumentException If the given $row is not valid + * + * @return array + */ + public function __invoke(array $row) + { + if (self::NULL_HANDLING_DISABLED == $this->null_handling_mode) { + return $row; + } + + array_walk($row, function ($value) { + if (! $this->isConvertibleContent($value)) { + throw new InvalidArgumentException('The values are not convertible into strings'); + } + }); + + if (self::NULL_AS_EMPTY == $this->null_handling_mode) { + return str_replace(null, '', $row); + } + + return array_filter($row, function ($value) { + return ! is_null($value); + }); + } + + /** + * Check if a given value can be added into a CSV cell + * + * The value MUST respect the null handling mode + * The value MUST be convertible into a string + * + * @param string|null $value the value to be added + * + * @return bool + */ + private function isConvertibleContent($value) + { + return (is_null($value) && self::NULL_AS_EXCEPTION != $this->null_handling_mode) + || \League\Csv\Writer::isValidString($value); + } +} diff --git a/src/Writer.php b/src/Writer.php index 9c52374..1e12ab2 100644 --- a/src/Writer.php +++ b/src/Writer.php @@ -13,9 +13,6 @@ namespace League\Csv; use InvalidArgumentException; -use OutOfBoundsException; -use RuntimeException; -use SplFileObject; use Traversable; /** @@ -28,47 +25,6 @@ use Traversable; class Writer extends AbstractCsv { /** - * set null handling mode to throw exception - */ - const NULL_AS_EXCEPTION = 1; - - /** - * set null handling mode to remove cell - */ - const NULL_AS_SKIP_CELL = 2; - - /** - * set null handling mode to convert null into empty string - */ - const NULL_AS_EMPTY = 3; - - /** - * disable null handling - */ - const NULL_HANDLING_DISABLED = 4; - - /** - * the object current null handling mode - * - * @var int - */ - protected $null_handling_mode = self::NULL_AS_EXCEPTION; - - /** - * The number of column per row - * - * @var int - */ - protected $columns_count = -1; - - /** - * should the class detect the column count based the inserted row - * - * @var bool - */ - protected $detect_columns_count = false; - - /** * {@ihneritdoc} */ protected $stream_filter_mode = STREAM_FILTER_WRITE; @@ -81,102 +37,107 @@ class Writer extends AbstractCsv protected $csv; /** + * Callables to filter the iterator + * + * @var callable[] + */ + protected $rules = []; + + /** * should the class validate the input before insertion * * @var boolean */ protected $useValidation = true; + /** - * Tell the class how to handle null value - * - * @param int $value a Writer null behavior constant + * Tells wether the library should check or not the input * - * @throws \OutOfBoundsException If the Integer is not valid + * @param bool $status * * @return static */ - public function setNullHandlingMode($value) + public function useValidation($activate) { - if (! in_array($value, [ - self::NULL_AS_SKIP_CELL, - self::NULL_AS_EXCEPTION, - self::NULL_AS_EMPTY, - self::NULL_HANDLING_DISABLED, - ])) { - throw new OutOfBoundsException('invalid value for null handling'); - } - $this->null_handling_mode = $value; + $this->useValidation = (bool) $activate; return $this; } /** - * null handling getter + * Set an Iterator sorting callable function + * + * @param callable $callable * - * @return int + * @return static */ - public function getNullHandlingMode() + public function addValidationRule(callable $callable) { - return $this->null_handling_mode; + $this->rules[] = $callable; + + return $this; } /** - * Set Inserted row column count + * Remove a callable from the collection * - * @param int $value - * - * @throws \InvalidArgumentException If $value is lesser than -1 + * @param callable $callable * * @return static */ - public function setColumnsCount($value) + public function removeValidationRule(callable $callable) { - if (false === filter_var($value, FILTER_VALIDATE_INT, ['options' => ['min_range' => -1]])) { - throw new InvalidArgumentException('the column count must an integer greater or equals to -1'); + $res = array_search($callable, $this->rules, true); + if (false !== $res) { + unset($this->rules[$res]); } - $this->detect_columns_count = false; - $this->columns_count = $value; return $this; } /** - * Column count getter + * Detect if the callable is already registered + * + * @param callable $callable * - * @return int + * @return bool */ - public function getColumnsCount() + public function hasValidationRule(callable $callable) { - return $this->columns_count; + return false !== array_search($callable, $this->rules, true); } /** - * The method will set the $columns_count property according to the next inserted row - * and therefore will also validate the next line whatever length it has no matter - * the current $columns_count property value. + * Remove all registered callable * * @return static */ - public function autodetectColumnsCount() + public function clearValidationRules() { - $this->detect_columns_count = true; + $this->rules = []; return $this; } /** - * Tells wether the library should check or not the input - * - * @param bool $status - * - * @return static - */ - public function useValidation($status) + * Filter the Iterator + * + * @param array $row + * + * @return array + */ + protected function applyValidationRules(array $row) { - $this->useValidation = (bool) $status; + if (! $this->useValidation || ! $this->rules) { + return $row; + } - return $this; + foreach ($this->rules as $rule) { + $row = $rule($row); + } + + return $row; } /** @@ -215,9 +176,7 @@ class Writer extends AbstractCsv public function insertOne($row) { $row = $this->formatRow($row); - if ($this->useValidation) { - $row = $this->validateRow($row); - } + $row = $this->applyValidationRules($row); $csv = $this->getCsv(); $csv->fputcsv($row, $this->delimiter, $this->enclosure); if ("\n" !== $this->newline) { @@ -245,91 +204,6 @@ class Writer extends AbstractCsv } /** - * Is the submitted row valid - * - * @param array $row - * - * @throws \InvalidArgumentException If the given $row is not valid - * @throws \RuntimeException If the given $row does not contain valid column count - * - * @return array - */ - protected function validateRow(array $row) - { - if (self::NULL_HANDLING_DISABLED != $this->null_handling_mode) { - array_walk($row, function ($value) { - if (! $this->isConvertibleContent($value)) { - throw new InvalidArgumentException('The values are not convertible into strings'); - } - }); - $row = $this->sanitizeColumnsContent($row); - } - - if (! $this->isColumnsCountConsistent($row)) { - throw new RuntimeException('Adding '.count($row).' cells on a {$this->columns_count} cells per row CSV.'); - } - - return $row; - } - - /** - * Check if a given value can be added into a CSV cell - * - * The value MUST respect the null handling mode - * The value MUST be convertible into a string - * - * @param string|null $value the value to be added - * - * @return bool - */ - protected function isConvertibleContent($value) - { - return (is_null($value) && self::NULL_AS_EXCEPTION != $this->null_handling_mode) - || self::isValidString($value); - } - - /** - * Format the row according to the null handling behavior - * - * @param array $row - * - * @return array - */ - protected function sanitizeColumnsContent(array $row) - { - if (self::NULL_AS_EMPTY == $this->null_handling_mode) { - return str_replace(null, '', $row); - } elseif (self::NULL_AS_SKIP_CELL == $this->null_handling_mode) { - return array_filter($row, function ($value) { - return !is_null($value); - }); - } - - return $row; - } - - /** - * Check column count consistency - * - * @param array $row the row to be added to the CSV - * - * @return bool - */ - protected function isColumnsCountConsistent(array $row) - { - if ($this->detect_columns_count) { - $this->columns_count = count($row); - $this->detect_columns_count = false; - - return true; - } elseif (-1 == $this->columns_count) { - return true; - } - - return count($row) == $this->columns_count; - } - - /** * set the csv container as a SplFileObject instance * insure we use the same object for insertion to * avoid loosing the cursor position |