diff options
author | Ignace Nyamagana Butera <nyamsprod@gmail.com> | 2014-05-09 13:58:41 +0200 |
---|---|---|
committer | Ignace Nyamagana Butera <nyamsprod@gmail.com> | 2014-05-09 13:58:41 +0200 |
commit | 4208a8c5b9f6c03a263eb1df1953220fc9a3f5c4 (patch) | |
tree | 2fbba382212b784d2ddd046aa21dfe218455b588 /src | |
parent | 3983ee8aebff00dcc53b68c5c4713c327d083091 (diff) | |
parent | 2d797799b7cfc7928ef1b8cc4a010312ad0d0c46 (diff) | |
download | csv-4208a8c5b9f6c03a263eb1df1953220fc9a3f5c4.zip csv-4208a8c5b9f6c03a263eb1df1953220fc9a3f5c4.tar.gz csv-4208a8c5b9f6c03a263eb1df1953220fc9a3f5c4.tar.bz2 |
Adding Stream Filtering capabilities #32
Diffstat (limited to 'src')
-rw-r--r-- | src/AbstractCsv.php | 225 | ||||
-rw-r--r-- | src/Iterator/IteratorFilter.php | 48 | ||||
-rw-r--r-- | src/Iterator/IteratorInterval.php | 49 | ||||
-rw-r--r-- | src/Iterator/IteratorQuery.php | 80 | ||||
-rw-r--r-- | src/Iterator/IteratorSortBy.php | 60 | ||||
-rw-r--r-- | src/Iterator/MapIterator.php | 30 | ||||
-rw-r--r-- | src/Reader.php | 91 | ||||
-rw-r--r-- | src/Stream/StreamFilter.php | 259 | ||||
-rw-r--r-- | src/Writer.php | 93 |
9 files changed, 535 insertions, 400 deletions
diff --git a/src/AbstractCsv.php b/src/AbstractCsv.php index d4e0160..746f334 100644 --- a/src/AbstractCsv.php +++ b/src/AbstractCsv.php @@ -1,51 +1,30 @@ <?php /** -* League.csv - A CSV data manipulation library +* This file is part of the League.csv library * -* @author Ignace Nyamagana Butera <nyamsprod@gmail.com> -* @copyright 2014 Ignace Nyamagana Butera -* @link https://github.com/thephpleague/csv/ * @license http://opensource.org/licenses/MIT -* @version 5.4.0 +* @link https://github.com/thephpleague/csv/ +* @version 5.5.0 * @package League.csv * -* MIT LICENSE -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. */ namespace League\Csv; use DomDocument; use JsonSerializable; - +use Traversable; use SplFileInfo; use SplFileObject; use SplTempFileObject; - use RuntimeException; use InvalidArgumentException; - use IteratorAggregate; use LimitIterator; use CallbackFilterIterator; use League\Csv\Iterator\MapIterator; +use League\Csv\Stream\StreamFilter; /** * An abstract class to enable basic CSV manipulation @@ -56,13 +35,10 @@ use League\Csv\Iterator\MapIterator; */ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate { - /** - * The CSV object holder - * - * @var \SplFileObject + * Stream Filter Trait */ - protected $csv; + use StreamFilter; /** * the field delimiter (one character only) @@ -97,7 +73,21 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate * * @var string */ - protected $encoding = 'UTF-8'; + protected $encodingFrom = 'UTF-8'; + + /** + * The constructor path + * + * @var mixed can be a SplFileInfo object or the path to a file + */ + protected $path; + + /** + * The file open mode flag + * + * @var string + */ + protected $open_mode; /** * The constructor @@ -107,18 +97,25 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate */ public function __construct($path, $open_mode = 'r+') { - ini_set("auto_detect_line_endings", true); - $this->setIterator($path, $open_mode); + if (! is_string($path) && ! $path instanceof SplFileInfo) { + throw new InvalidArgumentException( + 'path must be a valid string or a `SplFileInfo` object' + ); + } + ini_set("auto_detect_line_endings", '1'); + //lazy loading + $this->path = $path; + $this->open_mode = strtolower($open_mode); + $this->initStreamFilter($path); } /** * The destructor - * - * Make sure the class reference is destroy when the class is no longer used */ public function __destruct() { - $this->csv = null; + //in case path is a SplFileObject we need to remove its reference + $this->path = null; } /** @@ -146,6 +143,50 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate } /** + * Instantiate a AbstractCsv extended class + * + * @param string $class_name the Class to load {@link Writer} or {@link Reader} + * @param string $open_mode the file open mode flag + * + * @return \League\Csv\AbstractCSv + */ + protected function createFromCurrentInstance($class_name, $open_mode) + { + $obj = new $class_name($this->path, $open_mode); + $obj->delimiter = $this->delimiter; + $obj->enclosure = $this->enclosure; + $obj->escape = $this->escape; + $obj->flags = $this->flags; + $obj->encoding = $this->encodingFrom; + + return $obj; + } + + /** + * Instantiate a {@link Writer} class from the current object + * + * @param string $open_mode the file open mode flag + * + * @return \League\Csv\Writer + */ + public function createWriter($open_mode = 'r+') + { + return $this->createFromCurrentInstance('\League\Csv\Writer', $open_mode); + } + + /** + * Instantiate a {@link Reader} class from the current object + * + * @param string $open_mode the file open mode flag + * + * @return \League\Csv\Reader + */ + public function createReader($open_mode = 'r+') + { + return $this->createFromCurrentInstance('\League\Csv\Reader', $open_mode); + } + + /** * Validate a variable to be stringable * * @param mixed $str @@ -320,48 +361,40 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate } /** - * set the csv container as a SplFileObject instance - * - * @param mixed $path A SplFileInfo object or the path to a file - * - * @return self + * Return the CSV Iterator * - * @throws \InvalidArgumentException If the $file is not set - * @throws \RuntimeException If the $file could not be created and/or opened + * @return \SplFileObject */ - protected function setIterator($path, $open_mode) + public function getIterator() { - if ($path instanceof SplFileObject) { - $this->csv = $path; - - return $this; + $obj = $this->path; + if (! $obj instanceof SplFileObject) { + $obj = new SplFileObject($this->getStreamFilterPath(), $this->open_mode); } - $open_mode = strtolower($open_mode); - if ($path instanceof SplFileInfo) { - $this->csv = $path->openFile($open_mode); - - return $this; - } elseif (is_string($path)) { - $this->csv = new SplFileObject($path, $open_mode); + $obj->setCsvControl($this->delimiter, $this->enclosure, $this->escape); + $obj->setFlags($this->flags); - return $this; - } - throw new InvalidArgumentException( - '$path must be a `SplFileInfo` object or a valid file path.' - ); + return $obj; } /** - * Return the CSV Iterator + * DEPRECATION WARNING! This method will be removed in the next major point release * - * @return \SplFileObject + * @deprecated deprecated since version 5.5 */ - public function getIterator() + public function setEncoding($str) { - $this->csv->setCsvControl($this->delimiter, $this->enclosure, $this->escape); - $this->csv->setFlags($this->flags); + return $this->setEncodingFrom($str); + } - return $this->csv; + /** + * DEPRECATION WARNING! This method will be removed in the next major point release + * + * @deprecated deprecated since version 5.5 + */ + public function getEncoding() + { + return $this->getEncodingFrom(); } /** @@ -371,66 +404,42 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate * * @return self */ - public function setEncoding($str) + public function setEncodingFrom($str) { $str = str_replace('_', '-', $str); $str = filter_var($str, FILTER_SANITIZE_STRING, ['flags' => FILTER_FLAG_STRIP_LOW|FILTER_FLAG_STRIP_HIGH]); if (empty($str)) { throw new InvalidArgumentException('you should use a valid charset'); } - $this->encoding = strtoupper($str); + $this->encodingFrom = strtoupper($str); return $this; } /** - * Get the CSV encoding charset + * Get the source CSV encoding charset * * @return string */ - public function getEncoding() - { - return $this->encoding; - } - - /** - * Instantiate a AbstractCsv extended class - * - * @param string $class_name the Class to load {@link Writer} or {@link Reader} - * @param string $open_mode the file open mode flag - * - * @return \League\Csv\AbstractCSv - */ - protected function getInstance($class_name, $open_mode = 'r+') + public function getEncodingFrom() { - $obj = $this->csv; - if (! $obj instanceof SplTempFileObject && ($path = $obj->getRealPath()) !== false) { - $obj = new SplFileObject($path, $open_mode); - } - $csv = new $class_name($obj); - $csv->setDelimiter($this->delimiter); - $csv->setEnclosure($this->enclosure); - $csv->setEscape($this->escape); - $csv->setFlags($this->flags); - $csv->setEncoding($this->encoding); - - return $csv; + return $this->encodingFrom; } /** * Convert Csv file into UTF-8 * - * @return \Iterator + * @return \Traversable */ - protected function convert2Utf8() + protected function convert2Utf8(Traversable $iterator) { - if ('UTF-8' == $this->encoding) { - return $this->getIterator(); + if ('UTF-8' == $this->encodingFrom) { + return $iterator; } - return new MapIterator($this->getIterator(), function ($row) { + return new MapIterator($iterator, function ($row) { foreach ($row as &$value) { - $value = mb_convert_encoding($value, 'UTF-8', $this->encoding); + $value = mb_convert_encoding($value, 'UTF-8', $this->encodingFrom); } unset($value); @@ -445,7 +454,7 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate */ public function jsonSerialize() { - return iterator_to_array($this->convert2Utf8(), false); + return iterator_to_array($this->convert2Utf8($this->getIterator()), false); } /** @@ -458,8 +467,9 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate $iterator = $this->getIterator(); //@codeCoverageIgnoreStart if (! is_null($filename) && AbstractCsv::isValidString($filename)) { - header('Content-Type: text/csv; charset="'.$this->encoding.'"'); + header('Content-Type: text/csv; charset="'.$this->encodingFrom.'"'); header('Content-Disposition: attachment; filename="'.$filename.'"'); + header('Content-Transfer-Encoding: binary'); if (! $iterator instanceof SplTempFileObject) { header('Content-Length: '.$iterator->getSize()); } @@ -495,7 +505,8 @@ abstract class AbstractCsv implements JsonSerializable, IteratorAggregate { $doc = new DomDocument('1.0', 'UTF-8'); $root = $doc->createElement($root_name); - foreach ($this->convert2Utf8() as $row) { + $csv = $this->convert2Utf8($this->getIterator()); + foreach ($csv as $row) { $item = $doc->createElement($row_name); foreach ($row as $value) { $content = $doc->createTextNode($value); diff --git a/src/Iterator/IteratorFilter.php b/src/Iterator/IteratorFilter.php index bd44d10..888c97c 100644 --- a/src/Iterator/IteratorFilter.php +++ b/src/Iterator/IteratorFilter.php @@ -1,34 +1,14 @@ <?php /** -* League.csv - A CSV data manipulation library +* This file is part of the League.csv library * -* @author Ignace Nyamagana Butera <nyamsprod@gmail.com> -* @copyright 2014 Ignace Nyamagana Butera -* @link https://github.com/thephpleague/csv/ * @license http://opensource.org/licenses/MIT -* @version 5.4.0 +* @link https://github.com/thephpleague/csv/ +* @version 5.5.0 * @package League.csv * -* MIT LICENSE -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. */ namespace League\Csv\Iterator; @@ -49,7 +29,7 @@ trait IteratorFilter * * @var array */ - private $filter = []; + protected $iterator_filters = []; /** * Set the Iterator filter method @@ -70,13 +50,13 @@ trait IteratorFilter /** * Set the Iterator filter method * - * @param callable $filter + * @param callable $callable * * @return self */ public function addFilter(callable $callable) { - $this->filter[] = $callable; + $this->iterator_filters[] = $callable; return $this; } @@ -90,9 +70,9 @@ trait IteratorFilter */ public function removeFilter(callable $callable) { - $res = array_search($callable, $this->filter, true); + $res = array_search($callable, $this->iterator_filters, true); if (false !== $res) { - unset($this->filter[$res]); + unset($this->iterator_filters[$res]); } return $this; @@ -107,7 +87,7 @@ trait IteratorFilter */ public function hasFilter(callable $callable) { - return false !== array_search($callable, $this->filter, true); + return false !== array_search($callable, $this->iterator_filters, true); } /** @@ -117,7 +97,7 @@ trait IteratorFilter */ public function clearFilter() { - $this->filter = []; + $this->iterator_filters = []; return $this; } @@ -129,9 +109,9 @@ trait IteratorFilter * * @return \Iterator */ - protected function applyFilter(Iterator $iterator) + protected function applyIteratorFilter(Iterator $iterator) { - foreach ($this->filter as $callable) { + foreach ($this->iterator_filters as $callable) { $iterator = new CallbackFilterIterator($iterator, $callable); } $this->clearFilter(); diff --git a/src/Iterator/IteratorInterval.php b/src/Iterator/IteratorInterval.php index a77b90c..fe3cbb4 100644 --- a/src/Iterator/IteratorInterval.php +++ b/src/Iterator/IteratorInterval.php @@ -1,34 +1,13 @@ <?php /** -* League.csv - A CSV data manipulation library +* This file is part of the League.csv library * -* @author Ignace Nyamagana Butera <nyamsprod@gmail.com> -* @copyright 2014 Ignace Nyamagana Butera -* @link https://github.com/thephpleague/csv/ * @license http://opensource.org/licenses/MIT -* @version 5.4.0 +* @version 5.5.0 * @package League.csv * -* MIT LICENSE -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. */ namespace League\Csv\Iterator; @@ -50,14 +29,14 @@ trait IteratorInterval * * @var integer */ - private $offset = 0; + protected $iterator_offset = 0; /** * iterator maximum length * * @var integer */ - private $limit = -1; + protected $iterator_limit = -1; /** * Set LimitIterator Offset @@ -71,7 +50,7 @@ trait IteratorInterval if (false === filter_var($offset, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]])) { throw new InvalidArgumentException('the offset must be a positive integer or 0'); } - $this->offset = $offset; + $this->iterator_offset = $offset; return $this; } @@ -88,7 +67,7 @@ trait IteratorInterval if (false === filter_var($limit, FILTER_VALIDATE_INT, ['options' => ['min_range' => -1]])) { throw new InvalidArgumentException('the limit must an integer greater or equals to -1'); } - $this->limit = $limit; + $this->iterator_limit = $limit; return $this; } @@ -100,16 +79,16 @@ trait IteratorInterval * * @return \LimitIterator */ - protected function applyInterval(Iterator $iterator) + protected function applyIteratorInterval(Iterator $iterator) { - if (0 == $this->offset && -1 == $this->limit) { + if (0 == $this->iterator_offset && -1 == $this->iterator_limit) { return $iterator; } - $offset = $this->offset; - $limit = $this->limit; + $offset = $this->iterator_offset; + $limit = $this->iterator_limit; - $this->limit = -1; - $this->offset = 0; + $this->iterator_limit = -1; + $this->iterator_offset = 0; return new LimitIterator($iterator, $offset, $limit); } diff --git a/src/Iterator/IteratorQuery.php b/src/Iterator/IteratorQuery.php deleted file mode 100644 index 5ec12e2..0000000 --- a/src/Iterator/IteratorQuery.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -/** -* League.csv - A CSV data manipulation library -* -* @author Ignace Nyamagana Butera <nyamsprod@gmail.com> -* @copyright 2014 Ignace Nyamagana Butera -* @link https://github.com/thephpleague/csv/ -* @license http://opensource.org/licenses/MIT -* @version 5.4.0 -* @package League.csv -* -* MIT LICENSE -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ -namespace League\Csv\Iterator; - -use Iterator; - -/** - * A Trait to Query in a SQL-like manner Iterators - * - * @package League.csv - * @since 4.0.0 - * - */ -trait IteratorQuery -{ - /** - * Iterator Filtering Trait - */ - use IteratorFilter; - - /** - * Iterator Sorting Trait - */ - use IteratorSortBy; - - /** - * Iterator Set Interval Trait - */ - use IteratorInterval; - - /** - * Return a filtered Iterator based on the filtering settings - * - * @param Iterator $iterator The iterator to be filtered - * @param callable $callable a callable function to be applied to each Iterator item - * - * @return Iterator - */ - protected function execute(Iterator $iterator, callable $callable = null) - { - $iterator = $this->applyFilter($iterator); - $iterator = $this->applySortBy($iterator); - $iterator = $this->applyInterval($iterator); - if (! is_null($callable)) { - $iterator = new MapIterator($iterator, $callable); - } - - return $iterator; - } -} diff --git a/src/Iterator/IteratorSortBy.php b/src/Iterator/IteratorSortBy.php index 9a64d92..9324364 100644 --- a/src/Iterator/IteratorSortBy.php +++ b/src/Iterator/IteratorSortBy.php @@ -1,34 +1,14 @@ <?php /** -* League.csv - A CSV data manipulation library +* This file is part of the League.csv library * -* @author Ignace Nyamagana Butera <nyamsprod@gmail.com> -* @copyright 2014 Ignace Nyamagana Butera -* @link https://github.com/thephpleague/csv/ * @license http://opensource.org/licenses/MIT -* @version 5.4.0 +* @link https://github.com/thephpleague/csv/ +* @version 5.5.0 * @package League.csv * -* MIT LICENSE -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. */ namespace League\Csv\Iterator; @@ -49,7 +29,7 @@ trait IteratorSortBy * * @var callable */ - private $sortBy = []; + protected $iterator_sort_by = []; /** * Set the Iterator SortBy method @@ -68,15 +48,15 @@ trait IteratorSortBy } /** - * Set an Iterator sortBy method + * Set an Iterator sorting callable function * - * @param callable $filter + * @param callable $callable * * @return self */ public function addSortBy(callable $callable) { - $this->sortBy[] = $callable; + $this->iterator_sort_by[] = $callable; return $this; } @@ -84,15 +64,15 @@ trait IteratorSortBy /** * Remove a callable from the collection * - * @param callable $filter + * @param callable $callable * * @return self */ public function removeSortBy(callable $callable) { - $res = array_search($callable, $this->sortBy, true); + $res = array_search($callable, $this->iterator_sort_by, true); if (false !== $res) { - unset($this->sortBy[$res]); + unset($this->iterator_sort_by[$res]); } return $this; @@ -101,13 +81,13 @@ trait IteratorSortBy /** * Detect if the callable is already registered * - * @param callable $filter + * @param callable $callable * * @return boolean */ public function hasSortBy(callable $callable) { - return false !== array_search($callable, $this->sortBy, true); + return false !== array_search($callable, $this->iterator_sort_by, true); } /** @@ -117,7 +97,7 @@ trait IteratorSortBy */ public function clearSortBy() { - $this->sortBy = []; + $this->iterator_sort_by = []; return $this; } @@ -129,22 +109,22 @@ trait IteratorSortBy * * @return \ArrayIterator */ - protected function applySortBy(Iterator $iterator) + protected function applyIteratorSortBy(Iterator $iterator) { - if (! $this->sortBy) { + if (! $this->iterator_sort_by) { return $iterator; } $res = iterator_to_array($iterator, false); uasort($res, function ($rowA, $rowB) { - foreach ($this->sortBy as $callable) { + foreach ($this->iterator_sort_by as $callable) { $res = $callable($rowA, $rowB); if (0 !== $res) { - break; + return $res; } } - return $res; + return 0; }); $this->clearSortBy(); diff --git a/src/Iterator/MapIterator.php b/src/Iterator/MapIterator.php index 6e3cc8c..3239902 100644 --- a/src/Iterator/MapIterator.php +++ b/src/Iterator/MapIterator.php @@ -1,34 +1,14 @@ <?php /** -* League.csv - A CSV data manipulation library +* This file is part of the League.csv library * -* @author Ignace Nyamagana Butera <nyamsprod@gmail.com> -* @copyright 2014 Ignace Nyamagana Butera -* @link https://github.com/thephpleague/csv/ * @license http://opensource.org/licenses/MIT -* @version 5.4.0 +* @link https://github.com/thephpleague/csv/ +* @version 5.5.0 * @package League.csv * -* MIT LICENSE -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. */ namespace League\Csv\Iterator; diff --git a/src/Reader.php b/src/Reader.php index 15056ac..f8c733b 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -1,42 +1,24 @@ <?php /** -* League.csv - A CSV data manipulation library +* This file is part of the League.csv library * -* @author Ignace Nyamagana Butera <nyamsprod@gmail.com> -* @copyright 2014 Ignace Nyamagana Butera -* @link https://github.com/thephpleague/csv/ * @license http://opensource.org/licenses/MIT -* @version 5.4.0 +* @link https://github.com/thephpleague/csv/ +* @version 5.5.0 * @package League.csv * -* MIT LICENSE -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. */ namespace League\Csv; use InvalidArgumentException; - +use Iterator; use CallbackFilterIterator; use League\Csv\Iterator\MapIterator; -use League\Csv\Iterator\IteratorQuery; +use League\Csv\Iterator\IteratorFilter; +use League\Csv\Iterator\IteratorSortBy; +use League\Csv\Iterator\IteratorInterval; /** * A class to manage extracting and filtering a CSV @@ -48,13 +30,31 @@ use League\Csv\Iterator\IteratorQuery; class Reader extends AbstractCsv { /** - * Iterator Query Trait + * Iterator Filtering Trait */ - use IteratorQuery; + use IteratorFilter; + + /** + * Iterator Sorting Trait + */ + use IteratorSortBy; + + /** + * Iterator Set Interval Trait + */ + use IteratorInterval; + + /** + * {@ihneritdoc} + */ + protected $stream_filter_mode = STREAM_FILTER_READ; /** * Intelligent Array Combine * + * add or remove values from the $value array to + * match array $keys length before using PHP array_combine function + * * @param array $keys * @param array $value * @@ -78,7 +78,7 @@ class Reader extends AbstractCsv * * @param callable $callable a callable function to be applied to each Iterator item * - * @return \Iterator + * @return \Traversable */ public function query(callable $callable = null) { @@ -86,7 +86,14 @@ class Reader extends AbstractCsv return is_array($row); }); - return $this->execute($iterator, $callable); + $iterator = $this->applyIteratorFilter($iterator); + $iterator = $this->applyIteratorSortBy($iterator); + $iterator = $this->applyIteratorInterval($iterator); + if (! is_null($callable)) { + $iterator = new MapIterator($iterator, $callable); + } + + return $iterator; } /** @@ -100,11 +107,7 @@ class Reader extends AbstractCsv */ public function each(callable $callable) { - $iterator = new CallbackFilterIterator($this->getIterator(), function ($row) { - return is_array($row); - }); - - $iterator = $this->execute($iterator); + $iterator = $this->query(); $index = 0; foreach ($iterator as $rowIndex => $row) { if (true !== $callable($row, $rowIndex, $iterator)) { @@ -190,8 +193,8 @@ class Reader extends AbstractCsv * * @deprecated deprecated since version 5.4 * - * @param integer $fieldIndex field Index - * @param callable $callable a callable function to be applied to each value to be return + * @param integer $column_index field Index + * @param callable $callable a callable function to be applied to each value to be return * * @return array * @@ -205,8 +208,8 @@ class Reader extends AbstractCsv /** * Return a single column from the CSV data * - * @param integer $fieldIndex field Index - * @param callable $callable a callable function to be applied to each value to be return + * @param integer $column_index field Index + * @param callable $callable a callable function to be applied to each value to be return * * @return array * @@ -233,14 +236,12 @@ class Reader extends AbstractCsv } /** - * Instantiate a {@link Writer} class from the current {@link Reader} - * - * @param string $open_mode the file open mode flag + * DEPRECATION WARNING! This method will be removed in the next major point release * - * @return \League\Csv\Writer + * @deprecated deprecated since version 5.5 */ public function getWriter($open_mode = 'r+') { - return $this->getInstance('\League\Csv\Writer', $open_mode); + return $this->createWriter($open_mode); } } diff --git a/src/Stream/StreamFilter.php b/src/Stream/StreamFilter.php new file mode 100644 index 0000000..1f2ac6e --- /dev/null +++ b/src/Stream/StreamFilter.php @@ -0,0 +1,259 @@ +<?php +/** +* This file is part of the League.csv library +* +* @license http://opensource.org/licenses/MIT +* @link https://github.com/thephpleague/csv/ +* @version 5.5.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\Stream; + +use SplFileInfo; +use SplTempFileObject; +use InvalidArgumentException; +use RuntimeException; +use OutOfBoundsException; + +/** + * A Trait to add ease manipulation Stream Filters + * + * @package League.csv + * @since 5.5.0 + * + */ +trait StreamFilter +{ + /** + * collection of stream filters + * + * @var array + */ + protected $stream_filters = []; + + /** + * Stream filtering mode to apply on all filters + * + * @var integer + */ + protected $stream_filter_mode = STREAM_FILTER_ALL; + + /** + *the real path + * + * @var string the real path to the file + * + */ + protected $stream_real_path; + + /** + * Internal path setter + * + * @param mixed $path can be a SplFileInfo object or the path to a file + * + * @return self + * + * @throws InvalidArgumentException If $path is invalid + */ + protected function initStreamFilter($path) + { + if ($path instanceof SplTempFileObject) { + $this->stream_real_path = null; + + return $this; + + } elseif ($path instanceof SplFileInfo) { + //$path->getRealPath() returns false for php stream wrapper + $path = $path->getPath().'/'.$path->getBasename(); + } + + $path = trim($path); + //if we are submitting a filter meta wrapper + //we extract and inject the mode, the filter and the path + if (preg_match( + ',^php://filter/(?P<mode>:?read=|write=)?(?P<filters>.*?)/resource=(?P<resource>.*)$,i', + $path, + $matches + )) { + $matches['mode'] = strtolower($matches['mode']); + $mode = STREAM_FILTER_ALL; + if ('write=' == $matches['mode']) { + $mode = STREAM_FILTER_WRITE; + } elseif ('read=' == $matches['mode']) { + $mode = STREAM_FILTER_READ; + } + $this->stream_filter_mode = $mode; + $this->stream_real_path = $matches['resource']; + $this->stream_filters = explode('|', $matches['filters']); + + return $this; + } + + $this->stream_real_path = $path; + $this->stream_filters = []; + + return $this; + } + + /** + * stream filter mode Setter + * + * Set the new Stream Filter mode and remove all + * previously attached stream filters + * + * @param integer $mode + * + * @return self + */ + public function setStreamFilterMode($mode) + { + if (! in_array($mode, [STREAM_FILTER_ALL, STREAM_FILTER_READ, STREAM_FILTER_WRITE])) { + throw new OutOfBoundsException('the $mode should be a valid `STREAM_FILTER_*` constant'); + } + + $this->stream_filter_mode = $mode; + $this->stream_filters = []; + + return $this; + } + + /** + * stream filter mode getter + * + * @return integer + */ + public function getStreamFilterMode() + { + return $this->stream_filter_mode; + } + + /** + * Sanitize the stream filter name + * + * @param string $filter_name the stream filter name + * + * @return string + * + * @throws \InvalidArgumentException If $filter_name is not a string + */ + protected function sanitizeStreamFilter($filter_name) + { + if (! is_string($filter_name)) { + throw new InvalidArgumentException( + 'the filtername variable must be a string' + ); + } + + return trim($filter_name); + } + + /** + * append a stream filter + * + * @param string $filter_name a string or an object that implements the '__toString' method + * + * @return self + * + * @throws \InvalidArgumentException If what you try to add is invalid + * @throws \RuntimeException If adding Stream Filter is not possible + */ + public function appendStreamFilter($filter_name) + { + if (is_null($this->stream_real_path)) { + throw new RuntimeException( + 'no stream path found, you can not append a stream filter' + ); + } + $this->stream_filters[] = $this->sanitizeStreamFilter($filter_name); + + return $this; + } + + /** + * prepend a stream filter + * + * @param string $filter_name a string or an object that implements the '__toString' method + * + * @return self + * + * @throws \InvalidArgumentException If what you try to add is invalid + * @throws \RuntimeException If adding Stream Filter is not possible + */ + public function prependStreamFilter($filter_name) + { + if (is_null($this->stream_real_path)) { + throw new RuntimeException( + 'no stream path found, you can not prepend a stream filter' + ); + } + + array_unshift($this->stream_filters, $this->sanitizeStreamFilter($filter_name)); + + return $this; + } + + /** + * Detect if the stream filter is already present + * + * @param string $filter_name + * + * @return boolean + */ + public function hasStreamFilter($filter_name) + { + return false !== array_search($filter_name, $this->stream_filters, true); + } + + /** + * Remove a filter from the collection + * + * @param string $filter_name + * + * @return self + */ + public function removeStreamFilter($filter_name) + { + $res = array_search($filter_name, $this->stream_filters, true); + if (false !== $res) { + unset($this->stream_filters[$res]); + } + + return $this; + } + + /** + * Remove all registered stream filter + * + * @return self + */ + public function clearStreamFilter() + { + $this->stream_filters = []; + + return $this; + } + + /** + * Return the filter path + * + * @return string + */ + protected function getStreamFilterPath() + { + if (! $this->stream_filters) { + return $this->stream_real_path; + } + + $prefix = ''; + if (STREAM_FILTER_READ == $this->stream_filter_mode) { + $prefix = 'read='; + } elseif (STREAM_FILTER_WRITE == $this->stream_filter_mode) { + $prefix = 'write='; + } + + return 'php://filter/'.$prefix.implode('|', $this->stream_filters).'/resource='.$this->stream_real_path; + } +} diff --git a/src/Writer.php b/src/Writer.php index d379d7d..02a2da1 100644 --- a/src/Writer.php +++ b/src/Writer.php @@ -1,39 +1,19 @@ <?php /** -* League.csv - A CSV data manipulation library +* This file is part of the League.csv library * -* @author Ignace Nyamagana Butera <nyamsprod@gmail.com> -* @copyright 2014 Ignace Nyamagana Butera -* @link https://github.com/thephpleague/csv/ * @license http://opensource.org/licenses/MIT -* @version 5.4.0 +* @link https://github.com/thephpleague/csv/ +* @version 5.5.0 * @package League.csv * -* MIT LICENSE -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. */ namespace League\Csv; use Traversable; - +use SplFileObject; use InvalidArgumentException; use OutOfBoundsException; @@ -83,6 +63,27 @@ class Writer extends AbstractCsv protected $detect_columns_count = false; /** + * {@ihneritdoc} + */ + protected $stream_filter_mode = STREAM_FILTER_WRITE; + + /** + * The CSV object holder + * + * @var \SplFileObject + */ + protected $csv; + + /** + * The destructor + */ + public function __destruct() + { + $this->csv = null; + parent::__destruct(); + } + + /** * Tell the class how to handle null value * * @param integer $value a Writer null behavior constant @@ -213,7 +214,7 @@ class Writer extends AbstractCsv */ private function validateRow($row) { - //convert input row into a proper array + //convert input string row into a proper array if (self::isValidString($row)) { $row = str_getcsv((string) $row, $this->delimiter, $this->enclosure, $this->escape); } @@ -224,6 +225,7 @@ class Writer extends AbstractCsv ); } + //validate row according to null handling mode $check = array_filter($row, function ($value) { return (is_null($value) && self::NULL_AS_EXCEPTION != $this->null_handling_mode) || self::isValidString($value); @@ -239,6 +241,30 @@ class Writer extends AbstractCsv } /** + * set the csv container as a SplFileObject instance + * insure we use the same object for insertion to + * avoid loosing the cursor position + * + * @return SplFileObject + * + * @throws \RuntimeException If the file could not be created and/or opened + */ + protected function getCsv() + { + if (! is_null($this->csv)) { + return $this->csv; + } elseif ($this->path instanceof SplFileObject) { + $this->csv = $this->path; + + return $this->csv; + } + + $this->csv = new SplFileObject($this->getStreamFilterPath(), $this->open_mode); + + return $this->csv; + } + + /** * Add a new CSV row to the generated CSV * * @param mixed $data a string, an array or an object implementing to '__toString' method @@ -257,8 +283,7 @@ class Writer extends AbstractCsv that requires '.$this->columns_count.' columns per row.' ); } - - $this->csv->fputcsv($data, $this->delimiter, $this->enclosure); + $this->getCsv()->fputcsv($data, $this->delimiter, $this->enclosure); return $this; } @@ -266,6 +291,8 @@ class Writer extends AbstractCsv /** * Add multiple lines to the CSV your are generating * + * a simple helper/Wrapper method around insertOne + * * @param mixed $rows a multidimentional array or a Traversable object * * @return self @@ -288,14 +315,12 @@ class Writer extends AbstractCsv } /** - * Instantiate a {@link Reader} class from the current {@link Writer} - * - * @param string $open_mode the file open mode flag + * DEPRECATION WARNING! This method will be removed in the next major point release * - * @return \League\Csv\Reader + * @deprecated deprecated since version 5.5 */ public function getReader($open_mode = 'r+') { - return $this->getInstance('\League\Csv\Reader', $open_mode); + return $this->createReader($open_mode); } } |