Initial commit

This commit is contained in:
gustavooth
2026-07-23 22:56:30 -03:00
commit 22a1006598
1816 changed files with 410742 additions and 0 deletions
@@ -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;
}
}
@@ -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 '';
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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();
}
}