LICENSE 0000666 00000002107 13052363017 0005555 0 ustar 00 The MIT License (MIT)
Copyright (c) 2013-2015 ignace nyamagana butera
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.
composer.json 0000666 00000002352 13052363017 0007274 0 ustar 00 {
"name": "league/csv",
"type": "library",
"description" : "Csv data manipulation made easy in PHP",
"keywords": ["csv", "import", "export", "read", "write", "filter"],
"license": "MIT",
"homepage" : "http://csv.thephpleague.com",
"authors": [
{
"name" : "Ignace Nyamagana Butera",
"email" : "nyamsprod@gmail.com",
"homepage" : "https://github.com/nyamsprod/",
"role" : "Developer"
}
],
"support": {
"forum": "https://groups.google.com/forum/#!forum/thephpleague",
"issues": "https://github.com/thephpleague/csv/issues"
},
"require": {
"php" : ">=5.4.0",
"ext-mbstring" : "*"
},
"require-dev": {
"phpunit/phpunit" : "^4.0",
"fabpot/php-cs-fixer": "^1.9"
},
"autoload": {
"psr-4": {
"League\\Csv\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"League\\Csv\\Test\\": "test",
"lib\\": "examples\\lib"
}
},
"scripts": {
"test": "vendor/bin/phpunit; vendor/bin/php-cs-fixer fix -v --diff --dry-run;"
},
"extra": {
"branch-alias": {
"dev-master": "7.2-dev"
}
}
}
src/AbstractCsv.php 0000666 00000020132 13052363017 0010265 0 ustar 00 defaultFlags = SplFileObject::READ_CSV | SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY;
$this->flags = $this->defaultFlags;
$this->open_mode = strtolower($open_mode);
$this->path = $path;
$this->initStreamFilter($this->path);
}
/**
* The destructor
*/
public function __destruct()
{
$this->path = null;
}
/**
* Returns the CSV Iterator
*
* @return SplFileObject
*/
public function getIterator()
{
$iterator = $this->path;
if (!$iterator instanceof SplFileObject) {
$iterator = new SplFileObject($this->getStreamFilterPath(), $this->open_mode);
}
$iterator->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
$iterator->setFlags($this->flags);
return $iterator;
}
/**
* Returns the CSV Iterator for conversion
*
* @return Iterator
*/
protected function getConversionIterator()
{
$iterator = $this->getIterator();
$iterator->setFlags($this->defaultFlags);
$iterator = $this->applyBomStripping($iterator);
$iterator = $this->applyIteratorFilter($iterator);
$iterator = $this->applyIteratorSortBy($iterator);
return $this->applyIteratorInterval($iterator);
}
/**
* Creates a {@link AbstractCsv} from a string
*
* The path can be:
* - an SplFileInfo,
* - a SplFileObject,
* - an object that implements the `__toString` method,
* - a string
*
* BUT NOT a SplTempFileObject
*
*
*
*
*
* @param mixed $path file path
* @param string $open_mode the file open mode flag
*
* @throws InvalidArgumentException If $path is a \SplTempFileObject object
*
* @return static
*/
public static function createFromPath($path, $open_mode = 'r+')
{
if ($path instanceof SplTempFileObject) {
throw new InvalidArgumentException('an `SplTempFileObject` object does not contain a valid path');
}
if ($path instanceof SplFileInfo) {
$path = $path->getPath().'/'.$path->getBasename();
}
return new static(static::validateString($path), $open_mode);
}
/**
* validate a string
*
* @param mixed $str the value to evaluate as a string
*
* @throws InvalidArgumentException if the submitted data can not be converted to string
*
* @return string
*/
protected static function validateString($str)
{
if (is_string($str) || (is_object($str) && method_exists($str, '__toString'))) {
return (string) $str;
}
throw new InvalidArgumentException('Expected data must be a string or stringable');
}
/**
* Creates a {@link AbstractCsv} from a SplFileObject
*
* The path can be:
* - a SplFileObject,
* - a SplTempFileObject
*
*
*
*
*
* @param SplFileObject $file
*
* @return static
*/
public static function createFromFileObject(SplFileObject $file)
{
return new static($file);
}
/**
* Creates a {@link AbstractCsv} from a string
*
* The string must be an object that implements the `__toString` method,
* or a string
*
* @param string $str the string
* @param string $newline the newline character
*
* @return static
*/
public static function createFromString($str, $newline = "\n")
{
$file = new SplTempFileObject();
$file->fwrite(static::validateString($str));
$csv = static::createFromFileObject($file);
$csv->setNewline($newline);
return $csv;
}
/**
* Creates a {@link AbstractCsv} instance from another {@link AbstractCsv} object
*
* @param string $class_name the class to be instantiated
* @param string $open_mode the file open mode flag
*
* @return static
*/
protected function newInstance($class_name, $open_mode)
{
$csv = new $class_name($this->path, $open_mode);
$csv->delimiter = $this->delimiter;
$csv->enclosure = $this->enclosure;
$csv->escape = $this->escape;
$csv->encodingFrom = $this->encodingFrom;
$csv->flags = $this->flags;
$csv->input_bom = $this->input_bom;
$csv->output_bom = $this->output_bom;
$csv->newline = $this->newline;
return $csv;
}
/**
* Creates a {@link Writer} instance from a {@link AbstractCsv} object
*
* @param string $open_mode the file open mode flag
*
* @return Writer
*/
public function newWriter($open_mode = 'r+')
{
return $this->newInstance('\League\Csv\Writer', $open_mode);
}
/**
* Creates a {@link Reader} instance from a {@link AbstractCsv} object
*
* @param string $open_mode the file open mode flag
*
* @return Reader
*/
public function newReader($open_mode = 'r+')
{
return $this->newInstance('\League\Csv\Reader', $open_mode);
}
/**
* Validate the submitted integer
*
* @param int $int
* @param int $minValue
* @param string $errorMessage
*
* @throws InvalidArgumentException If the value is invalid
*
* @return int
*/
protected function filterInteger($int, $minValue, $errorMessage)
{
if (false === ($int = filter_var($int, FILTER_VALIDATE_INT, ['options' => ['min_range' => $minValue]]))) {
throw new InvalidArgumentException($errorMessage);
}
return $int;
}
}
src/Config/Controls.php 0000666 00000015415 13052363017 0011066 0 ustar 00 isValidCsvControls($delimiter)) {
throw new InvalidArgumentException('The delimiter must be a single character');
}
$this->delimiter = $delimiter;
return $this;
}
/**
* Tell whether the submitted string is a valid CSV Control character
*
* @param string $str The submitted string
*
* @return bool
*/
protected function isValidCsvControls($str)
{
return 1 == mb_strlen($str);
}
/**
* Returns the current field delimiter
*
* @return string
*/
public function getDelimiter()
{
return $this->delimiter;
}
/**
* Detects the CSV file delimiters
*
* Returns a associative array where each key represents
* the number of occurences and each value a delimiter with the
* given occurence
*
* This method returns incorrect informations when two delimiters
* have the same occurrence count
*
* DEPRECATION WARNING! This method will be removed in the next major point release
*
* @deprecated deprecated since version 7.2
*
* @param int $nb_rows
* @param string[] $delimiters additional delimiters
*
* @return string[]
*/
public function detectDelimiterList($nb_rows = 1, array $delimiters = [])
{
$delimiters = array_merge([$this->delimiter, ',', ';', "\t"], $delimiters);
$stats = $this->fetchDelimitersOccurrence($delimiters, $nb_rows);
return array_flip(array_filter($stats));
}
/**
* Detect Delimiters occurences in the CSV
*
* Returns a associative array where each key represents
* a valid delimiter and each value the number of occurences
*
* @param string[] $delimiters the delimiters to consider
* @param int $nb_rows Detection is made using $nb_rows of the CSV
*
* @throws InvalidArgumentException If $nb_rows value is invalid
*
* @return array
*/
public function fetchDelimitersOccurrence(array $delimiters, $nb_rows = 1)
{
if (!($nb_rows = filter_var($nb_rows, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]))) {
throw new InvalidArgumentException('The number of rows to consider must be a valid positive integer');
}
$filterRow = function ($row) {
return is_array($row) && count($row) > 1;
};
$delimiters = array_unique(array_filter($delimiters, [$this, 'isValidCsvControls']));
$csv = $this->getIterator();
$res = [];
foreach ($delimiters as $delim) {
$csv->setCsvControl($delim, $this->enclosure, $this->escape);
$iterator = new CallbackFilterIterator(new LimitIterator($csv, 0, $nb_rows), $filterRow);
$res[$delim] = count(iterator_to_array($iterator, false), COUNT_RECURSIVE);
}
arsort($res, SORT_NUMERIC);
return $res;
}
/**
* Returns the CSV Iterator
*
* @return SplFileObject
*/
abstract public function getIterator();
/**
* Sets the field enclosure
*
* @param string $enclosure
*
* @throws InvalidArgumentException If $enclosure is not a single character
*
* @return $this
*/
public function setEnclosure($enclosure)
{
if (!$this->isValidCsvControls($enclosure)) {
throw new InvalidArgumentException('The enclosure must be a single character');
}
$this->enclosure = $enclosure;
return $this;
}
/**
* Returns the current field enclosure
*
* @return string
*/
public function getEnclosure()
{
return $this->enclosure;
}
/**
* Sets the field escape character
*
* @param string $escape
*
* @throws InvalidArgumentException If $escape is not a single character
*
* @return $this
*/
public function setEscape($escape)
{
if (!$this->isValidCsvControls($escape)) {
throw new InvalidArgumentException('The escape character must be a single character');
}
$this->escape = $escape;
return $this;
}
/**
* Returns the current field escape character
*
* @return string
*/
public function getEscape()
{
return $this->escape;
}
/**
* Sets the Flags associated to the CSV SplFileObject
*
* @param int $flags
*
* @throws InvalidArgumentException If the argument is not a valid integer
*
* @return $this
*/
public function setFlags($flags)
{
$flags = $this->filterInteger($flags, 0, 'you should use a `SplFileObject` Constant');
$this->flags = $flags | SplFileObject::READ_CSV;
return $this;
}
/**
* @inheritdoc
*/
abstract protected function filterInteger($int, $minValue, $errorMessage);
/**
* Returns the file Flags
*
* @return int
*/
public function getFlags()
{
return $this->flags;
}
/**
* Sets the newline sequence characters
*
* @param string $newline
*
* @return static
*/
public function setNewline($newline)
{
$this->newline = (string) $newline;
return $this;
}
/**
* Returns the current newline sequence characters
*
* @return string
*/
public function getNewline()
{
return $this->newline;
}
}
src/Config/Output.php 0000666 00000015440 13052363017 0010561 0 ustar 00 FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH]);
if (empty($str)) {
throw new InvalidArgumentException('you should use a valid charset');
}
$this->encodingFrom = strtoupper($str);
return $this;
}
/**
* Gets the source CSV encoding charset
*
* @return string
*/
public function getEncodingFrom()
{
return $this->encodingFrom;
}
/**
* Sets the BOM sequence to prepend the CSV on output
*
* @param string $str The BOM sequence
*
* @return static
*/
public function setOutputBOM($str = null)
{
if (empty($str)) {
$this->output_bom = null;
return $this;
}
$this->output_bom = (string) $str;
return $this;
}
/**
* Returns the BOM sequence in use on Output methods
*
* @return string
*/
public function getOutputBOM()
{
return $this->output_bom;
}
/**
* Returns the BOM sequence of the given CSV
*
* @return string
*/
public function getInputBOM()
{
if (! $this->input_bom) {
$bom = [
self::BOM_UTF32_BE, self::BOM_UTF32_LE,
self::BOM_UTF16_BE, self::BOM_UTF16_LE, self::BOM_UTF8,
];
$csv = $this->getIterator();
$csv->setFlags(SplFileObject::READ_CSV);
$csv->rewind();
$line = $csv->fgets();
$res = array_filter($bom, function ($sequence) use ($line) {
return strpos($line, $sequence) === 0;
});
$this->input_bom = array_shift($res);
}
return $this->input_bom;
}
/**
* Outputs all data on the CSV file
*
* @param string $filename CSV downloaded name if present adds extra headers
*
* @return int Returns the number of characters read from the handle
* and passed through to the output.
*/
public function output($filename = null)
{
if (!is_null($filename)) {
$filename = filter_var($filename, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
header('Content-Type: application/octet-stream');
header('Content-Transfer-Encoding: binary');
header("Content-Disposition: attachment; filename=\"$filename\"");
}
return $this->fpassthru();
}
/**
* Outputs all data from the CSV
*
* @return int Returns the number of characters read from the handle
* and passed through to the output.
*/
protected function fpassthru()
{
$bom = '';
$input_bom = $this->getInputBOM();
if ($this->output_bom && $input_bom != $this->output_bom) {
$bom = $this->output_bom;
}
$csv = $this->getIterator();
$csv->setFlags(SplFileObject::READ_CSV);
$csv->rewind();
if (!empty($bom)) {
$csv->fseek(mb_strlen($input_bom));
}
echo $bom;
$res = $csv->fpassthru();
return $res + strlen($bom);
}
/**
* Retrieves the CSV content
*
* @return string
*/
public function __toString()
{
ob_start();
$this->fpassthru();
return ob_get_clean();
}
/**
* JsonSerializable Interface
*
* @return array
*/
public function jsonSerialize()
{
return iterator_to_array($this->convertToUtf8($this->getConversionIterator()), false);
}
/**
* Convert Csv file into UTF-8
*
* @param Iterator $iterator
*
* @return Iterator
*/
protected function convertToUtf8(Iterator $iterator)
{
if (strpos($this->encodingFrom, 'UTF-8') !== false) {
return $iterator;
}
return new MapIterator($iterator, function ($row) {
foreach ($row as &$value) {
$value = mb_convert_encoding($value, 'UTF-8', $this->encodingFrom);
}
unset($value);
return $row;
});
}
/**
* Returns a HTML table representation of the CSV Table
*
* @param string $class_name optional classname
*
* @return string
*/
public function toHTML($class_name = 'table-csv-data')
{
$doc = $this->toXML('table', 'tr', 'td');
$doc->documentElement->setAttribute('class', $class_name);
return $doc->saveHTML($doc->documentElement);
}
/**
* Transforms a CSV into a XML
*
* @param string $root_name XML root node name
* @param string $row_name XML row node name
* @param string $cell_name XML cell node name
*
* @return DomDocument
*/
public function toXML($root_name = 'csv', $row_name = 'row', $cell_name = 'cell')
{
$doc = new DomDocument('1.0', 'UTF-8');
$root = $doc->createElement($root_name);
$iterator = $this->convertToUtf8($this->getConversionIterator());
foreach ($iterator as $row) {
$item = $doc->createElement($row_name);
array_walk($row, function ($value) use (&$item, $doc, $cell_name) {
$content = $doc->createTextNode($value);
$cell = $doc->createElement($cell_name);
$cell->appendChild($content);
$item->appendChild($cell);
});
$root->appendChild($item);
}
$doc->appendChild($root);
return $doc;
}
}
src/Exception/InvalidRowException.php 0000666 00000002566 13052363017 0013754 0 ustar 00 name = $name;
$this->data = $data;
}
/**
* return the validator name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* return the invalid data submitted
*
* @return array
*/
public function getData()
{
return $this->data;
}
}
src/Modifier/MapIterator.php 0000666 00000002300 13052363017 0012030 0 ustar 00 callable = $callable;
}
/**
* Get the value of the current element
*/
public function current()
{
$iterator = $this->getInnerIterator();
return call_user_func($this->callable, $iterator->current(), $iterator->key(), $iterator);
}
}
src/Modifier/QueryFilter.php 0000666 00000016521 13052363017 0012066 0 ustar 00 strip_bom = (bool) $status;
return $this;
}
/**
* Tell whether we can strip or not the leading BOM sequence
*
* @return bool
*/
protected function isBomStrippable()
{
$bom = $this->getInputBom();
return ! empty($bom) && $this->strip_bom;
}
/**
* {@inheritdoc}
*/
abstract public function getInputBom();
/**
* Set LimitIterator Offset
*
* @param $offset
*
* @return $this
*/
public function setOffset($offset = 0)
{
$this->iterator_offset = $this->filterInteger($offset, 0, 'the offset must be a positive integer or 0');
return $this;
}
/**
* @inheritdoc
*/
abstract protected function filterInteger($int, $minValue, $errorMessage);
/**
* Set LimitIterator Count
*
* @param int $limit
*
* @return $this
*/
public function setLimit($limit = -1)
{
$this->iterator_limit = $this->filterInteger($limit, -1, 'the limit must an integer greater or equals to -1');
return $this;
}
/**
* Set an Iterator sorting callable function
*
* @param callable $callable
*
* @return $this
*/
public function addSortBy(callable $callable)
{
$this->iterator_sort_by[] = $callable;
return $this;
}
/**
* Remove a callable from the collection
*
* @param callable $callable
*
* @return $this
*/
public function removeSortBy(callable $callable)
{
$res = array_search($callable, $this->iterator_sort_by, true);
unset($this->iterator_sort_by[$res]);
return $this;
}
/**
* Detect if the callable is already registered
*
* @param callable $callable
*
* @return bool
*/
public function hasSortBy(callable $callable)
{
return false !== array_search($callable, $this->iterator_sort_by, true);
}
/**
* Remove all registered callable
*
* @return $this
*/
public function clearSortBy()
{
$this->iterator_sort_by = [];
return $this;
}
/**
* Set the Iterator filter method
*
* @param callable $callable
*
* @return $this
*/
public function addFilter(callable $callable)
{
$this->iterator_filters[] = $callable;
return $this;
}
/**
* Remove a filter from the callable collection
*
* @param callable $callable
*
* @return $this
*/
public function removeFilter(callable $callable)
{
$res = array_search($callable, $this->iterator_filters, true);
unset($this->iterator_filters[$res]);
return $this;
}
/**
* Detect if the callable filter is already registered
*
* @param callable $callable
*
* @return bool
*/
public function hasFilter(callable $callable)
{
return false !== array_search($callable, $this->iterator_filters, true);
}
/**
* Remove all registered callable filter
*
* @return $this
*/
public function clearFilter()
{
$this->iterator_filters = [];
return $this;
}
/**
* Remove the BOM sequence from the CSV
*
* @param Iterator $iterator
*
* @return \Iterator
*/
protected function applyBomStripping(Iterator $iterator)
{
if (! $this->strip_bom) {
return $iterator;
}
if (! $this->isBomStrippable()) {
$this->strip_bom = false;
return $iterator;
}
$this->strip_bom = false;
return $this->getStripBomIterator($iterator);
}
/**
* Return the Iterator without the BOM sequence
*
* @param Iterator $iterator
*
* @return Iterator
*/
protected function getStripBomIterator(Iterator $iterator)
{
$bom = $this->getInputBom();
return new MapIterator($iterator, function ($row, $index) use ($bom) {
if (0 == $index) {
$row[0] = mb_substr($row[0], mb_strlen($bom));
$enclosure = $this->getEnclosure();
//enclosure should be remove when a BOM sequence is stripped
if ($row[0][0] === $enclosure && mb_substr($row[0], -1, 1) == $enclosure) {
$row[0] = mb_substr($row[0], 1, -1);
}
}
return $row;
});
}
/**
* {@inheritdoc}
*/
abstract public function getEnclosure();
/**
* Filter the Iterator
*
* @param \Iterator $iterator
*
* @return \Iterator
*/
protected function applyIteratorFilter(Iterator $iterator)
{
foreach ($this->iterator_filters as $callable) {
$iterator = new CallbackFilterIterator($iterator, $callable);
}
$this->clearFilter();
return $iterator;
}
/**
* Sort the Iterator
*
* @param \Iterator $iterator
*
* @return \Iterator
*/
protected function applyIteratorInterval(Iterator $iterator)
{
if (0 == $this->iterator_offset && -1 == $this->iterator_limit) {
return $iterator;
}
$offset = $this->iterator_offset;
$limit = $this->iterator_limit;
$this->iterator_limit = -1;
$this->iterator_offset = 0;
return new LimitIterator($iterator, $offset, $limit);
}
/**
* Sort the Iterator
*
* @param \Iterator $iterator
*
* @return \Iterator
*/
protected function applyIteratorSortBy(Iterator $iterator)
{
if (! $this->iterator_sort_by) {
return $iterator;
}
$obj = new ArrayObject(iterator_to_array($iterator, false));
$obj->uasort(function ($rowA, $rowB) {
$sortRes = 0;
foreach ($this->iterator_sort_by as $callable) {
if (0 !== ($sortRes = call_user_func($callable, $rowA, $rowB))) {
break;
}
}
return $sortRes;
});
$this->clearSortBy();
return $obj->getIterator();
}
}
src/Modifier/RowFilter.php 0000666 00000007122 13052363017 0011525 0 ustar 00 formatters[] = $callable;
return $this;
}
/**
* Remove a formatter from the collection
*
* @param callable $callable
*
* @return $this
*/
public function removeFormatter(callable $callable)
{
$res = array_search($callable, $this->formatters, true);
unset($this->formatters[$res]);
return $this;
}
/**
* Detect if the formatter is already registered
*
* @param callable $callable
*
* @return bool
*/
public function hasFormatter(callable $callable)
{
return false !== array_search($callable, $this->formatters, true);
}
/**
* Remove all registered formatter
*
* @return $this
*/
public function clearFormatters()
{
$this->formatters = [];
return $this;
}
/**
* add a Validator to the collection
*
* @param callable $callable
* @param string $name the rule name
*
* @return $this
*/
public function addValidator(callable $callable, $name)
{
$this->validators[$name] = $callable;
return $this;
}
/**
* Remove a validator from the collection
*
* @param string $name the validator name
*
* @return $this
*/
public function removeValidator($name)
{
if (array_key_exists($name, $this->validators)) {
unset($this->validators[$name]);
}
return $this;
}
/**
* Detect if a validator is already registered
*
* @param string $name the validator name
*
* @return bool
*/
public function hasValidator($name)
{
return array_key_exists($name, $this->validators);
}
/**
* Remove all registered validators
*
* @return $this
*/
public function clearValidators()
{
$this->validators = [];
return $this;
}
/**
* Format the given row
*
* @param array|string $row
*
* @return array
*/
protected function formatRow(array $row)
{
foreach ($this->formatters as $formatter) {
$row = call_user_func($formatter, $row);
}
return $row;
}
/**
* validate a row
*
* @param array $row
*
* @throws InvalidRowException If the validation failed
*/
protected function validateRow(array $row)
{
foreach ($this->validators as $name => $validator) {
if (true !== call_user_func($validator, $row)) {
throw new InvalidRowException($name, $row, 'row validation failed');
}
}
}
}
src/Modifier/StreamFilter.php 0000666 00000015063 13052363017 0012214 0 ustar 00 :?read=|write=)? # The resource open mode
(?P.*?) # The resource registered filters
/resource=(?P.*) # The resource path
$,ix';
/**
* Internal path setter
*
* The path must be an SplFileInfo object
* an object that implements the `__toString` method
* a path to a file
*
* @param \SplFileObject|string $path The file path
*/
protected function initStreamFilter($path)
{
$this->stream_filters = [];
if (! is_string($path)) {
$this->stream_uri = null;
return;
}
if (! preg_match($this->stream_regex, $path, $matches)) {
$this->stream_uri = $path;
return;
}
$this->stream_uri = $matches['resource'];
$this->stream_filters = explode('|', $matches['filters']);
$this->stream_filter_mode = $this->fetchStreamModeAsInt($matches['mode']);
}
/**
* Get the stream mode
*
* @param string $mode
*
* @return int
*/
protected function fetchStreamModeAsInt($mode)
{
$mode = strtolower($mode);
$mode = rtrim($mode, '=');
if ('write' == $mode) {
return STREAM_FILTER_WRITE;
}
if ('read' == $mode) {
return STREAM_FILTER_READ;
}
return STREAM_FILTER_ALL;
}
/**
* Check if the trait methods can be used
*
* @throws LogicException If the API can not be use
*/
protected function assertStreamable()
{
if (!is_string($this->stream_uri)) {
throw new LogicException('The stream filter API can not be used');
}
}
/**
* Tells whether the stream filter capabilities can be used
*
* @return bool
*/
public function isActiveStreamFilter()
{
return is_string($this->stream_uri);
}
/**
* stream filter mode Setter
*
* Set the new Stream Filter mode and remove all
* previously attached stream filters
*
* @param int $mode
*
* @throws OutOfBoundsException If the mode is invalid
*
* @return $this
*/
public function setStreamFilterMode($mode)
{
$this->assertStreamable();
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 int
*/
public function getStreamFilterMode()
{
$this->assertStreamable();
return $this->stream_filter_mode;
}
/**
* append a stream filter
*
* @param string $filter_name a string or an object that implements the '__toString' method
*
* @return $this
*/
public function appendStreamFilter($filter_name)
{
$this->assertStreamable();
$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 $this
*/
public function prependStreamFilter($filter_name)
{
$this->assertStreamable();
array_unshift($this->stream_filters, $this->sanitizeStreamFilter($filter_name));
return $this;
}
/**
* Sanitize the stream filter name
*
* @param string $filter_name the stream filter name
*
* @return string
*/
protected function sanitizeStreamFilter($filter_name)
{
$this->assertStreamable();
return (string) $filter_name;
}
/**
* Detect if the stream filter is already present
*
* @param string $filter_name
*
* @return bool
*/
public function hasStreamFilter($filter_name)
{
$this->assertStreamable();
return false !== array_search($filter_name, $this->stream_filters, true);
}
/**
* Remove a filter from the collection
*
* @param string $filter_name
*
* @return $this
*/
public function removeStreamFilter($filter_name)
{
$this->assertStreamable();
$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 $this
*/
public function clearStreamFilter()
{
$this->assertStreamable();
$this->stream_filters = [];
return $this;
}
/**
* Return the filter path
*
* @return string
*/
protected function getStreamFilterPath()
{
$this->assertStreamable();
if (! $this->stream_filters) {
return $this->stream_uri;
}
return 'php://filter/'
.$this->getStreamFilterPrefix()
.implode('|', $this->stream_filters)
.'/resource='.$this->stream_uri;
}
/**
* Return PHP stream filter prefix
*
* @return string
*/
protected function getStreamFilterPrefix()
{
if (STREAM_FILTER_READ == $this->stream_filter_mode) {
return 'read=';
}
if (STREAM_FILTER_WRITE == $this->stream_filter_mode) {
return 'write=';
}
return '';
}
}
src/Plugin/ColumnConsistencyValidator.php 0000666 00000004400 13052363017 0014631 0 ustar 00 ['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;
}
/**
* 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.
*
*/
public function autodetectColumnsCount()
{
$this->detect_columns_count = true;
}
/**
* Is the submitted row valid
*
* @param array $row
*
* @return bool
*/
public function __invoke(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;
}
}
src/Plugin/ForbiddenNullValuesValidator.php 0000666 00000001441 13052363017 0015063 0 ustar 00 fetch($callable);
}
/**
* Return a Filtered Iterator
*
* @param callable $callable a callable function to be applied to each Iterator item
*
* @return Iterator
*/
public function fetch(callable $callable = null)
{
$this->addFilter(function ($row) {
return is_array($row);
});
$iterator = $this->getIterator();
$iterator = $this->applyBomStripping($iterator);
$iterator = $this->applyIteratorFilter($iterator);
$iterator = $this->applyIteratorSortBy($iterator);
$iterator = $this->applyIteratorInterval($iterator);
if (!is_null($callable)) {
return new MapIterator($iterator, $callable);
}
return $iterator;
}
/**
* Applies a callback function on the CSV
*
* The callback function must return TRUE in order to continue
* iterating over the iterator.
*
* @param callable $callable The callback function
*
* @return int the iteration count
*/
public function each(callable $callable)
{
$index = 0;
$iterator = $this->fetch();
$iterator->rewind();
while ($iterator->valid() && true === call_user_func(
$callable,
$iterator->current(),
$iterator->key(),
$iterator
)) {
++$index;
$iterator->next();
}
return $index;
}
/**
* Returns a single row from the CSV
*
* @param int $offset
*
* @throws InvalidArgumentException If the $offset is not a valid Integer
*
* @return array
*/
public function fetchOne($offset = 0)
{
$this->setOffset($offset);
$this->setLimit(1);
$iterator = $this->fetch();
$iterator->rewind();
return (array) $iterator->current();
}
/**
* Returns a sequential array of all CSV lines
*
* The callable function will be applied to each Iterator item
*
* @param callable $callable a callable function
*
* @return array
*/
public function fetchAll(callable $callable = null)
{
return iterator_to_array($this->fetch($callable), false);
}
/**
* Returns a single column from the CSV data
*
* The callable function will be applied to each value to be return
*
* @param int $column_index field Index
* @param callable $callable a callable function
*
* @throws InvalidArgumentException If the column index is not a positive integer or 0
*
* @return array
*/
public function fetchColumn($column_index = 0, callable $callable = null)
{
if (false === filter_var($column_index, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]])) {
throw new InvalidArgumentException(
'the column index must be a positive integer or 0'
);
}
$filterColumn = function ($row) use ($column_index) {
return array_key_exists($column_index, $row);
};
$selectColumn = function ($row) use ($column_index) {
return $row[$column_index];
};
$iterator = $this->fetch($callable);
$iterator = new CallbackFilterIterator($iterator, $filterColumn);
return iterator_to_array(new MapIterator($iterator, $selectColumn), false);
}
/**
* Returns a sequential array of all CSV lines;
*
* The rows are presented as associated arrays
* The callable function will be applied to each Iterator item
*
* @param int|array $offset_or_keys the name for each key member OR the row Index to be
* used as the associated named keys
*
* @param callable $callable a callable function
*
* @throws InvalidArgumentException If the submitted keys are invalid
*
* @return Iterator
*/
public function fetchAssoc($offset_or_keys = 0, callable $callable = null)
{
$keys = $this->getAssocKeys($offset_or_keys);
$keys_count = count($keys);
$combineArray = function (array $row) use ($keys, $keys_count) {
if ($keys_count != count($row)) {
$row = array_slice(array_pad($row, $keys_count, null), 0, $keys_count);
}
return array_combine($keys, $row);
};
return iterator_to_array(new MapIterator($this->fetch($callable), $combineArray), false);
}
/**
* Selects the array to be used as key for the fetchAssoc method
*
* @param int|array $offset_or_keys the assoc key OR the row Index to be used
* as the key index
*
* @throws InvalidArgumentException If the row index and/or the resulting array is invalid
*
* @return array
*/
protected function getAssocKeys($offset_or_keys)
{
if (is_array($offset_or_keys)) {
return $this->validateAssocKeys($offset_or_keys);
}
if (false === filter_var($offset_or_keys, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]])) {
throw new InvalidArgumentException('the row index must be a positive integer, 0 or a non empty array');
}
$keys = $this->getRow($offset_or_keys);
$keys = $this->validateAssocKeys($keys);
$filterOutRow = function ($row, $rowIndex) use ($offset_or_keys) {
return is_array($row) && $rowIndex != $offset_or_keys;
};
$this->addFilter($filterOutRow);
return $keys;
}
/**
* Validates the array to be used by the fetchAssoc method
*
* @param array $keys
*
* @throws InvalidArgumentException If the submitted array fails the assertion
*/
protected function validateAssocKeys(array $keys)
{
if (empty($keys)) {
throw new InvalidArgumentException('The array can not be empty');
}
foreach ($keys as &$str) {
$str = $this->validateString($str);
}
unset($str);
if ($keys == array_unique($keys)) {
return $keys;
}
throw new InvalidArgumentException('The array must contain unique values');
}
/**
* Returns a single row from the CSV without filtering
*
* @param int $offset
*
* @throws InvalidArgumentException If the $offset is not valid or the row does not exist
*
* @return array
*/
protected function getRow($offset)
{
$csv = $this->getIterator();
$csv->setFlags($this->getFlags() & ~SplFileObject::READ_CSV);
$iterator = new LimitIterator($csv, $offset, 1);
$iterator->rewind();
$res = $iterator->current();
if (empty($res)) {
throw new InvalidArgumentException('the specified row does not exist or is empty');
}
if (0 == $offset && $this->isBomStrippable()) {
$res = mb_substr($res, mb_strlen($this->getInputBom()));
}
return str_getcsv($res, $this->delimiter, $this->enclosure, $this->escape);
}
}
src/Writer.php 0000666 00000007347 13052363017 0007337 0 ustar 00 getNumberOfParameters();
}
}
/**
* Adds multiple lines to the CSV document
*
* a simple wrapper method around insertOne
*
* @param Traversable|array $rows a multidimensional array or a Traversable object
*
* @throws InvalidArgumentException If the given rows format is invalid
*
* @return static
*/
public function insertAll($rows)
{
if (!is_array($rows) && !$rows instanceof Traversable) {
throw new InvalidArgumentException(
'the provided data must be an array OR a \Traversable object'
);
}
foreach ($rows as $row) {
$this->insertOne($row);
}
return $this;
}
/**
* Adds a single line to a CSV document
*
* @param string[]|string $row a string, an array or an object implementing to '__toString' method
*
* @return static
*/
public function insertOne($row)
{
if (!is_array($row)) {
$row = str_getcsv($row, $this->delimiter, $this->enclosure, $this->escape);
}
$row = $this->formatRow($row);
$this->validateRow($row);
if (is_null($this->csv)) {
$this->csv = $this->getIterator();
}
static::$fputcsv->invokeArgs($this->csv, $this->getFputcsvParameters($row));
if ("\n" !== $this->newline) {
$this->csv->fseek(-1, SEEK_CUR);
$this->csv->fwrite($this->newline);
}
return $this;
}
/**
* returns the parameters for SplFileObject::fputcsv
*
* @param array $fields The fields to be add
*
* @return array
*/
protected function getFputcsvParameters(array $fields)
{
$parameters = [$fields, $this->delimiter, $this->enclosure];
if (4 == static::$fputcsv_param_count) {
$parameters[] = $this->escape;
}
return $parameters;
}
/**
* {@inheritdoc}
*/
public function isActiveStreamFilter()
{
return parent::isActiveStreamFilter() && is_null($this->csv);
}
/**
* {@inheritdoc}
*/
public function __destruct()
{
$this->csv = null;
parent::__destruct();
}
}