Initial commit
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
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.
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
spl_autoload_register(function ($class) {
|
||||
|
||||
$prefix = 'League\Csv\\';
|
||||
if (0 !== strpos($class, $prefix)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file = __DIR__
|
||||
.DIRECTORY_SEPARATOR
|
||||
.'src'
|
||||
.DIRECTORY_SEPARATOR
|
||||
.str_replace('\\', DIRECTORY_SEPARATOR, substr($class, strlen($prefix)))
|
||||
.'.php';
|
||||
if (!is_readable($file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
require $file;
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
use IteratorAggregate;
|
||||
use JsonSerializable;
|
||||
use League\Csv\Config\Controls;
|
||||
use League\Csv\Config\Output;
|
||||
use League\Csv\Modifier\QueryFilter;
|
||||
use League\Csv\Modifier\StreamFilter;
|
||||
use SplFileInfo;
|
||||
use SplFileObject;
|
||||
use SplTempFileObject;
|
||||
|
||||
/**
|
||||
* An abstract class to enable basic CSV manipulation
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 4.0.0
|
||||
*
|
||||
*/
|
||||
abstract class AbstractCsv implements JsonSerializable, IteratorAggregate
|
||||
{
|
||||
use Controls;
|
||||
|
||||
use Output;
|
||||
|
||||
use QueryFilter;
|
||||
|
||||
use StreamFilter;
|
||||
|
||||
/**
|
||||
* UTF-8 BOM sequence
|
||||
*/
|
||||
const BOM_UTF8 = "\xEF\xBB\xBF";
|
||||
|
||||
/**
|
||||
* UTF-16 BE BOM sequence
|
||||
*/
|
||||
const BOM_UTF16_BE = "\xFE\xFF";
|
||||
|
||||
/**
|
||||
* UTF-16 LE BOM sequence
|
||||
*/
|
||||
const BOM_UTF16_LE = "\xFF\xFE";
|
||||
|
||||
/**
|
||||
* UTF-32 BE BOM sequence
|
||||
*/
|
||||
const BOM_UTF32_BE = "\x00\x00\xFE\xFF";
|
||||
|
||||
/**
|
||||
* UTF-32 LE BOM sequence
|
||||
*/
|
||||
const BOM_UTF32_LE = "\x00\x00\xFF\xFE";
|
||||
|
||||
/**
|
||||
* The constructor path
|
||||
*
|
||||
* can be a SplFileInfo object or the string path to a file
|
||||
*
|
||||
* @var SplFileObject|string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* The file open mode flag
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $open_mode;
|
||||
|
||||
/**
|
||||
* Default SplFileObject flags settings
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $defaultFlags;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* 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
|
||||
* @param string $open_mode the file open mode flag
|
||||
*/
|
||||
protected function __construct($path, $open_mode = 'r+')
|
||||
{
|
||||
$this->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
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
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
|
||||
*
|
||||
* <code>
|
||||
*<?php
|
||||
* $csv = new Reader::createFromPath('/path/to/file.csv', 'a+');
|
||||
* $csv = new Reader::createFromPath(new SplFileInfo('/path/to/file.csv'));
|
||||
* $csv = new Reader::createFromPath(new SplFileObject('/path/to/file.csv'), 'rb');
|
||||
*
|
||||
* ?>
|
||||
* </code>
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* <code>
|
||||
*<?php
|
||||
* $csv = new Writer::createFromFileObject(new SplFileInfo('/path/to/file.csv'));
|
||||
* $csv = new Writer::createFromFileObject(new SplTempFileObject);
|
||||
*
|
||||
* ?>
|
||||
* </code>
|
||||
*
|
||||
* @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();
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
|
||||
$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]]))) {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
||||
throw new InvalidArgumentException($errorMessage);
|
||||
}
|
||||
|
||||
return $int;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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\Config;
|
||||
|
||||
use CallbackFilterIterator;
|
||||
use InvalidArgumentException;
|
||||
use LimitIterator;
|
||||
use SplFileObject;
|
||||
|
||||
/**
|
||||
* A trait to configure and check CSV file and content
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 6.0.0
|
||||
*
|
||||
*/
|
||||
trait Controls
|
||||
{
|
||||
/**
|
||||
* the field delimiter (one character only)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $delimiter = ',';
|
||||
|
||||
/**
|
||||
* the field enclosure character (one character only)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $enclosure = '"';
|
||||
|
||||
/**
|
||||
* the field escape character (one character only)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $escape = '\\';
|
||||
|
||||
/**
|
||||
* the \SplFileObject flags holder
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $flags;
|
||||
|
||||
/**
|
||||
* newline character
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $newline = "\n";
|
||||
|
||||
/**
|
||||
* Sets the field delimiter
|
||||
*
|
||||
* @param string $delimiter
|
||||
*
|
||||
* @throws InvalidArgumentException If $delimiter is not a single character
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDelimiter($delimiter)
|
||||
{
|
||||
if (!$this->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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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\Config;
|
||||
|
||||
use DomDocument;
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
use League\Csv\Modifier\MapIterator;
|
||||
use SplFileObject;
|
||||
|
||||
/**
|
||||
* A trait to output CSV
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 6.3.0
|
||||
*
|
||||
*/
|
||||
trait Output
|
||||
{
|
||||
/**
|
||||
* Charset Encoding for the CSV
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $encodingFrom = 'UTF-8';
|
||||
|
||||
/**
|
||||
* The Input file BOM character
|
||||
* @var string
|
||||
*/
|
||||
protected $input_bom;
|
||||
|
||||
/**
|
||||
* The Output file BOM character
|
||||
* @var string
|
||||
*/
|
||||
protected $output_bom;
|
||||
|
||||
/**
|
||||
* Returns the CSV Iterator
|
||||
*
|
||||
* @return Iterator
|
||||
*/
|
||||
abstract protected function getConversionIterator();
|
||||
|
||||
/**
|
||||
* Returns the CSV Iterator
|
||||
*
|
||||
* @return Iterator
|
||||
*/
|
||||
abstract public function getIterator();
|
||||
|
||||
/**
|
||||
* Sets the CSV encoding charset
|
||||
*
|
||||
* @param string $str
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
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->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; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$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
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
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;
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a data is not validated prior to insertion
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 7.0.0
|
||||
*
|
||||
*/
|
||||
class InvalidRowException extends \InvalidArgumentException
|
||||
{
|
||||
/**
|
||||
* Validator which did not validated the data
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* Validator Data which caused the error
|
||||
* @var array
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* New Instance
|
||||
*
|
||||
* @param string $name validator name
|
||||
* @param array $data invalid data
|
||||
* @param string $message exception message
|
||||
*/
|
||||
public function __construct($name, array $data = [], $message = '')
|
||||
{
|
||||
parent::__construct($message);
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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\Modifier;
|
||||
|
||||
use Iterator;
|
||||
use IteratorIterator;
|
||||
|
||||
/**
|
||||
* A simple MapIterator
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 3.3.0
|
||||
* @internal used internally to modify CSV content
|
||||
*
|
||||
*/
|
||||
class MapIterator extends IteratorIterator
|
||||
{
|
||||
/**
|
||||
* The function to be apply on all InnerIterator element
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
private $callable;
|
||||
|
||||
/**
|
||||
* The Constructor
|
||||
*
|
||||
* @param Iterator $iterator
|
||||
* @param callable $callable
|
||||
*/
|
||||
public function __construct(Iterator $iterator, callable $callable)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
$this->callable = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the current element
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
$iterator = $this->getInnerIterator();
|
||||
|
||||
return call_user_func($this->callable, $iterator->current(), $iterator->key(), $iterator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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\Modifier;
|
||||
|
||||
use ArrayObject;
|
||||
use CallbackFilterIterator;
|
||||
use Iterator;
|
||||
use LimitIterator;
|
||||
|
||||
/**
|
||||
* A Trait to Query rows against a SplFileObject
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 4.2.1
|
||||
*
|
||||
*/
|
||||
trait QueryFilter
|
||||
{
|
||||
/**
|
||||
* Callables to filter the iterator
|
||||
*
|
||||
* @var callable[]
|
||||
*/
|
||||
protected $iterator_filters = [];
|
||||
|
||||
/**
|
||||
* Callables to sort the iterator
|
||||
*
|
||||
* @var callable[]
|
||||
*/
|
||||
protected $iterator_sort_by = [];
|
||||
|
||||
/**
|
||||
* iterator Offset
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $iterator_offset = 0;
|
||||
|
||||
/**
|
||||
* iterator maximum length
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $iterator_limit = -1;
|
||||
|
||||
/**
|
||||
* Stripping BOM status
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $strip_bom = false;
|
||||
|
||||
/**
|
||||
* Stripping BOM setter
|
||||
*
|
||||
* @param bool $status
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function stripBom($status)
|
||||
{
|
||||
$this->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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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\Modifier;
|
||||
|
||||
use League\Csv\Exception\InvalidRowException;
|
||||
|
||||
/**
|
||||
* Trait to format and validate the row before insertion
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 7.0.0
|
||||
*
|
||||
*/
|
||||
trait RowFilter
|
||||
{
|
||||
/**
|
||||
* Callables to validate the row before insertion
|
||||
*
|
||||
* @var callable[]
|
||||
*/
|
||||
protected $validators = [];
|
||||
|
||||
/**
|
||||
* Callables to format the row before insertion
|
||||
*
|
||||
* @var callable[]
|
||||
*/
|
||||
protected $formatters = [];
|
||||
|
||||
/**
|
||||
* add a formatter to the collection
|
||||
*
|
||||
* @param callable $callable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addFormatter(callable $callable)
|
||||
{
|
||||
$this->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)) {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
||||
throw new InvalidRowException($name, $row, 'row validation failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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\Modifier;
|
||||
|
||||
use LogicException;
|
||||
use OutOfBoundsException;
|
||||
|
||||
/**
|
||||
* A Trait to ease PHP Stream Filters manipulation
|
||||
* with a SplFileObject
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 6.0.0
|
||||
*
|
||||
*/
|
||||
trait StreamFilter
|
||||
{
|
||||
/**
|
||||
* collection of stream filters
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $stream_filters = [];
|
||||
|
||||
/**
|
||||
* Stream filtering mode to apply on all filters
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $stream_filter_mode = STREAM_FILTER_ALL;
|
||||
|
||||
/**
|
||||
*the real path
|
||||
*
|
||||
* @var string the real path to the file
|
||||
*
|
||||
*/
|
||||
protected $stream_uri;
|
||||
|
||||
/**
|
||||
* PHP Stream Filter Regex
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $stream_regex = ',^
|
||||
php://filter/
|
||||
(?P<mode>:?read=|write=)? # The resource open mode
|
||||
(?P<filters>.*?) # The resource registered filters
|
||||
/resource=(?P<resource>.*) # 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 '';
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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\Plugin;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* A class to manage column consistency on data insertion into a CSV
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 7.0.0
|
||||
*
|
||||
*/
|
||||
class ColumnConsistencyValidator
|
||||
{
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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\Plugin;
|
||||
|
||||
/**
|
||||
* A class to validate null value handling on data insertion into a CSV
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 7.0.0
|
||||
*
|
||||
*/
|
||||
class ForbiddenNullValuesValidator
|
||||
{
|
||||
/**
|
||||
* Is the submitted row valid
|
||||
*
|
||||
* @param array $row
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function __invoke(array $row)
|
||||
{
|
||||
$res = array_filter($row, function ($value) {
|
||||
return is_null($value);
|
||||
});
|
||||
|
||||
return !$res;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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\Plugin;
|
||||
|
||||
/**
|
||||
* A class to remove null value from data before insertion into a CSV
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 7.0.0
|
||||
*
|
||||
*/
|
||||
class SkipNullValuesFormatter
|
||||
{
|
||||
/**
|
||||
* remove null value form the submitted array
|
||||
*
|
||||
* @param array $row
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __invoke(array $row)
|
||||
{
|
||||
return array_filter($row, function ($value) {
|
||||
return !is_null($value);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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;
|
||||
|
||||
use CallbackFilterIterator;
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
use League\Csv\Modifier\MapIterator;
|
||||
use LimitIterator;
|
||||
use SplFileObject;
|
||||
|
||||
/**
|
||||
* A class to manage extracting and filtering a CSV
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 3.0.0
|
||||
*
|
||||
*/
|
||||
class Reader extends AbstractCsv
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected $stream_filter_mode = STREAM_FILTER_READ;
|
||||
|
||||
/**
|
||||
* Returns a Filtered Iterator
|
||||
*
|
||||
* DEPRECATION WARNING! This method will be removed in the next major point release
|
||||
*
|
||||
* @deprecated deprecated since version 7.2
|
||||
*
|
||||
* @return Iterator
|
||||
*/
|
||||
public function query(callable $callable = null)
|
||||
{
|
||||
return $this->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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the League.csv library
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @link https://github.com/thephpleague/csv/
|
||||
* @version 7.2.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;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use League\Csv\Modifier\RowFilter;
|
||||
use ReflectionMethod;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* A class to manage data insertion into a CSV
|
||||
*
|
||||
* @package League.csv
|
||||
* @since 4.0.0
|
||||
*
|
||||
*/
|
||||
class Writer extends AbstractCsv
|
||||
{
|
||||
use RowFilter;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected $stream_filter_mode = STREAM_FILTER_WRITE;
|
||||
|
||||
/**
|
||||
* The CSV object holder
|
||||
*
|
||||
* @var \SplFileObject
|
||||
*/
|
||||
protected $csv;
|
||||
|
||||
/**
|
||||
* fputcsv method from SplFileObject
|
||||
*
|
||||
* @var ReflectionMethod
|
||||
*/
|
||||
protected static $fputcsv;
|
||||
|
||||
/**
|
||||
* Nb parameters for SplFileObject::fputcsv method
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected static $fputcsv_param_count;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function __construct($path, $open_mode = 'r+')
|
||||
{
|
||||
parent::__construct($path, $open_mode);
|
||||
static::initFputcsv();
|
||||
}
|
||||
|
||||
/**
|
||||
* initiate a SplFileObject::fputcsv method
|
||||
*/
|
||||
protected static function initFputcsv()
|
||||
{
|
||||
if (is_null(static::$fputcsv)) {
|
||||
static::$fputcsv = new ReflectionMethod('\SplFileObject', 'fputcsv');
|
||||
static::$fputcsv_param_count = static::$fputcsv->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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user