first
This commit is contained in:
492
system/vendor/league/csv/src/AbstractCsv.php
vendored
Executable file
492
system/vendor/league/csv/src/AbstractCsv.php
vendored
Executable file
@@ -0,0 +1,492 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use Generator;
|
||||
use SplFileObject;
|
||||
use function filter_var;
|
||||
use function get_class;
|
||||
use function mb_strlen;
|
||||
use function rawurlencode;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use function str_split;
|
||||
use function strcspn;
|
||||
use function strlen;
|
||||
use const FILTER_FLAG_STRIP_HIGH;
|
||||
use const FILTER_FLAG_STRIP_LOW;
|
||||
use const FILTER_UNSAFE_RAW;
|
||||
|
||||
/**
|
||||
* An abstract class to enable CSV document loading.
|
||||
*/
|
||||
abstract class AbstractCsv implements ByteSequence
|
||||
{
|
||||
protected const STREAM_FILTER_MODE = STREAM_FILTER_READ;
|
||||
|
||||
/** @var SplFileObject|Stream The CSV document. */
|
||||
protected $document;
|
||||
/** @var array<string, bool> collection of stream filters. */
|
||||
protected array $stream_filters = [];
|
||||
protected ?string $input_bom = null;
|
||||
protected string $output_bom = '';
|
||||
protected string $delimiter = ',';
|
||||
protected string $enclosure = '"';
|
||||
protected string $escape = '\\';
|
||||
protected bool $is_input_bom_included = false;
|
||||
|
||||
/**
|
||||
* @final This method should not be overwritten in child classes
|
||||
*
|
||||
* @param SplFileObject|Stream $document The CSV Object instance
|
||||
*/
|
||||
protected function __construct($document)
|
||||
{
|
||||
$this->document = $document;
|
||||
[$this->delimiter, $this->enclosure, $this->escape] = $this->document->getCsvControl();
|
||||
$this->resetProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset dynamic object properties to improve performance.
|
||||
*/
|
||||
abstract protected function resetProperties(): void;
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
unset($this->document);
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
throw UnavailableStream::dueToForbiddenCloning(static::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance from a SplFileObject.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromFileObject(SplFileObject $file)
|
||||
{
|
||||
return new static($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance from a PHP resource stream.
|
||||
*
|
||||
* @param resource $stream
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromStream($stream)
|
||||
{
|
||||
return new static(new Stream($stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance from a string.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromString(string $content = '')
|
||||
{
|
||||
return new static(Stream::createFromString($content));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance from a file path.
|
||||
*
|
||||
* @param resource|null $context the resource context
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromPath(string $path, string $open_mode = 'r+', $context = null)
|
||||
{
|
||||
return new static(Stream::createFromPath($path, $open_mode, $context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current field delimiter.
|
||||
*/
|
||||
public function getDelimiter(): string
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current field enclosure.
|
||||
*/
|
||||
public function getEnclosure(): string
|
||||
{
|
||||
return $this->enclosure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pathname of the underlying document.
|
||||
*/
|
||||
public function getPathname(): string
|
||||
{
|
||||
return $this->document->getPathname();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current field escape character.
|
||||
*/
|
||||
public function getEscape(): string
|
||||
{
|
||||
return $this->escape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BOM sequence in use on Output methods.
|
||||
*/
|
||||
public function getOutputBOM(): string
|
||||
{
|
||||
return $this->output_bom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BOM sequence of the given CSV.
|
||||
*/
|
||||
public function getInputBOM(): string
|
||||
{
|
||||
if (null !== $this->input_bom) {
|
||||
return $this->input_bom;
|
||||
}
|
||||
|
||||
$this->document->setFlags(SplFileObject::READ_CSV);
|
||||
$this->document->rewind();
|
||||
$this->input_bom = Info::fetchBOMSequence((string) $this->document->fread(4)) ?? '';
|
||||
|
||||
return $this->input_bom;
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATION WARNING! This method will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.7.0
|
||||
* @see AbstractCsv::supportsStreamFilterOnRead
|
||||
* @see AbstractCsv::supportsStreamFilterOnWrite
|
||||
*
|
||||
* Returns the stream filter mode.
|
||||
*/
|
||||
public function getStreamFilterMode(): int
|
||||
{
|
||||
return static::STREAM_FILTER_MODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATION WARNING! This method will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.7.0
|
||||
* @see AbstractCsv::supportsStreamFilterOnRead
|
||||
* @see AbstractCsv::supportsStreamFilterOnWrite
|
||||
*
|
||||
* Tells whether the stream filter capabilities can be used.
|
||||
*/
|
||||
public function supportsStreamFilter(): bool
|
||||
{
|
||||
return $this->document instanceof Stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the stream filter read capabilities can be used.
|
||||
*/
|
||||
public function supportsStreamFilterOnRead(): bool
|
||||
{
|
||||
return $this->document instanceof Stream
|
||||
&& (static::STREAM_FILTER_MODE & STREAM_FILTER_READ) === STREAM_FILTER_READ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the stream filter write capabilities can be used.
|
||||
*/
|
||||
public function supportsStreamFilterOnWrite(): bool
|
||||
{
|
||||
return $this->document instanceof Stream
|
||||
&& (static::STREAM_FILTER_MODE & STREAM_FILTER_WRITE) === STREAM_FILTER_WRITE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether the specify stream filter is attach to the current stream.
|
||||
*/
|
||||
public function hasStreamFilter(string $filtername): bool
|
||||
{
|
||||
return $this->stream_filters[$filtername] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the BOM can be stripped if presents.
|
||||
*/
|
||||
public function isInputBOMIncluded(): bool
|
||||
{
|
||||
return $this->is_input_bom_included;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CSV document as a Generator of string chunk.
|
||||
*
|
||||
* @param int $length number of bytes read
|
||||
*
|
||||
* @throws Exception if the number of bytes is lesser than 1
|
||||
*/
|
||||
public function chunk(int $length): Generator
|
||||
{
|
||||
if ($length < 1) {
|
||||
throw InvalidArgument::dueToInvalidChunkSize($length, __METHOD__);
|
||||
}
|
||||
|
||||
$input_bom = $this->getInputBOM();
|
||||
$this->document->rewind();
|
||||
$this->document->setFlags(0);
|
||||
$this->document->fseek(strlen($input_bom));
|
||||
/** @var array<int, string> $chunks */
|
||||
$chunks = str_split($this->output_bom.$this->document->fread($length), $length);
|
||||
foreach ($chunks as $chunk) {
|
||||
yield $chunk;
|
||||
}
|
||||
|
||||
while ($this->document->valid()) {
|
||||
yield $this->document->fread($length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATION WARNING! This method will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.1.0
|
||||
* @see AbstractCsv::toString
|
||||
*
|
||||
* Retrieves the CSV content
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the CSV content.
|
||||
*
|
||||
* DEPRECATION WARNING! This method will be removed in the next major point release
|
||||
*
|
||||
* @deprecated since version 9.7.0
|
||||
* @see AbstractCsv::toString
|
||||
*/
|
||||
public function getContent(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the CSV content.
|
||||
*
|
||||
* @throws Exception If the string representation can not be returned
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
$raw = '';
|
||||
foreach ($this->chunk(8192) as $chunk) {
|
||||
$raw .= $chunk;
|
||||
}
|
||||
|
||||
return $raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs all data on the CSV file.
|
||||
*
|
||||
* @return int Returns the number of characters read from the handle
|
||||
* and passed through to the output.
|
||||
*/
|
||||
public function output(string $filename = null): int
|
||||
{
|
||||
if (null !== $filename) {
|
||||
$this->sendHeaders($filename);
|
||||
}
|
||||
|
||||
$this->document->rewind();
|
||||
if (!$this->is_input_bom_included) {
|
||||
$this->document->fseek(strlen($this->getInputBOM()));
|
||||
}
|
||||
|
||||
echo $this->output_bom;
|
||||
|
||||
return strlen($this->output_bom) + (int) $this->document->fpassthru();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the CSV headers.
|
||||
*
|
||||
* Adapted from Symfony\Component\HttpFoundation\ResponseHeaderBag::makeDisposition
|
||||
*
|
||||
* @throws Exception if the submitted header is invalid according to RFC 6266
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc6266#section-4.3
|
||||
*/
|
||||
protected function sendHeaders(string $filename): void
|
||||
{
|
||||
if (strlen($filename) != strcspn($filename, '\\/')) {
|
||||
throw InvalidArgument::dueToInvalidHeaderFilename($filename);
|
||||
}
|
||||
|
||||
$flag = FILTER_FLAG_STRIP_LOW;
|
||||
if (strlen($filename) !== mb_strlen($filename)) {
|
||||
$flag |= FILTER_FLAG_STRIP_HIGH;
|
||||
}
|
||||
|
||||
/** @var string $filtered_name */
|
||||
$filtered_name = filter_var($filename, FILTER_UNSAFE_RAW, $flag);
|
||||
$filename_fallback = str_replace('%', '', $filtered_name);
|
||||
|
||||
$disposition = sprintf('attachment; filename="%s"', str_replace('"', '\\"', $filename_fallback));
|
||||
if ($filename !== $filename_fallback) {
|
||||
$disposition .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));
|
||||
}
|
||||
|
||||
header('Content-Type: text/csv');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Disposition: '.$disposition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field delimiter.
|
||||
*
|
||||
* @throws InvalidArgument If the Csv control character is not one character only.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setDelimiter(string $delimiter): self
|
||||
{
|
||||
if ($delimiter === $this->delimiter) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (1 !== strlen($delimiter)) {
|
||||
throw InvalidArgument::dueToInvalidDelimiterCharacter($delimiter, __METHOD__);
|
||||
}
|
||||
|
||||
$this->delimiter = $delimiter;
|
||||
$this->resetProperties();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field enclosure.
|
||||
*
|
||||
* @throws InvalidArgument If the Csv control character is not one character only.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setEnclosure(string $enclosure): self
|
||||
{
|
||||
if ($enclosure === $this->enclosure) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (1 !== strlen($enclosure)) {
|
||||
throw InvalidArgument::dueToInvalidEnclosureCharacter($enclosure, __METHOD__);
|
||||
}
|
||||
|
||||
$this->enclosure = $enclosure;
|
||||
$this->resetProperties();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field escape character.
|
||||
*
|
||||
* @throws InvalidArgument If the Csv control character is not one character only.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setEscape(string $escape): self
|
||||
{
|
||||
if ($escape === $this->escape) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ('' !== $escape && 1 !== strlen($escape)) {
|
||||
throw InvalidArgument::dueToInvalidEscapeCharacter($escape, __METHOD__);
|
||||
}
|
||||
|
||||
$this->escape = $escape;
|
||||
$this->resetProperties();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables BOM Stripping.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function skipInputBOM(): self
|
||||
{
|
||||
$this->is_input_bom_included = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables skipping Input BOM.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function includeInputBOM(): self
|
||||
{
|
||||
$this->is_input_bom_included = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the BOM sequence to prepend the CSV on output.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setOutputBOM(string $str): self
|
||||
{
|
||||
$this->output_bom = $str;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* append a stream filter.
|
||||
*
|
||||
* @param null|array $params
|
||||
*
|
||||
* @throws InvalidArgument If the stream filter API can not be appended
|
||||
* @throws UnavailableFeature If the stream filter API can not be used
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function addStreamFilter(string $filtername, $params = null): self
|
||||
{
|
||||
if (!$this->document instanceof Stream) {
|
||||
throw UnavailableFeature::dueToUnsupportedStreamFilterApi(get_class($this->document));
|
||||
}
|
||||
|
||||
$this->document->appendFilter($filtername, static::STREAM_FILTER_MODE, $params);
|
||||
$this->stream_filters[$filtername] = true;
|
||||
$this->resetProperties();
|
||||
$this->input_bom = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
24
system/vendor/league/csv/src/ByteSequence.php
vendored
Executable file
24
system/vendor/league/csv/src/ByteSequence.php
vendored
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
/**
|
||||
* Defines constants for common BOM sequences.
|
||||
*/
|
||||
interface ByteSequence
|
||||
{
|
||||
const BOM_UTF8 = "\xEF\xBB\xBF";
|
||||
const BOM_UTF16_BE = "\xFE\xFF";
|
||||
const BOM_UTF16_LE = "\xFF\xFE";
|
||||
const BOM_UTF32_BE = "\x00\x00\xFE\xFF";
|
||||
const BOM_UTF32_LE = "\xFF\xFE\x00\x00";
|
||||
}
|
||||
66
system/vendor/league/csv/src/CannotInsertRecord.php
vendored
Executable file
66
system/vendor/league/csv/src/CannotInsertRecord.php
vendored
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
/**
|
||||
* Thrown when a data is not added to the Csv Document.
|
||||
*/
|
||||
class CannotInsertRecord extends Exception
|
||||
{
|
||||
/** The record submitted for insertion. */
|
||||
protected array $record;
|
||||
/** Validator which did not validated the data. */
|
||||
protected string $name = '';
|
||||
|
||||
/**
|
||||
* Create an Exception from a record insertion into a stream.
|
||||
*/
|
||||
public static function triggerOnInsertion(array $record): self
|
||||
{
|
||||
$exception = new self('Unable to write record to the CSV document');
|
||||
$exception->record = $record;
|
||||
|
||||
return $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Exception from a Record Validation.
|
||||
*/
|
||||
public static function triggerOnValidation(string $name, array $record): self
|
||||
{
|
||||
$exception = new self('Record validation failed');
|
||||
$exception->name = $name;
|
||||
$exception->record = $record;
|
||||
|
||||
return $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the validator name.
|
||||
*
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the invalid data submitted.
|
||||
*
|
||||
*/
|
||||
public function getRecord(): array
|
||||
{
|
||||
return $this->record;
|
||||
}
|
||||
}
|
||||
220
system/vendor/league/csv/src/CharsetConverter.php
vendored
Executable file
220
system/vendor/league/csv/src/CharsetConverter.php
vendored
Executable file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use OutOfRangeException;
|
||||
use php_user_filter;
|
||||
use Traversable;
|
||||
use function array_combine;
|
||||
use function array_map;
|
||||
use function in_array;
|
||||
use function is_numeric;
|
||||
use function mb_convert_encoding;
|
||||
use function mb_list_encodings;
|
||||
use function preg_match;
|
||||
use function sprintf;
|
||||
use function stream_bucket_append;
|
||||
use function stream_bucket_make_writeable;
|
||||
use function stream_filter_register;
|
||||
use function stream_get_filters;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Converts resource stream or tabular data content charset.
|
||||
*/
|
||||
class CharsetConverter extends php_user_filter
|
||||
{
|
||||
const FILTERNAME = 'convert.league.csv';
|
||||
|
||||
protected string $input_encoding = 'UTF-8';
|
||||
protected string $output_encoding = 'UTF-8';
|
||||
|
||||
/**
|
||||
* Static method to add the stream filter to a {@link AbstractCsv} object.
|
||||
*/
|
||||
public static function addTo(AbstractCsv $csv, string $input_encoding, string $output_encoding): AbstractCsv
|
||||
{
|
||||
self::register();
|
||||
|
||||
return $csv->addStreamFilter(self::getFiltername($input_encoding, $output_encoding));
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to register the class as a stream filter.
|
||||
*/
|
||||
public static function register(): void
|
||||
{
|
||||
$filter_name = self::FILTERNAME.'.*';
|
||||
if (!in_array($filter_name, stream_get_filters(), true)) {
|
||||
stream_filter_register($filter_name, self::class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to return the stream filter filtername.
|
||||
*/
|
||||
public static function getFiltername(string $input_encoding, string $output_encoding): string
|
||||
{
|
||||
return sprintf(
|
||||
'%s.%s/%s',
|
||||
self::FILTERNAME,
|
||||
self::filterEncoding($input_encoding),
|
||||
self::filterEncoding($output_encoding)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter encoding charset.
|
||||
*
|
||||
* @throws OutOfRangeException if the charset is malformed or unsupported
|
||||
*/
|
||||
protected static function filterEncoding(string $encoding): string
|
||||
{
|
||||
static $encoding_list;
|
||||
if (null === $encoding_list) {
|
||||
$list = mb_list_encodings();
|
||||
$encoding_list = array_combine(array_map('strtolower', $list), $list);
|
||||
}
|
||||
|
||||
$key = strtolower($encoding);
|
||||
if (isset($encoding_list[$key])) {
|
||||
return $encoding_list[$key];
|
||||
}
|
||||
|
||||
throw new OutOfRangeException('The submitted charset '.$encoding.' is not supported by the mbstring extension.');
|
||||
}
|
||||
|
||||
public function onCreate(): bool
|
||||
{
|
||||
$prefix = self::FILTERNAME.'.';
|
||||
if (0 !== strpos($this->filtername, $prefix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$encodings = substr($this->filtername, strlen($prefix));
|
||||
if (1 !== preg_match(',^(?<input>[-\w]+)\/(?<output>[-\w]+)$,', $encodings, $matches)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->input_encoding = self::filterEncoding($matches['input']);
|
||||
$this->output_encoding = self::filterEncoding($matches['output']);
|
||||
} catch (OutOfRangeException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $in
|
||||
* @param resource $out
|
||||
* @param int $consumed
|
||||
* @param bool $closing
|
||||
*/
|
||||
public function filter($in, $out, &$consumed, $closing): int
|
||||
{
|
||||
while (null !== ($bucket = stream_bucket_make_writeable($in))) {
|
||||
$bucket->data = @mb_convert_encoding($bucket->data, $this->output_encoding, $this->input_encoding);
|
||||
$consumed += $bucket->datalen;
|
||||
stream_bucket_append($out, $bucket);
|
||||
}
|
||||
|
||||
return PSFS_PASS_ON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Csv records collection into UTF-8.
|
||||
*/
|
||||
public function convert(iterable $records): iterable
|
||||
{
|
||||
if ($this->output_encoding === $this->input_encoding) {
|
||||
return $records;
|
||||
}
|
||||
|
||||
if (is_array($records)) {
|
||||
return array_map($this, $records);
|
||||
}
|
||||
|
||||
/* @var Traversable $records */
|
||||
return new MapIterator($records, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable using the class as a formatter for the {@link Writer}.
|
||||
*/
|
||||
public function __invoke(array $record): array
|
||||
{
|
||||
$outputRecord = [];
|
||||
foreach ($record as $offset => $value) {
|
||||
[$newOffset, $newValue] = $this->encodeField($value, $offset);
|
||||
$outputRecord[$newOffset] = $newValue;
|
||||
}
|
||||
|
||||
return $outputRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walker method to convert the offset and the value of a CSV record field.
|
||||
*
|
||||
* @param int|float|string|null $value can be a scalar type or null
|
||||
* @param int|string $offset can be a string or an int
|
||||
*/
|
||||
protected function encodeField($value, $offset): array
|
||||
{
|
||||
if (null !== $value && !is_numeric($value)) {
|
||||
$value = mb_convert_encoding($value, $this->output_encoding, $this->input_encoding);
|
||||
}
|
||||
|
||||
if (!is_numeric($offset)) {
|
||||
$offset = mb_convert_encoding($offset, $this->output_encoding, $this->input_encoding);
|
||||
}
|
||||
|
||||
return [$offset, $value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the records input encoding charset.
|
||||
*/
|
||||
public function inputEncoding(string $encoding): self
|
||||
{
|
||||
$encoding = self::filterEncoding($encoding);
|
||||
if ($encoding === $this->input_encoding) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$clone = clone $this;
|
||||
$clone->input_encoding = $encoding;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the records output encoding charset.
|
||||
*/
|
||||
public function outputEncoding(string $encoding): self
|
||||
{
|
||||
$encoding = self::filterEncoding($encoding);
|
||||
if ($encoding === $this->output_encoding) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$clone = clone $this;
|
||||
$clone->output_encoding = $encoding;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
}
|
||||
59
system/vendor/league/csv/src/ColumnConsistency.php
vendored
Executable file
59
system/vendor/league/csv/src/ColumnConsistency.php
vendored
Executable file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Validates column consistency when inserting records into a CSV document.
|
||||
*/
|
||||
class ColumnConsistency
|
||||
{
|
||||
protected int $columns_count;
|
||||
|
||||
/**
|
||||
* @throws InvalidArgument if the column count is lesser than -1
|
||||
*/
|
||||
public function __construct(int $columns_count = -1)
|
||||
{
|
||||
if ($columns_count < -1) {
|
||||
throw InvalidArgument::dueToInvalidColumnCount($columns_count, __METHOD__);
|
||||
}
|
||||
|
||||
$this->columns_count = $columns_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column count.
|
||||
*/
|
||||
public function getColumnCount(): int
|
||||
{
|
||||
return $this->columns_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether the submitted record is valid.
|
||||
*/
|
||||
public function __invoke(array $record): bool
|
||||
{
|
||||
$count = count($record);
|
||||
if (-1 === $this->columns_count) {
|
||||
$this->columns_count = $count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return $count === $this->columns_count;
|
||||
}
|
||||
}
|
||||
112
system/vendor/league/csv/src/EncloseField.php
vendored
Executable file
112
system/vendor/league/csv/src/EncloseField.php
vendored
Executable file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use php_user_filter;
|
||||
use function array_map;
|
||||
use function in_array;
|
||||
use function str_replace;
|
||||
use function strcspn;
|
||||
use function stream_bucket_append;
|
||||
use function stream_bucket_make_writeable;
|
||||
use function stream_filter_register;
|
||||
use function stream_get_filters;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* A stream filter to improve enclosure character usage.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4180#section-2
|
||||
* @see https://bugs.php.net/bug.php?id=38301
|
||||
*/
|
||||
class EncloseField extends php_user_filter
|
||||
{
|
||||
const FILTERNAME = 'convert.league.csv.enclosure';
|
||||
|
||||
/** Default sequence. */
|
||||
protected string $sequence;
|
||||
/** Characters that triggers enclosure in PHP. */
|
||||
protected static string $force_enclosure = "\n\r\t ";
|
||||
|
||||
/**
|
||||
* Static method to return the stream filter filtername.
|
||||
*/
|
||||
public static function getFiltername(): string
|
||||
{
|
||||
return self::FILTERNAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to register the class as a stream filter.
|
||||
*/
|
||||
public static function register(): void
|
||||
{
|
||||
if (!in_array(self::FILTERNAME, stream_get_filters(), true)) {
|
||||
stream_filter_register(self::FILTERNAME, self::class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to add the stream filter to a {@link Writer} object.
|
||||
*
|
||||
* @throws InvalidArgumentException if the sequence is malformed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function addTo(Writer $csv, string $sequence): Writer
|
||||
{
|
||||
self::register();
|
||||
|
||||
if (!self::isValidSequence($sequence)) {
|
||||
throw new InvalidArgumentException('The sequence must contain at least one character to force enclosure');
|
||||
}
|
||||
|
||||
return $csv
|
||||
->addFormatter(fn (array $record): array => array_map(fn (?string $value): string => $sequence.$value, $record))
|
||||
->addStreamFilter(self::FILTERNAME, ['sequence' => $sequence]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter type and sequence parameters.
|
||||
*
|
||||
* The sequence to force enclosure MUST contains one of the following character ("\n\r\t ")
|
||||
*/
|
||||
protected static function isValidSequence(string $sequence): bool
|
||||
{
|
||||
return strlen($sequence) != strcspn($sequence, self::$force_enclosure);
|
||||
}
|
||||
|
||||
public function onCreate(): bool
|
||||
{
|
||||
return isset($this->params['sequence'])
|
||||
&& self::isValidSequence($this->params['sequence']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $in
|
||||
* @param resource $out
|
||||
* @param int $consumed
|
||||
* @param bool $closing
|
||||
*/
|
||||
public function filter($in, $out, &$consumed, $closing): int
|
||||
{
|
||||
while (null !== ($bucket = stream_bucket_make_writeable($in))) {
|
||||
$bucket->data = str_replace($this->params['sequence'], '', $bucket->data);
|
||||
$consumed += $bucket->datalen;
|
||||
stream_bucket_append($out, $bucket);
|
||||
}
|
||||
|
||||
return PSFS_PASS_ON;
|
||||
}
|
||||
}
|
||||
146
system/vendor/league/csv/src/EscapeFormula.php
vendored
Executable file
146
system/vendor/league/csv/src/EscapeFormula.php
vendored
Executable file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function array_fill_keys;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function array_unique;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function method_exists;
|
||||
|
||||
/**
|
||||
* A Formatter to tackle CSV Formula Injection.
|
||||
*
|
||||
* @see http://georgemauer.net/2017/10/07/csv-injection.html
|
||||
*/
|
||||
class EscapeFormula
|
||||
{
|
||||
/** Spreadsheet formula starting character. */
|
||||
const FORMULA_STARTING_CHARS = ['=', '-', '+', '@', "\t", "\r"];
|
||||
|
||||
/** Effective Spreadsheet formula starting characters. */
|
||||
protected array $special_chars = [];
|
||||
/** Escape character to escape each CSV formula field. */
|
||||
protected string $escape;
|
||||
|
||||
/**
|
||||
* @param string $escape escape character to escape each CSV formula field
|
||||
* @param string[] $special_chars additional spreadsheet formula starting characters
|
||||
*/
|
||||
public function __construct(string $escape = "'", array $special_chars = [])
|
||||
{
|
||||
$this->escape = $escape;
|
||||
if ([] !== $special_chars) {
|
||||
$special_chars = $this->filterSpecialCharacters(...$special_chars);
|
||||
}
|
||||
|
||||
$chars = array_unique(array_merge(self::FORMULA_STARTING_CHARS, $special_chars));
|
||||
$this->special_chars = array_fill_keys($chars, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter submitted special characters.
|
||||
*
|
||||
* @param string ...$characters
|
||||
*
|
||||
* @throws InvalidArgumentException if the string is not a single character
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
protected function filterSpecialCharacters(string ...$characters): array
|
||||
{
|
||||
foreach ($characters as $str) {
|
||||
if (1 != strlen($str)) {
|
||||
throw new InvalidArgumentException('The submitted string '.$str.' must be a single character');
|
||||
}
|
||||
}
|
||||
|
||||
return $characters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of character the instance will escape.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getSpecialCharacters(): array
|
||||
{
|
||||
return array_keys($this->special_chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the escape character.
|
||||
*/
|
||||
public function getEscape(): string
|
||||
{
|
||||
return $this->escape;
|
||||
}
|
||||
|
||||
/**
|
||||
* League CSV formatter hook.
|
||||
*
|
||||
* @see escapeRecord
|
||||
*/
|
||||
public function __invoke(array $record): array
|
||||
{
|
||||
return $this->escapeRecord($record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a CSV record.
|
||||
*/
|
||||
public function escapeRecord(array $record): array
|
||||
{
|
||||
return array_map([$this, 'escapeField'], $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a CSV cell if its content is stringable.
|
||||
*
|
||||
* @param int|float|string|object|resource|array $cell the content of the cell
|
||||
*
|
||||
* @return mixed the escaped content
|
||||
*/
|
||||
protected function escapeField($cell)
|
||||
{
|
||||
if (!is_string($cell) && (!is_object($cell) || !method_exists($cell, '__toString'))) {
|
||||
return $cell;
|
||||
}
|
||||
|
||||
$str_cell = (string) $cell;
|
||||
if (isset($str_cell[0], $this->special_chars[$str_cell[0]])) {
|
||||
return $this->escape.$str_cell;
|
||||
}
|
||||
|
||||
return $cell;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 9.7.2 will be removed in the next major release
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* Tells whether the submitted value is stringable.
|
||||
*
|
||||
* @param mixed $value value to check if it is stringable
|
||||
*/
|
||||
protected function isStringable($value): bool
|
||||
{
|
||||
return is_string($value)
|
||||
|| (is_object($value) && method_exists($value, '__toString'));
|
||||
}
|
||||
}
|
||||
23
system/vendor/league/csv/src/Exception.php
vendored
Executable file
23
system/vendor/league/csv/src/Exception.php
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use Exception as PhpException;
|
||||
|
||||
/**
|
||||
* League Csv Base Exception.
|
||||
*/
|
||||
class Exception extends PhpException implements UnableToProcessCsv
|
||||
{
|
||||
}
|
||||
161
system/vendor/league/csv/src/HTMLConverter.php
vendored
Executable file
161
system/vendor/league/csv/src/HTMLConverter.php
vendored
Executable file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMException;
|
||||
use function preg_match;
|
||||
|
||||
/**
|
||||
* Converts tabular data into an HTML Table string.
|
||||
*/
|
||||
class HTMLConverter
|
||||
{
|
||||
/** table class attribute value. */
|
||||
protected string $class_name = 'table-csv-data';
|
||||
/** table id attribute value. */
|
||||
protected string $id_value = '';
|
||||
protected XMLConverter $xml_converter;
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATION WARNING! This method will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.7.0
|
||||
* @see HTMLConverterTest::create()
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->xml_converter = XMLConverter::create()
|
||||
->rootElement('table')
|
||||
->recordElement('tr')
|
||||
->fieldElement('td')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a tabular data collection into a HTML table string.
|
||||
*
|
||||
* @param string[] $header_record An optional array of headers outputted using the`<thead>` section
|
||||
* @param string[] $footer_record An optional array of footers to output to the table using `<tfoot>` and `<th>` elements
|
||||
*/
|
||||
public function convert(iterable $records, array $header_record = [], array $footer_record = []): string
|
||||
{
|
||||
$doc = new DOMDocument('1.0');
|
||||
if ([] === $header_record && [] === $footer_record) {
|
||||
$table = $this->xml_converter->import($records, $doc);
|
||||
$this->addHTMLAttributes($table);
|
||||
$doc->appendChild($table);
|
||||
|
||||
/** @var string $content */
|
||||
$content = $doc->saveHTML();
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
$table = $doc->createElement('table');
|
||||
|
||||
$this->addHTMLAttributes($table);
|
||||
$this->appendHeaderSection('thead', $header_record, $table);
|
||||
$this->appendHeaderSection('tfoot', $footer_record, $table);
|
||||
|
||||
$table->appendChild($this->xml_converter->rootElement('tbody')->import($records, $doc));
|
||||
|
||||
$doc->appendChild($table);
|
||||
|
||||
/** @var string $content */
|
||||
$content = $doc->saveHTML();
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DOMElement representing a HTML table heading section.
|
||||
*/
|
||||
protected function appendHeaderSection(string $node_name, array $record, DOMElement $table): void
|
||||
{
|
||||
if ([] === $record) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var DOMDocument $ownerDocument */
|
||||
$ownerDocument = $table->ownerDocument;
|
||||
$node = $this->xml_converter
|
||||
->rootElement($node_name)
|
||||
->recordElement('tr')
|
||||
->fieldElement('th')
|
||||
->import([$record], $ownerDocument)
|
||||
;
|
||||
|
||||
/** @var DOMElement $element */
|
||||
foreach ($node->getElementsByTagName('th') as $element) {
|
||||
$element->setAttribute('scope', 'col');
|
||||
}
|
||||
|
||||
$table->appendChild($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds class and id attributes to an HTML tag.
|
||||
*/
|
||||
protected function addHTMLAttributes(DOMElement $node): void
|
||||
{
|
||||
$node->setAttribute('class', $this->class_name);
|
||||
$node->setAttribute('id', $this->id_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML table class name setter.
|
||||
*
|
||||
* @throws DOMException if the id_value contains any type of whitespace
|
||||
*/
|
||||
public function table(string $class_name, string $id_value = ''): self
|
||||
{
|
||||
if (1 === preg_match(",\s,", $id_value)) {
|
||||
throw new DOMException("the id attribute's value must not contain whitespace (spaces, tabs etc.)");
|
||||
}
|
||||
$clone = clone $this;
|
||||
$clone->class_name = $class_name;
|
||||
$clone->id_value = $id_value;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML tr record offset attribute setter.
|
||||
*/
|
||||
public function tr(string $record_offset_attribute_name): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->xml_converter = $this->xml_converter->recordElement('tr', $record_offset_attribute_name);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML td field name attribute setter.
|
||||
*/
|
||||
public function td(string $fieldname_attribute_name): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->xml_converter = $this->xml_converter->fieldElement('td', $fieldname_attribute_name);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
}
|
||||
99
system/vendor/league/csv/src/Info.php
vendored
Executable file
99
system/vendor/league/csv/src/Info.php
vendored
Executable file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use function array_fill_keys;
|
||||
use function array_filter;
|
||||
use function array_reduce;
|
||||
use function array_unique;
|
||||
use function count;
|
||||
use function iterator_to_array;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use const COUNT_RECURSIVE;
|
||||
|
||||
final class Info implements ByteSequence
|
||||
{
|
||||
private const BOM_SEQUENCE_LIST = [
|
||||
self::BOM_UTF32_BE,
|
||||
self::BOM_UTF32_LE,
|
||||
self::BOM_UTF16_BE,
|
||||
self::BOM_UTF16_LE,
|
||||
self::BOM_UTF8,
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the BOM sequence found at the start of the string.
|
||||
*
|
||||
* If no valid BOM sequence is found an empty string is returned
|
||||
*/
|
||||
public static function fetchBOMSequence(string $str): ?string
|
||||
{
|
||||
foreach (self::BOM_SEQUENCE_LIST as $sequence) {
|
||||
if (0 === strpos($str, $sequence)) {
|
||||
return $sequence;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect Delimiters usage in a {@link Reader} object.
|
||||
*
|
||||
* Returns a associative array where each key represents
|
||||
* a submitted delimiter and each value the number CSV fields found
|
||||
* when processing at most $limit CSV records with the given delimiter
|
||||
*
|
||||
* @param string[] $delimiters
|
||||
*
|
||||
* @return array<string, int>
|
||||
*/
|
||||
public static function getDelimiterStats(Reader $csv, array $delimiters, int $limit = 1): array
|
||||
{
|
||||
$delimiterFilter = static fn (string $value): bool => 1 === strlen($value);
|
||||
|
||||
$recordFilter = static fn (array $record): bool => 1 < count($record);
|
||||
|
||||
$stmt = Statement::create()->offset(0)->limit($limit);
|
||||
|
||||
$delimiterStats = static function (array $stats, string $delimiter) use ($csv, $stmt, $recordFilter): array {
|
||||
$csv->setDelimiter($delimiter);
|
||||
$foundRecords = array_filter(
|
||||
iterator_to_array($stmt->process($csv)->getRecords(), false),
|
||||
$recordFilter
|
||||
);
|
||||
|
||||
$stats[$delimiter] = count($foundRecords, COUNT_RECURSIVE);
|
||||
|
||||
return $stats;
|
||||
};
|
||||
|
||||
$currentDelimiter = $csv->getDelimiter();
|
||||
$currentHeaderOffset = $csv->getHeaderOffset();
|
||||
|
||||
$csv->setHeaderOffset(null);
|
||||
|
||||
$stats = array_reduce(
|
||||
array_unique(array_filter($delimiters, $delimiterFilter)),
|
||||
$delimiterStats,
|
||||
array_fill_keys($delimiters, 0)
|
||||
);
|
||||
|
||||
$csv->setHeaderOffset($currentHeaderOffset);
|
||||
$csv->setDelimiter($currentDelimiter);
|
||||
|
||||
return $stats;
|
||||
}
|
||||
}
|
||||
100
system/vendor/league/csv/src/InvalidArgument.php
vendored
Executable file
100
system/vendor/league/csv/src/InvalidArgument.php
vendored
Executable file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* InvalidArgument Exception.
|
||||
*/
|
||||
class InvalidArgument extends Exception
|
||||
{
|
||||
/**
|
||||
* DEPRECATION WARNING! This class will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.7.0
|
||||
*/
|
||||
public function __construct(string $message = '', int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public static function dueToInvalidChunkSize(int $length, string $method): self
|
||||
{
|
||||
return new self($method.'() expects the length to be a positive integer '.$length.' given.');
|
||||
}
|
||||
|
||||
public static function dueToInvalidHeaderFilename(string $filename): self
|
||||
{
|
||||
return new self('The filename `'.$filename.'` cannot contain the "/" and "\\" characters.');
|
||||
}
|
||||
|
||||
public static function dueToInvalidDelimiterCharacter(string $delimiter, string $method): self
|
||||
{
|
||||
return new self($method.'() expects delimiter to be a single character; `'.$delimiter.'` given.');
|
||||
}
|
||||
|
||||
public static function dueToInvalidEnclosureCharacter(string $enclosure, string $method): self
|
||||
{
|
||||
return new self($method.'() expects enclosure to be a single character; `'.$enclosure.'` given.');
|
||||
}
|
||||
|
||||
public static function dueToInvalidEscapeCharacter(string $escape, string $method): self
|
||||
{
|
||||
return new self($method.'() expects escape to be a single character or the empty string; `'.$escape.'` given.');
|
||||
}
|
||||
|
||||
public static function dueToInvalidColumnCount(int $columns_count, string $method): self
|
||||
{
|
||||
return new self($method.'() expects the column count to be greater or equal to -1 '.$columns_count.' given.');
|
||||
}
|
||||
|
||||
public static function dueToInvalidHeaderOffset(int $offset, string $method): self
|
||||
{
|
||||
return new self($method.'() expects header offset to be greater or equal to 0; `'.$offset.'` given.');
|
||||
}
|
||||
|
||||
public static function dueToInvalidRecordOffset(int $offset, string $method): self
|
||||
{
|
||||
return new self($method.'() expects the submitted offset to be a positive integer or 0, '.$offset.' given');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $index
|
||||
*/
|
||||
public static function dueToInvalidColumnIndex($index, string $type, string $method): self
|
||||
{
|
||||
return new self($method.'() expects the '.$type.' index to be a valid string or integer, `'.$index.'` given');
|
||||
}
|
||||
|
||||
public static function dueToInvalidLimit(int $limit, string $method): self
|
||||
{
|
||||
return new self($method.'() expects the limit to be greater or equal to -1, '.$limit.' given.');
|
||||
}
|
||||
|
||||
public static function dueToInvalidSeekingPosition(int $position, string $method): self
|
||||
{
|
||||
return new self($method.'() can\'t seek stream to negative line '.$position);
|
||||
}
|
||||
|
||||
public static function dueToStreamFilterNotFound(string $filtername): self
|
||||
{
|
||||
return new self('unable to locate filter `'.$filtername.'`');
|
||||
}
|
||||
|
||||
public static function dueToInvalidThreshold(int $threshold, string $method): self
|
||||
{
|
||||
return new self($method.'() expects threshold to be null or a valid integer greater or equal to 1');
|
||||
}
|
||||
}
|
||||
44
system/vendor/league/csv/src/MapIterator.php
vendored
Executable file
44
system/vendor/league/csv/src/MapIterator.php
vendored
Executable file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use IteratorIterator;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Map value from an iterator before yielding.
|
||||
*
|
||||
* @internal used internally to modify CSV content
|
||||
*/
|
||||
final class MapIterator extends IteratorIterator
|
||||
{
|
||||
/** @var callable The callback to apply on all InnerIterator current value. */
|
||||
private $callable;
|
||||
|
||||
public function __construct(Traversable $iterator, callable $callable)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
$this->callable = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed The value of the current element.
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return ($this->callable)(parent::current(), parent::key());
|
||||
}
|
||||
}
|
||||
182
system/vendor/league/csv/src/RFC4180Field.php
vendored
Executable file
182
system/vendor/league/csv/src/RFC4180Field.php
vendored
Executable file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use php_user_filter;
|
||||
use function array_map;
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
use function str_replace;
|
||||
use function strcspn;
|
||||
use function stream_bucket_append;
|
||||
use function stream_bucket_make_writeable;
|
||||
use function stream_filter_register;
|
||||
use function stream_get_filters;
|
||||
use function strlen;
|
||||
use const STREAM_FILTER_READ;
|
||||
use const STREAM_FILTER_WRITE;
|
||||
|
||||
/**
|
||||
* A stream filter to conform the CSV field to RFC4180.
|
||||
*
|
||||
* DEPRECATION WARNING! This class will be removed in the next major point release
|
||||
*
|
||||
* @deprecated since version 9.2.0
|
||||
* @see AbstractCsv::setEscape
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4180#section-2
|
||||
*/
|
||||
class RFC4180Field extends php_user_filter
|
||||
{
|
||||
public const FILTERNAME = 'convert.league.csv.rfc4180';
|
||||
|
||||
/**
|
||||
* The value being search for.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $search;
|
||||
|
||||
/**
|
||||
* The replacement value that replace found $search values.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $replace;
|
||||
|
||||
/**
|
||||
* Characters that triggers enclosure with PHP fputcsv.
|
||||
*
|
||||
*/
|
||||
protected static string $force_enclosure = "\n\r\t ";
|
||||
|
||||
/**
|
||||
* Static method to add the stream filter to a {@link AbstractCsv} object.
|
||||
*/
|
||||
public static function addTo(AbstractCsv $csv, string $whitespace_replace = ''): AbstractCsv
|
||||
{
|
||||
self::register();
|
||||
|
||||
$params = [
|
||||
'enclosure' => $csv->getEnclosure(),
|
||||
'escape' => $csv->getEscape(),
|
||||
'mode' => $csv->getStreamFilterMode(),
|
||||
];
|
||||
|
||||
if ($csv instanceof Writer && '' != $whitespace_replace) {
|
||||
self::addFormatterTo($csv, $whitespace_replace);
|
||||
$params['whitespace_replace'] = $whitespace_replace;
|
||||
}
|
||||
|
||||
return $csv->addStreamFilter(self::FILTERNAME, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a formatter to the {@link Writer} object to format the record
|
||||
* field to avoid enclosure around a field with an empty space.
|
||||
*/
|
||||
public static function addFormatterTo(Writer $csv, string $whitespace_replace): Writer
|
||||
{
|
||||
if ('' == $whitespace_replace || strlen($whitespace_replace) != strcspn($whitespace_replace, self::$force_enclosure)) {
|
||||
throw new InvalidArgumentException('The sequence contains a character that enforces enclosure or is a CSV control character or is the empty string.');
|
||||
}
|
||||
|
||||
$mapper = fn ($value) => is_string($value)
|
||||
? str_replace(' ', $whitespace_replace, $value)
|
||||
: $value;
|
||||
|
||||
return $csv->addFormatter(fn (array $record): array => array_map($mapper, $record));
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to register the class as a stream filter.
|
||||
*/
|
||||
public static function register(): void
|
||||
{
|
||||
if (!in_array(self::FILTERNAME, stream_get_filters(), true)) {
|
||||
stream_filter_register(self::FILTERNAME, self::class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to return the stream filter filtername.
|
||||
*/
|
||||
public static function getFiltername(): string
|
||||
{
|
||||
return self::FILTERNAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $in
|
||||
* @param resource $out
|
||||
* @param int $consumed
|
||||
* @param bool $closing
|
||||
*/
|
||||
public function filter($in, $out, &$consumed, $closing): int
|
||||
{
|
||||
while (null !== ($bucket = stream_bucket_make_writeable($in))) {
|
||||
$bucket->data = str_replace($this->search, $this->replace, $bucket->data);
|
||||
$consumed += $bucket->datalen;
|
||||
stream_bucket_append($out, $bucket);
|
||||
}
|
||||
|
||||
return PSFS_PASS_ON;
|
||||
}
|
||||
|
||||
public function onCreate(): bool
|
||||
{
|
||||
if (!$this->isValidParams($this->params)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->search = [$this->params['escape'].$this->params['enclosure']];
|
||||
$this->replace = [$this->params['enclosure'].$this->params['enclosure']];
|
||||
if (STREAM_FILTER_WRITE != $this->params['mode']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->search = [$this->params['escape'].$this->params['enclosure']];
|
||||
$this->replace = [$this->params['escape'].$this->params['enclosure'].$this->params['enclosure']];
|
||||
if ($this->isValidSequence($this->params)) {
|
||||
$this->search[] = $this->params['whitespace_replace'];
|
||||
$this->replace[] = ' ';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate params property.
|
||||
*/
|
||||
protected function isValidParams(array $params): bool
|
||||
{
|
||||
static $mode_list = [STREAM_FILTER_READ => 1, STREAM_FILTER_WRITE => 1];
|
||||
|
||||
return isset($params['enclosure'], $params['escape'], $params['mode'], $mode_list[$params['mode']])
|
||||
&& 1 == strlen($params['enclosure'])
|
||||
&& 1 == strlen($params['escape']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Valid White space replaced sequence.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValidSequence(array $params)
|
||||
{
|
||||
return isset($params['whitespace_replace'])
|
||||
&& strlen($params['whitespace_replace']) == strcspn($params['whitespace_replace'], self::$force_enclosure);
|
||||
}
|
||||
}
|
||||
365
system/vendor/league/csv/src/Reader.php
vendored
Executable file
365
system/vendor/league/csv/src/Reader.php
vendored
Executable file
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use CallbackFilterIterator;
|
||||
use Iterator;
|
||||
use JsonSerializable;
|
||||
use SplFileObject;
|
||||
use function array_combine;
|
||||
use function array_filter;
|
||||
use function array_pad;
|
||||
use function array_slice;
|
||||
use function array_unique;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function iterator_count;
|
||||
use function iterator_to_array;
|
||||
use function mb_strlen;
|
||||
use function mb_substr;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use const STREAM_FILTER_READ;
|
||||
|
||||
/**
|
||||
* A class to parse and read records from a CSV document.
|
||||
*/
|
||||
class Reader extends AbstractCsv implements TabularDataReader, JsonSerializable
|
||||
{
|
||||
protected const STREAM_FILTER_MODE = STREAM_FILTER_READ;
|
||||
|
||||
protected ?int $header_offset = null;
|
||||
protected int $nb_records = -1;
|
||||
protected bool $is_empty_records_included = false;
|
||||
/** @var array<string> header record. */
|
||||
protected array $header = [];
|
||||
|
||||
public static function createFromPath(string $path, string $open_mode = 'r', $context = null)
|
||||
{
|
||||
return parent::createFromPath($path, $open_mode, $context);
|
||||
}
|
||||
|
||||
protected function resetProperties(): void
|
||||
{
|
||||
$this->nb_records = -1;
|
||||
$this->header = [];
|
||||
}
|
||||
|
||||
/** Returns the header offset. */
|
||||
public function getHeaderOffset(): ?int
|
||||
{
|
||||
return $this->header_offset;
|
||||
}
|
||||
|
||||
public function getHeader(): array
|
||||
{
|
||||
if (null === $this->header_offset) {
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
if ([] !== $this->header) {
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
$this->header = $this->setHeader($this->header_offset);
|
||||
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the CSV record header.
|
||||
*
|
||||
* @throws Exception If the header offset is set and no record is found or is the empty array
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
protected function setHeader(int $offset): array
|
||||
{
|
||||
$header = $this->seekRow($offset);
|
||||
if (in_array($header, [[], [null]], true)) {
|
||||
throw SyntaxError::dueToHeaderNotFound($offset);
|
||||
}
|
||||
|
||||
if (0 !== $offset) {
|
||||
return $header;
|
||||
}
|
||||
|
||||
$header = $this->removeBOM($header, mb_strlen($this->getInputBOM()), $this->enclosure);
|
||||
if ([''] === $header) {
|
||||
throw SyntaxError::dueToHeaderNotFound($offset);
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/** Returns the row at a given offset. */
|
||||
protected function seekRow(int $offset): array
|
||||
{
|
||||
foreach ($this->getDocument() as $index => $record) {
|
||||
if ($offset === $index) {
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the document as an Iterator.
|
||||
*/
|
||||
protected function getDocument(): Iterator
|
||||
{
|
||||
$this->document->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD);
|
||||
$this->document->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
|
||||
$this->document->rewind();
|
||||
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the BOM sequence from a record.
|
||||
*
|
||||
* @param string[] $record
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
protected function removeBOM(array $record, int $bom_length, string $enclosure): array
|
||||
{
|
||||
if (0 === $bom_length) {
|
||||
return $record;
|
||||
}
|
||||
|
||||
$record[0] = mb_substr($record[0], $bom_length);
|
||||
if ($enclosure.$enclosure != substr($record[0].$record[0], strlen($record[0]) - 1, 2)) {
|
||||
return $record;
|
||||
}
|
||||
|
||||
$record[0] = substr($record[0], 1, -1);
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
public function fetchColumnByName(string $name): Iterator
|
||||
{
|
||||
return ResultSet::createFromTabularDataReader($this)->fetchColumnByName($name);
|
||||
}
|
||||
|
||||
public function fetchColumnByOffset(int $offset = 0): Iterator
|
||||
{
|
||||
return ResultSet::createFromTabularDataReader($this)->fetchColumnByOffset($offset);
|
||||
}
|
||||
|
||||
public function fetchColumn($index = 0): Iterator
|
||||
{
|
||||
return ResultSet::createFromTabularDataReader($this)->fetchColumn($index);
|
||||
}
|
||||
|
||||
public function fetchOne(int $nth_record = 0): array
|
||||
{
|
||||
return ResultSet::createFromTabularDataReader($this)->fetchOne($nth_record);
|
||||
}
|
||||
|
||||
public function fetchPairs($offset_index = 0, $value_index = 1): Iterator
|
||||
{
|
||||
return ResultSet::createFromTabularDataReader($this)->fetchPairs($offset_index, $value_index);
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
if (-1 === $this->nb_records) {
|
||||
$this->nb_records = iterator_count($this->getRecords());
|
||||
}
|
||||
|
||||
return $this->nb_records;
|
||||
}
|
||||
|
||||
public function getIterator(): Iterator
|
||||
{
|
||||
return $this->getRecords();
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return iterator_to_array($this->getRecords(), false);
|
||||
}
|
||||
|
||||
public function getRecords(array $header = []): Iterator
|
||||
{
|
||||
$header = $this->computeHeader($header);
|
||||
$normalized = fn ($record): bool => is_array($record) && ($this->is_empty_records_included || $record != [null]);
|
||||
|
||||
$bom = '';
|
||||
if (!$this->is_input_bom_included) {
|
||||
$bom = $this->getInputBOM();
|
||||
}
|
||||
|
||||
$document = $this->getDocument();
|
||||
$records = $this->stripBOM(new CallbackFilterIterator($document, $normalized), $bom);
|
||||
if (null !== $this->header_offset) {
|
||||
$records = new CallbackFilterIterator($records, fn (array $record, int $offset): bool => $offset !== $this->header_offset);
|
||||
}
|
||||
|
||||
if ($this->is_empty_records_included) {
|
||||
return $this->combineHeader(new MapIterator(
|
||||
$records,
|
||||
fn (array $record): array => ([null] === $record) ? [] : $record
|
||||
), $header);
|
||||
}
|
||||
|
||||
return $this->combineHeader($records, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header to be used for iteration.
|
||||
*
|
||||
* @param string[] $header
|
||||
*
|
||||
* @throws Exception If the header contains non unique column name
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
protected function computeHeader(array $header)
|
||||
{
|
||||
if ([] === $header) {
|
||||
$header = $this->getHeader();
|
||||
}
|
||||
|
||||
if ($header !== ($filtered_header = array_filter($header, 'is_string'))) {
|
||||
throw SyntaxError::dueToInvalidHeaderColumnNames();
|
||||
}
|
||||
|
||||
if ($header !== array_unique($filtered_header)) {
|
||||
throw SyntaxError::dueToDuplicateHeaderColumnNames($header);
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine the CSV header to each record if present.
|
||||
*
|
||||
* @param string[] $header
|
||||
*/
|
||||
protected function combineHeader(Iterator $iterator, array $header): Iterator
|
||||
{
|
||||
if ([] === $header) {
|
||||
return $iterator;
|
||||
}
|
||||
|
||||
$field_count = count($header);
|
||||
$mapper = static function (array $record) use ($header, $field_count): array {
|
||||
if (count($record) != $field_count) {
|
||||
$record = array_slice(array_pad($record, $field_count, null), 0, $field_count);
|
||||
}
|
||||
|
||||
/** @var array<string|null> $assocRecord */
|
||||
$assocRecord = array_combine($header, $record);
|
||||
|
||||
return $assocRecord;
|
||||
};
|
||||
|
||||
return new MapIterator($iterator, $mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the BOM sequence from the returned records if necessary.
|
||||
*/
|
||||
protected function stripBOM(Iterator $iterator, string $bom): Iterator
|
||||
{
|
||||
if ('' === $bom) {
|
||||
return $iterator;
|
||||
}
|
||||
|
||||
$bom_length = mb_strlen($bom);
|
||||
$mapper = function (array $record, int $index) use ($bom_length): array {
|
||||
if (0 !== $index) {
|
||||
return $record;
|
||||
}
|
||||
|
||||
$record = $this->removeBOM($record, $bom_length, $this->enclosure);
|
||||
if ([''] === $record) {
|
||||
return [null];
|
||||
}
|
||||
|
||||
return $record;
|
||||
};
|
||||
|
||||
return new CallbackFilterIterator(
|
||||
new MapIterator($iterator, $mapper),
|
||||
fn (array $record): bool => $this->is_empty_records_included || $record != [null]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the record to be used as the CSV header.
|
||||
*
|
||||
* Because the header is represented as an array, to be valid
|
||||
* a header MUST contain only unique string value.
|
||||
*
|
||||
* @param int|null $offset the header record offset
|
||||
*
|
||||
* @throws Exception if the offset is a negative integer
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setHeaderOffset(?int $offset): self
|
||||
{
|
||||
if ($offset === $this->header_offset) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (null !== $offset && 0 > $offset) {
|
||||
throw InvalidArgument::dueToInvalidHeaderOffset($offset, __METHOD__);
|
||||
}
|
||||
|
||||
$this->header_offset = $offset;
|
||||
$this->resetProperties();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable skipping empty records.
|
||||
*/
|
||||
public function skipEmptyRecords(): self
|
||||
{
|
||||
if ($this->is_empty_records_included) {
|
||||
$this->is_empty_records_included = false;
|
||||
$this->nb_records = -1;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable skipping empty records.
|
||||
*/
|
||||
public function includeEmptyRecords(): self
|
||||
{
|
||||
if (!$this->is_empty_records_included) {
|
||||
$this->is_empty_records_included = true;
|
||||
$this->nb_records = -1;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether empty records are skipped by the instance.
|
||||
*/
|
||||
public function isEmptyRecordsIncluded(): bool
|
||||
{
|
||||
return $this->is_empty_records_included;
|
||||
}
|
||||
}
|
||||
261
system/vendor/league/csv/src/ResultSet.php
vendored
Executable file
261
system/vendor/league/csv/src/ResultSet.php
vendored
Executable file
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use CallbackFilterIterator;
|
||||
use Generator;
|
||||
use Iterator;
|
||||
use JsonSerializable;
|
||||
use LimitIterator;
|
||||
use function array_flip;
|
||||
use function array_search;
|
||||
use function is_string;
|
||||
use function iterator_count;
|
||||
use function iterator_to_array;
|
||||
|
||||
/**
|
||||
* Represents the result set of a {@link Reader} processed by a {@link Statement}.
|
||||
*/
|
||||
class ResultSet implements TabularDataReader, JsonSerializable
|
||||
{
|
||||
/** The CSV records collection. */
|
||||
protected Iterator $records;
|
||||
/** @var array<string> The CSV records collection header. */
|
||||
protected array $header = [];
|
||||
|
||||
public function __construct(Iterator $records, array $header)
|
||||
{
|
||||
$this->validateHeader($header);
|
||||
|
||||
$this->records = $records;
|
||||
$this->header = $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SyntaxError if the header syntax is invalid
|
||||
*/
|
||||
protected function validateHeader(array $header): void
|
||||
{
|
||||
if ($header !== ($filtered_header = array_filter($header, 'is_string'))) {
|
||||
throw SyntaxError::dueToInvalidHeaderColumnNames();
|
||||
}
|
||||
|
||||
if ($header !== array_unique($filtered_header)) {
|
||||
throw SyntaxError::dueToDuplicateHeaderColumnNames($header);
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
unset($this->records);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance from an object implementing the TabularDataReader interface.
|
||||
*/
|
||||
public static function createFromTabularDataReader(TabularDataReader $reader): self
|
||||
{
|
||||
return new self($reader->getRecords(), $reader->getHeader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header associated with the result set.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getHeader(): array
|
||||
{
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
public function getIterator(): Iterator
|
||||
{
|
||||
return $this->getRecords();
|
||||
}
|
||||
|
||||
public function getRecords(array $header = []): Iterator
|
||||
{
|
||||
$this->validateHeader($header);
|
||||
$records = $this->combineHeader($header);
|
||||
foreach ($records as $offset => $value) {
|
||||
yield $offset => $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine the header to each record if present.
|
||||
*/
|
||||
protected function combineHeader(array $header): Iterator
|
||||
{
|
||||
if ($header === $this->header || [] === $header) {
|
||||
return $this->records;
|
||||
}
|
||||
|
||||
$field_count = count($header);
|
||||
$mapper = static function (array $record) use ($header, $field_count): array {
|
||||
if (count($record) != $field_count) {
|
||||
$record = array_slice(array_pad($record, $field_count, null), 0, $field_count);
|
||||
}
|
||||
|
||||
/** @var array<string|null> $assocRecord */
|
||||
$assocRecord = array_combine($header, $record);
|
||||
|
||||
return $assocRecord;
|
||||
};
|
||||
|
||||
return new MapIterator($this->records, $mapper);
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return iterator_count($this->records);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return iterator_to_array($this->records, false);
|
||||
}
|
||||
|
||||
public function fetchOne(int $nth_record = 0): array
|
||||
{
|
||||
if ($nth_record < 0) {
|
||||
throw InvalidArgument::dueToInvalidRecordOffset($nth_record, __METHOD__);
|
||||
}
|
||||
|
||||
$iterator = new LimitIterator($this->records, $nth_record, 1);
|
||||
$iterator->rewind();
|
||||
|
||||
$result = $iterator->current();
|
||||
if (!is_array($result)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function fetchColumnByName(string $name): Iterator
|
||||
{
|
||||
return $this->yieldColumn(
|
||||
$this->getColumnIndexByValue($name, 'name', __METHOD__)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function fetchColumnByOffset(int $offset): Iterator
|
||||
{
|
||||
return $this->yieldColumn(
|
||||
$this->getColumnIndexByKey($offset, 'offset', __METHOD__)
|
||||
);
|
||||
}
|
||||
|
||||
public function fetchColumn($index = 0): Iterator
|
||||
{
|
||||
return $this->yieldColumn(
|
||||
$this->getColumnIndex($index, 'offset', __METHOD__)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $offset
|
||||
*/
|
||||
protected function yieldColumn($offset): Generator
|
||||
{
|
||||
$iterator = new MapIterator(
|
||||
new CallbackFilterIterator($this->records, fn (array $record): bool => isset($record[$offset])),
|
||||
fn (array $record): string => $record[$offset]
|
||||
);
|
||||
|
||||
foreach ($iterator as $key => $value) {
|
||||
yield $key => $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a column name against the header if any.
|
||||
*
|
||||
* @param string|int $field the field name or the field index
|
||||
*
|
||||
* @throws InvalidArgument if the field is invalid or not found
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
protected function getColumnIndex($field, string $type, string $method)
|
||||
{
|
||||
if (is_string($field)) {
|
||||
return $this->getColumnIndexByValue($field, $type, $method);
|
||||
}
|
||||
|
||||
return $this->getColumnIndexByKey($field, $type, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected column name.
|
||||
*
|
||||
* @throws InvalidArgument if the column is not found
|
||||
*/
|
||||
protected function getColumnIndexByValue(string $value, string $type, string $method): string
|
||||
{
|
||||
if (false === array_search($value, $this->header, true)) {
|
||||
throw InvalidArgument::dueToInvalidColumnIndex($value, $type, $method);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected column name according to its offset.
|
||||
*
|
||||
* @throws InvalidArgument if the field is invalid or not found
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
protected function getColumnIndexByKey(int $index, string $type, string $method)
|
||||
{
|
||||
if ($index < 0) {
|
||||
throw InvalidArgument::dueToInvalidColumnIndex($index, $type, $method);
|
||||
}
|
||||
|
||||
if ([] === $this->header) {
|
||||
return $index;
|
||||
}
|
||||
|
||||
$value = array_search($index, array_flip($this->header), true);
|
||||
if (false === $value) {
|
||||
throw InvalidArgument::dueToInvalidColumnIndex($index, $type, $method);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function fetchPairs($offset_index = 0, $value_index = 1): Iterator
|
||||
{
|
||||
$offset = $this->getColumnIndex($offset_index, 'offset', __METHOD__);
|
||||
$value = $this->getColumnIndex($value_index, 'value', __METHOD__);
|
||||
|
||||
$iterator = new MapIterator(
|
||||
new CallbackFilterIterator($this->records, fn (array $record): bool => isset($record[$offset])),
|
||||
fn (array $record): array => [$record[$offset], $record[$value] ?? null]
|
||||
);
|
||||
|
||||
/** @var array{0:int|string, 1:string|null} $pair */
|
||||
foreach ($iterator as $pair) {
|
||||
yield $pair[0] => $pair[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
166
system/vendor/league/csv/src/Statement.php
vendored
Executable file
166
system/vendor/league/csv/src/Statement.php
vendored
Executable file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use ArrayIterator;
|
||||
use CallbackFilterIterator;
|
||||
use Iterator;
|
||||
use LimitIterator;
|
||||
use function array_reduce;
|
||||
|
||||
/**
|
||||
* Criteria to filter a {@link Reader} object.
|
||||
*/
|
||||
class Statement
|
||||
{
|
||||
/** @var array<callable> Callables to filter the iterator. */
|
||||
protected array $where = [];
|
||||
/** @var array<callable> Callables to sort the iterator. */
|
||||
protected array $order_by = [];
|
||||
/** iterator Offset. */
|
||||
protected int $offset = 0;
|
||||
/** iterator maximum length. */
|
||||
protected int $limit = -1;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function create(callable $where = null, int $offset = 0, int $limit = -1): self
|
||||
{
|
||||
$stmt = new self();
|
||||
if (null !== $where) {
|
||||
$stmt = $stmt->where($where);
|
||||
}
|
||||
|
||||
return $stmt->offset($offset)->limit($limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Iterator filter method.
|
||||
*/
|
||||
public function where(callable $where): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->where[] = $where;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an Iterator sorting callable function.
|
||||
*/
|
||||
public function orderBy(callable $order_by): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->order_by[] = $order_by;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set LimitIterator Offset.
|
||||
*
|
||||
* @throws Exception if the offset is lesser than 0
|
||||
*/
|
||||
public function offset(int $offset): self
|
||||
{
|
||||
if (0 > $offset) {
|
||||
throw InvalidArgument::dueToInvalidRecordOffset($offset, __METHOD__);
|
||||
}
|
||||
|
||||
if ($offset === $this->offset) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$clone = clone $this;
|
||||
$clone->offset = $offset;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set LimitIterator Count.
|
||||
*
|
||||
* @throws Exception if the limit is lesser than -1
|
||||
*/
|
||||
public function limit(int $limit): self
|
||||
{
|
||||
if (-1 > $limit) {
|
||||
throw InvalidArgument::dueToInvalidLimit($limit, __METHOD__);
|
||||
}
|
||||
|
||||
if ($limit === $this->limit) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$clone = clone $this;
|
||||
$clone->limit = $limit;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the prepared Statement on the {@link Reader} object.
|
||||
*
|
||||
* @param array<string> $header an optional header to use instead of the CSV document header
|
||||
*/
|
||||
public function process(TabularDataReader $tabular_data, array $header = []): TabularDataReader
|
||||
{
|
||||
if ([] === $header) {
|
||||
$header = $tabular_data->getHeader();
|
||||
}
|
||||
|
||||
$iterator = $tabular_data->getRecords($header);
|
||||
$iterator = array_reduce($this->where, [$this, 'filter'], $iterator);
|
||||
$iterator = $this->buildOrderBy($iterator);
|
||||
|
||||
return new ResultSet(new LimitIterator($iterator, $this->offset, $this->limit), $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters elements of an Iterator using a callback function.
|
||||
*/
|
||||
protected function filter(Iterator $iterator, callable $callable): CallbackFilterIterator
|
||||
{
|
||||
return new CallbackFilterIterator($iterator, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the Iterator.
|
||||
*/
|
||||
protected function buildOrderBy(Iterator $iterator): Iterator
|
||||
{
|
||||
if ([] === $this->order_by) {
|
||||
return $iterator;
|
||||
}
|
||||
|
||||
$compare = function (array $record_a, array $record_b): int {
|
||||
foreach ($this->order_by as $callable) {
|
||||
if (0 !== ($cmp = $callable($record_a, $record_b))) {
|
||||
return $cmp;
|
||||
}
|
||||
}
|
||||
|
||||
return $cmp ?? 0;
|
||||
};
|
||||
|
||||
$it = new ArrayIterator();
|
||||
foreach ($iterator as $offset => $value) {
|
||||
$it[$offset] = $value;
|
||||
}
|
||||
$it->uasort($compare);
|
||||
|
||||
return $it;
|
||||
}
|
||||
}
|
||||
447
system/vendor/league/csv/src/Stream.php
vendored
Executable file
447
system/vendor/league/csv/src/Stream.php
vendored
Executable file
@@ -0,0 +1,447 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use ReturnTypeWillChange;
|
||||
use SeekableIterator;
|
||||
use SplFileObject;
|
||||
use TypeError;
|
||||
use function array_keys;
|
||||
use function array_walk_recursive;
|
||||
use function fclose;
|
||||
use function feof;
|
||||
use function fflush;
|
||||
use function fgetcsv;
|
||||
use function fgets;
|
||||
use function fopen;
|
||||
use function fpassthru;
|
||||
use function fputcsv;
|
||||
use function fread;
|
||||
use function fseek;
|
||||
use function fwrite;
|
||||
use function get_resource_type;
|
||||
use function gettype;
|
||||
use function is_array;
|
||||
use function is_resource;
|
||||
use function rewind;
|
||||
use function stream_filter_append;
|
||||
use function stream_filter_remove;
|
||||
use function stream_get_meta_data;
|
||||
use function strlen;
|
||||
use const PHP_VERSION_ID;
|
||||
use const SEEK_SET;
|
||||
|
||||
/**
|
||||
* An object oriented API to handle a PHP stream resource.
|
||||
*
|
||||
* @internal used internally to iterate over a stream resource
|
||||
*/
|
||||
final class Stream implements SeekableIterator
|
||||
{
|
||||
/** @var array<string, array<resource>> Attached filters. */
|
||||
private array $filters = [];
|
||||
/** @var resource */
|
||||
private $stream;
|
||||
private bool $should_close_stream = false;
|
||||
/** @var mixed can be a null false or a scalar type value. Current iterator value. */
|
||||
private $value;
|
||||
/** Current iterator key. */
|
||||
private int $offset;
|
||||
/** Flags for the Document.*/
|
||||
private int $flags = 0;
|
||||
private string $delimiter = ',';
|
||||
private string $enclosure = '"';
|
||||
private string $escape = '\\';
|
||||
private bool $is_seekable = false;
|
||||
|
||||
/**
|
||||
* @param mixed $stream stream type resource
|
||||
*/
|
||||
public function __construct($stream)
|
||||
{
|
||||
if (!is_resource($stream)) {
|
||||
throw new TypeError('Argument passed must be a stream resource, '.gettype($stream).' given.');
|
||||
}
|
||||
|
||||
if ('stream' !== ($type = get_resource_type($stream))) {
|
||||
throw new TypeError('Argument passed must be a stream resource, '.$type.' resource given');
|
||||
}
|
||||
|
||||
$this->is_seekable = stream_get_meta_data($stream)['seekable'];
|
||||
$this->stream = $stream;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
array_walk_recursive($this->filters, fn ($filter): bool => @stream_filter_remove($filter));
|
||||
|
||||
if ($this->should_close_stream && is_resource($this->stream)) {
|
||||
fclose($this->stream);
|
||||
}
|
||||
|
||||
unset($this->stream);
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
throw UnavailableStream::dueToForbiddenCloning(self::class);
|
||||
}
|
||||
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return stream_get_meta_data($this->stream) + [
|
||||
'delimiter' => $this->delimiter,
|
||||
'enclosure' => $this->enclosure,
|
||||
'escape' => $this->escape,
|
||||
'stream_filters' => array_keys($this->filters),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance from a file path.
|
||||
*
|
||||
* @param resource|null $context
|
||||
*
|
||||
* @throws Exception if the stream resource can not be created
|
||||
*/
|
||||
public static function createFromPath(string $path, string $open_mode = 'r', $context = null): self
|
||||
{
|
||||
$args = [$path, $open_mode];
|
||||
if (null !== $context) {
|
||||
$args[] = false;
|
||||
$args[] = $context;
|
||||
}
|
||||
|
||||
$resource = @fopen(...$args);
|
||||
if (!is_resource($resource)) {
|
||||
throw UnavailableStream::dueToPathNotFound($path);
|
||||
}
|
||||
|
||||
$instance = new self($resource);
|
||||
$instance->should_close_stream = true;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance from a string.
|
||||
*/
|
||||
public static function createFromString(string $content = ''): self
|
||||
{
|
||||
/** @var resource $resource */
|
||||
$resource = fopen('php://temp', 'r+');
|
||||
fwrite($resource, $content);
|
||||
|
||||
$instance = new self($resource);
|
||||
$instance->should_close_stream = true;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the URI of the underlying stream.
|
||||
*
|
||||
* @see https://www.php.net/manual/en/splfileinfo.getpathname.php
|
||||
*/
|
||||
public function getPathname(): string
|
||||
{
|
||||
return stream_get_meta_data($this->stream)['uri'];
|
||||
}
|
||||
|
||||
/**
|
||||
* append a filter.
|
||||
*
|
||||
* @see http://php.net/manual/en/function.stream-filter-append.php
|
||||
*
|
||||
* @throws InvalidArgument if the filter can not be appended
|
||||
*/
|
||||
public function appendFilter(string $filtername, int $read_write, array $params = null): void
|
||||
{
|
||||
$res = @stream_filter_append($this->stream, $filtername, $read_write, $params ?? []);
|
||||
if (!is_resource($res)) {
|
||||
throw InvalidArgument::dueToStreamFilterNotFound($filtername);
|
||||
}
|
||||
|
||||
$this->filters[$filtername][] = $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set CSV control.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.setcsvcontrol.php
|
||||
*/
|
||||
public function setCsvControl(string $delimiter = ',', string $enclosure = '"', string $escape = '\\'): void
|
||||
{
|
||||
[$this->delimiter, $this->enclosure, $this->escape] = $this->filterControl($delimiter, $enclosure, $escape, __METHOD__);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter Csv control characters.
|
||||
*
|
||||
* @throws InvalidArgument If the Csv control character is not one character only.
|
||||
*/
|
||||
private function filterControl(string $delimiter, string $enclosure, string $escape, string $caller): array
|
||||
{
|
||||
if (1 !== strlen($delimiter)) {
|
||||
throw InvalidArgument::dueToInvalidDelimiterCharacter($delimiter, $caller);
|
||||
}
|
||||
|
||||
if (1 !== strlen($enclosure)) {
|
||||
throw InvalidArgument::dueToInvalidEnclosureCharacter($enclosure, $caller);
|
||||
}
|
||||
|
||||
if (1 === strlen($escape) || ('' === $escape && 70400 <= PHP_VERSION_ID)) {
|
||||
return [$delimiter, $enclosure, $escape];
|
||||
}
|
||||
|
||||
throw InvalidArgument::dueToInvalidEscapeCharacter($escape, $caller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set CSV control.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.getcsvcontrol.php
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getCsvControl(): array
|
||||
{
|
||||
return [$this->delimiter, $this->enclosure, $this->escape];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set CSV stream flags.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.setflags.php
|
||||
*/
|
||||
public function setFlags(int $flags): void
|
||||
{
|
||||
$this->flags = $flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a field array as a CSV line.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.fputcsv.php
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public function fputcsv(array $fields, string $delimiter = ',', string $enclosure = '"', string $escape = '\\', string $eol = "\n")
|
||||
{
|
||||
$controls = $this->filterControl($delimiter, $enclosure, $escape, __METHOD__);
|
||||
if (80100 <= PHP_VERSION_ID) {
|
||||
$controls[] = $eol;
|
||||
}
|
||||
|
||||
return fputcsv($this->stream, $fields, ...$controls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get line number.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.key.php
|
||||
*/
|
||||
public function key(): int
|
||||
{
|
||||
return $this->offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read next line.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.next.php
|
||||
*/
|
||||
public function next(): void
|
||||
{
|
||||
$this->value = false;
|
||||
$this->offset++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the file to the first line.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.rewind.php
|
||||
*
|
||||
* @throws Exception if the stream resource is not seekable
|
||||
*/
|
||||
public function rewind(): void
|
||||
{
|
||||
if (!$this->is_seekable) {
|
||||
throw UnavailableFeature::dueToMissingStreamSeekability();
|
||||
}
|
||||
|
||||
rewind($this->stream);
|
||||
$this->offset = 0;
|
||||
$this->value = false;
|
||||
if (0 !== ($this->flags & SplFileObject::READ_AHEAD)) {
|
||||
$this->current();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Not at EOF.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.valid.php
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
if (0 !== ($this->flags & SplFileObject::READ_AHEAD)) {
|
||||
return $this->current() !== false;
|
||||
}
|
||||
|
||||
return !feof($this->stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current line of the file.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.current.php
|
||||
*
|
||||
* @return mixed The value of the current element.
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
if (false !== $this->value) {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
$this->value = $this->getCurrentRecord();
|
||||
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current line as a CSV Record.
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
private function getCurrentRecord()
|
||||
{
|
||||
$flag = 0 !== ($this->flags & SplFileObject::SKIP_EMPTY);
|
||||
do {
|
||||
$ret = fgetcsv($this->stream, 0, $this->delimiter, $this->enclosure, $this->escape);
|
||||
} while ($flag && is_array($ret) && null === $ret[0]);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to specified line.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.seek.php
|
||||
*
|
||||
* @param int $position
|
||||
* @throws Exception if the position is negative
|
||||
*/
|
||||
public function seek($position): void
|
||||
{
|
||||
if ($position < 0) {
|
||||
throw InvalidArgument::dueToInvalidSeekingPosition($position, __METHOD__);
|
||||
}
|
||||
|
||||
$this->rewind();
|
||||
while ($this->key() !== $position && $this->valid()) {
|
||||
$this->current();
|
||||
$this->next();
|
||||
}
|
||||
|
||||
if (0 !== $position) {
|
||||
$this->offset--;
|
||||
}
|
||||
|
||||
$this->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output all remaining data on a file pointer.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.fpatssthru.php
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public function fpassthru()
|
||||
{
|
||||
return fpassthru($this->stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read from file.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.fread.php
|
||||
*
|
||||
* @param int<0, max> $length The number of bytes to read
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function fread(int $length)
|
||||
{
|
||||
return fread($this->stream, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a line from file.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.fgets.php
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function fgets()
|
||||
{
|
||||
return fgets($this->stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to a position.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.fseek.php
|
||||
*
|
||||
* @throws Exception if the stream resource is not seekable
|
||||
*/
|
||||
public function fseek(int $offset, int $whence = SEEK_SET): int
|
||||
{
|
||||
if (!$this->is_seekable) {
|
||||
throw UnavailableFeature::dueToMissingStreamSeekability();
|
||||
}
|
||||
|
||||
return fseek($this->stream, $offset, $whence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to stream.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.fwrite.php
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public function fwrite(string $str, int $length = null)
|
||||
{
|
||||
$args = [$this->stream, $str];
|
||||
if (null !== $length) {
|
||||
$args[] = $length;
|
||||
}
|
||||
|
||||
return fwrite(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the output to a file.
|
||||
*
|
||||
* @see http://php.net/manual/en/SplFileObject.fwrite.php
|
||||
*/
|
||||
public function fflush(): bool
|
||||
{
|
||||
return fflush($this->stream);
|
||||
}
|
||||
}
|
||||
60
system/vendor/league/csv/src/SyntaxError.php
vendored
Executable file
60
system/vendor/league/csv/src/SyntaxError.php
vendored
Executable file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* SyntaxError Exception.
|
||||
*/
|
||||
class SyntaxError extends Exception
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array $duplicateColumnNames = [];
|
||||
|
||||
/**
|
||||
* DEPRECATION WARNING! This class will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.7.0
|
||||
*/
|
||||
public function __construct(string $message = '', int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public static function dueToHeaderNotFound(int $offset): self
|
||||
{
|
||||
return new self('The header record does not exist or is empty at offset: `'.$offset.'`');
|
||||
}
|
||||
|
||||
public static function dueToInvalidHeaderColumnNames(): self
|
||||
{
|
||||
return new self('The header record contains non string colum names.');
|
||||
}
|
||||
|
||||
public static function dueToDuplicateHeaderColumnNames(array $header): self
|
||||
{
|
||||
$instance = new self('The header record contains duplicate column names.');
|
||||
$instance->duplicateColumnNames = array_keys(array_filter(array_count_values($header), fn (int $value): bool => $value > 1));
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public function duplicateColumnNames(): array
|
||||
{
|
||||
return $this->duplicateColumnNames;
|
||||
}
|
||||
}
|
||||
117
system/vendor/league/csv/src/TabularDataReader.php
vendored
Executable file
117
system/vendor/league/csv/src/TabularDataReader.php
vendored
Executable file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use Countable;
|
||||
use Iterator;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* Represents a Tabular data.
|
||||
*
|
||||
* @method Iterator fetchColumnByName(string $name) returns a column from its name
|
||||
* @method Iterator fetchColumnByOffset(int $offset) returns a column from its offset
|
||||
*/
|
||||
interface TabularDataReader extends Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Returns the number of records contained in the tabular data structure
|
||||
* excluding the header record.
|
||||
*/
|
||||
public function count(): int;
|
||||
|
||||
/**
|
||||
* Returns the tabular data records as an iterator object.
|
||||
*
|
||||
* Each record is represented as a simple array containing strings or null values.
|
||||
*
|
||||
* If the CSV document has a header record then each record is combined
|
||||
* to the header record and the header record is removed from the iterator.
|
||||
*
|
||||
* If the CSV document is inconsistent. Missing record fields are
|
||||
* filled with null values while extra record fields are strip from
|
||||
* the returned object.
|
||||
*/
|
||||
public function getIterator(): Iterator;
|
||||
|
||||
/**
|
||||
* Returns the header associated with the tabular data.
|
||||
*
|
||||
* The header must contains unique string or is an empty array
|
||||
* if no header was specified.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getHeader(): array;
|
||||
|
||||
/**
|
||||
* Returns the tabular data records as an iterator object.
|
||||
*
|
||||
* Each record is represented as a simple array containing strings or null values.
|
||||
*
|
||||
* If the tabular data has a header record then each record is combined
|
||||
* to the header record and the header record is removed from the iterator.
|
||||
*
|
||||
* If the tabular data is inconsistent. Missing record fields are
|
||||
* filled with null values while extra record fields are strip from
|
||||
* the returned object.
|
||||
*
|
||||
* @param array<string> $header an optional header to use instead of the CSV document header
|
||||
*/
|
||||
public function getRecords(array $header = []): Iterator;
|
||||
|
||||
/**
|
||||
* Returns the nth record from the tabular data.
|
||||
*
|
||||
* By default if no index is provided the first record of the tabular data is returned
|
||||
*
|
||||
* @param int $nth_record the tabular data record offset
|
||||
*
|
||||
* @throws UnableToProcessCsv if argument is lesser than 0
|
||||
*/
|
||||
public function fetchOne(int $nth_record = 0): array;
|
||||
|
||||
/**
|
||||
* DEPRECATION WARNING! This class will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.8.0
|
||||
*
|
||||
* @see ::fetchColumnByName
|
||||
* @see ::fetchColumnByOffset
|
||||
*
|
||||
* Returns a single column from the next record of the tabular data.
|
||||
*
|
||||
* By default if no value is supplied the first column is fetch
|
||||
*
|
||||
* @param string|int $index CSV column index
|
||||
*
|
||||
* @throws UnableToProcessCsv if the column index is invalid or not found
|
||||
*/
|
||||
public function fetchColumn($index = 0): Iterator;
|
||||
|
||||
/**
|
||||
* Returns the next key-value pairs from the tabular data (first
|
||||
* column is the key, second column is the value).
|
||||
*
|
||||
* By default if no column index is provided:
|
||||
* - the first column is used to provide the keys
|
||||
* - the second column is used to provide the value
|
||||
*
|
||||
* @param string|int $offset_index The column index to serve as offset
|
||||
* @param string|int $value_index The column index to serve as value
|
||||
*
|
||||
* @throws UnableToProcessCsv if the column index is invalid or not found
|
||||
*/
|
||||
public function fetchPairs($offset_index = 0, $value_index = 1): Iterator;
|
||||
}
|
||||
20
system/vendor/league/csv/src/UnableToProcessCsv.php
vendored
Executable file
20
system/vendor/league/csv/src/UnableToProcessCsv.php
vendored
Executable file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use Throwable;
|
||||
|
||||
interface UnableToProcessCsv extends Throwable
|
||||
{
|
||||
}
|
||||
42
system/vendor/league/csv/src/UnavailableFeature.php
vendored
Executable file
42
system/vendor/league/csv/src/UnavailableFeature.php
vendored
Executable file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* StreamFilterSupportMissing Exception.
|
||||
*/
|
||||
class UnavailableFeature extends Exception
|
||||
{
|
||||
/**
|
||||
* DEPRECATION WARNING! This class will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.7.0
|
||||
*/
|
||||
public function __construct(string $message = '', int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public static function dueToUnsupportedStreamFilterApi(string $className): self
|
||||
{
|
||||
return new self('The stream filter API can not be used with a '.$className.' instance.');
|
||||
}
|
||||
|
||||
public static function dueToMissingStreamSeekability(): self
|
||||
{
|
||||
return new self('stream does not support seeking');
|
||||
}
|
||||
}
|
||||
32
system/vendor/league/csv/src/UnavailableStream.php
vendored
Executable file
32
system/vendor/league/csv/src/UnavailableStream.php
vendored
Executable file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
final class UnavailableStream extends Exception
|
||||
{
|
||||
private function __construct(string $message)
|
||||
{
|
||||
parent::__construct($message);
|
||||
}
|
||||
|
||||
public static function dueToPathNotFound(string $path): self
|
||||
{
|
||||
return new self('`'.$path.'`: failed to open stream: No such file or directory.');
|
||||
}
|
||||
|
||||
public static function dueToForbiddenCloning(string $class_name): self
|
||||
{
|
||||
return new self('An object of class '.$class_name.' cannot be cloned.');
|
||||
}
|
||||
}
|
||||
222
system/vendor/league/csv/src/Writer.php
vendored
Executable file
222
system/vendor/league/csv/src/Writer.php
vendored
Executable file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use function array_reduce;
|
||||
use function strlen;
|
||||
use const PHP_VERSION_ID;
|
||||
use const SEEK_CUR;
|
||||
use const STREAM_FILTER_WRITE;
|
||||
|
||||
/**
|
||||
* A class to insert records into a CSV Document.
|
||||
*/
|
||||
class Writer extends AbstractCsv
|
||||
{
|
||||
protected const STREAM_FILTER_MODE = STREAM_FILTER_WRITE;
|
||||
|
||||
/** @var array<callable> callable collection to format the record before insertion. */
|
||||
protected array $formatters = [];
|
||||
/** @var array<callable> callable collection to validate the record before insertion. */
|
||||
protected array $validators = [];
|
||||
protected string $newline = "\n";
|
||||
protected int $flush_counter = 0;
|
||||
protected ?int $flush_threshold = null;
|
||||
|
||||
protected function resetProperties(): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current newline sequence characters.
|
||||
*/
|
||||
public function getNewline(): string
|
||||
{
|
||||
return $this->newline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the flush threshold.
|
||||
*/
|
||||
public function getFlushThreshold(): ?int
|
||||
{
|
||||
return $this->flush_threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple records to the CSV document.
|
||||
*
|
||||
* @see Writer::insertOne
|
||||
*/
|
||||
public function insertAll(iterable $records): int
|
||||
{
|
||||
$bytes = 0;
|
||||
foreach ($records as $record) {
|
||||
$bytes += $this->insertOne($record);
|
||||
}
|
||||
|
||||
$this->flush_counter = 0;
|
||||
$this->document->fflush();
|
||||
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single record to a CSV document.
|
||||
*
|
||||
* A record is an array that can contains scalar types values, NULL values
|
||||
* or objects implementing the __toString method.
|
||||
*
|
||||
* @throws CannotInsertRecord If the record can not be inserted
|
||||
*/
|
||||
public function insertOne(array $record): int
|
||||
{
|
||||
$record = array_reduce($this->formatters, fn (array $record, callable $formatter): array => $formatter($record), $record);
|
||||
$this->validateRecord($record);
|
||||
$bytes = $this->addRecord($record);
|
||||
if (false === $bytes || 0 >= $bytes) {
|
||||
throw CannotInsertRecord::triggerOnInsertion($record);
|
||||
}
|
||||
|
||||
return $bytes + $this->consolidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single record to a CSV Document using PHP algorithm.
|
||||
*
|
||||
* @see https://php.net/manual/en/function.fputcsv.php
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
protected function addRecord(array $record)
|
||||
{
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
return $this->document->fputcsv($record, $this->delimiter, $this->enclosure, $this->escape);
|
||||
}
|
||||
|
||||
return $this->document->fputcsv($record, $this->delimiter, $this->enclosure, $this->escape, $this->newline);
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATION WARNING! This method will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.8.0
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* Format a record.
|
||||
*
|
||||
* The returned array must contain
|
||||
* - scalar types values,
|
||||
* - NULL values,
|
||||
* - or objects implementing the __toString() method.
|
||||
*/
|
||||
protected function formatRecord(array $record, callable $formatter): array
|
||||
{
|
||||
return $formatter($record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a record.
|
||||
*
|
||||
* @throws CannotInsertRecord If the validation failed
|
||||
*/
|
||||
protected function validateRecord(array $record): void
|
||||
{
|
||||
foreach ($this->validators as $name => $validator) {
|
||||
if (true !== $validator($record)) {
|
||||
throw CannotInsertRecord::triggerOnValidation($name, $record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply post insertion actions.
|
||||
*/
|
||||
protected function consolidate(): int
|
||||
{
|
||||
$bytes = 0;
|
||||
if (80100 > PHP_VERSION_ID && "\n" !== $this->newline) {
|
||||
$this->document->fseek(-1, SEEK_CUR);
|
||||
/** @var int $newlineBytes */
|
||||
$newlineBytes = $this->document->fwrite($this->newline, strlen($this->newline));
|
||||
$bytes = $newlineBytes - 1;
|
||||
}
|
||||
|
||||
if (null === $this->flush_threshold) {
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
++$this->flush_counter;
|
||||
if (0 === $this->flush_counter % $this->flush_threshold) {
|
||||
$this->flush_counter = 0;
|
||||
$this->document->fflush();
|
||||
}
|
||||
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a record formatter.
|
||||
*/
|
||||
public function addFormatter(callable $formatter): self
|
||||
{
|
||||
$this->formatters[] = $formatter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a record validator.
|
||||
*/
|
||||
public function addValidator(callable $validator, string $validator_name): self
|
||||
{
|
||||
$this->validators[$validator_name] = $validator;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the newline sequence.
|
||||
*/
|
||||
public function setNewline(string $newline): self
|
||||
{
|
||||
$this->newline = $newline;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flush threshold.
|
||||
*
|
||||
* @param ?int $threshold
|
||||
*
|
||||
* @throws InvalidArgument if the threshold is a integer lesser than 1
|
||||
*/
|
||||
public function setFlushThreshold(?int $threshold): self
|
||||
{
|
||||
if ($threshold === $this->flush_threshold) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (null !== $threshold && 1 > $threshold) {
|
||||
throw InvalidArgument::dueToInvalidThreshold($threshold, __METHOD__);
|
||||
}
|
||||
|
||||
$this->flush_threshold = $threshold;
|
||||
$this->flush_counter = 0;
|
||||
$this->document->fflush();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
193
system/vendor/league/csv/src/XMLConverter.php
vendored
Executable file
193
system/vendor/league/csv/src/XMLConverter.php
vendored
Executable file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
use DOMAttr;
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMException;
|
||||
|
||||
/**
|
||||
* Converts tabular data into a DOMDocument object.
|
||||
*/
|
||||
class XMLConverter
|
||||
{
|
||||
/**
|
||||
* XML Root name.
|
||||
*/
|
||||
protected string $root_name = 'csv';
|
||||
|
||||
/**
|
||||
* XML Node name.
|
||||
*/
|
||||
protected string $record_name = 'row';
|
||||
|
||||
/**
|
||||
* XML Item name.
|
||||
*/
|
||||
protected string $field_name = 'cell';
|
||||
|
||||
/**
|
||||
* XML column attribute name.
|
||||
*/
|
||||
protected string $column_attr = '';
|
||||
|
||||
/**
|
||||
* XML offset attribute name.
|
||||
*/
|
||||
protected string $offset_attr = '';
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATION WARNING! This method will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.7.0
|
||||
* @see XMLConverter::create()
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Record collection into a DOMDocument.
|
||||
*/
|
||||
public function convert(iterable $records): DOMDocument
|
||||
{
|
||||
$doc = new DOMDocument('1.0');
|
||||
$node = $this->import($records, $doc);
|
||||
$doc->appendChild($node);
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DOMElement related to the given DOMDocument.
|
||||
*
|
||||
* **DOES NOT** attach to the DOMDocument
|
||||
*/
|
||||
public function import(iterable $records, DOMDocument $doc): DOMElement
|
||||
{
|
||||
$root = $doc->createElement($this->root_name);
|
||||
foreach ($records as $offset => $record) {
|
||||
$node = $this->recordToElement($doc, $record, $offset);
|
||||
$root->appendChild($node);
|
||||
}
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a CSV record into a DOMElement and
|
||||
* adds its offset as DOMElement attribute.
|
||||
*/
|
||||
protected function recordToElement(DOMDocument $doc, array $record, int $offset): DOMElement
|
||||
{
|
||||
$node = $doc->createElement($this->record_name);
|
||||
foreach ($record as $node_name => $value) {
|
||||
$item = $this->fieldToElement($doc, (string) $value, $node_name);
|
||||
$node->appendChild($item);
|
||||
}
|
||||
|
||||
if ('' !== $this->offset_attr) {
|
||||
$node->setAttribute($this->offset_attr, (string) $offset);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Cell to Item.
|
||||
*
|
||||
* Convert the CSV item into a DOMElement and adds the item offset
|
||||
* as attribute to the returned DOMElement
|
||||
*
|
||||
* @param int|string $node_name
|
||||
*/
|
||||
protected function fieldToElement(DOMDocument $doc, string $value, $node_name): DOMElement
|
||||
{
|
||||
$item = $doc->createElement($this->field_name);
|
||||
$item->appendChild($doc->createTextNode($value));
|
||||
|
||||
if ('' !== $this->column_attr) {
|
||||
$item->setAttribute($this->column_attr, (string) $node_name);
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML root element setter.
|
||||
*/
|
||||
public function rootElement(string $node_name): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->root_name = $this->filterElementName($node_name);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter XML element name.
|
||||
*
|
||||
* @throws DOMException If the Element name is invalid
|
||||
*/
|
||||
protected function filterElementName(string $value): string
|
||||
{
|
||||
return (new DOMElement($value))->tagName;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML Record element setter.
|
||||
*/
|
||||
public function recordElement(string $node_name, string $record_offset_attribute_name = ''): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->record_name = $this->filterElementName($node_name);
|
||||
$clone->offset_attr = $this->filterAttributeName($record_offset_attribute_name);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter XML attribute name.
|
||||
*
|
||||
* @param string $value Element name
|
||||
*
|
||||
* @throws DOMException If the Element attribute name is invalid
|
||||
*/
|
||||
protected function filterAttributeName(string $value): string
|
||||
{
|
||||
if ('' === $value) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return (new DOMAttr($value))->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML Field element setter.
|
||||
*/
|
||||
public function fieldElement(string $node_name, string $fieldname_attribute_name = ''): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->field_name = $this->filterElementName($node_name);
|
||||
$clone->column_attr = $this->filterAttributeName($fieldname_attribute_name);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
}
|
||||
48
system/vendor/league/csv/src/functions.php
vendored
Executable file
48
system/vendor/league/csv/src/functions.php
vendored
Executable file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Csv;
|
||||
|
||||
/**
|
||||
* DEPRECATION WARNING! This class will be removed in the next major point release.
|
||||
*
|
||||
* @deprecated since version 9.7.0
|
||||
* @see Info::fetchBOMSequence()
|
||||
*
|
||||
* Returns the BOM sequence found at the start of the string.
|
||||
*
|
||||
* If no valid BOM sequence is found an empty string is returned
|
||||
*/
|
||||
function bom_match(string $str): string
|
||||
{
|
||||
return Info::fetchBOMSequence($str) ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $delimiters
|
||||
*
|
||||
* @return array<string,int>
|
||||
* @deprecated since version 9.7.0
|
||||
* @see Info::getDelimiterStats()
|
||||
*
|
||||
* Detect Delimiters usage in a {@link Reader} object.
|
||||
*
|
||||
* Returns a associative array where each key represents
|
||||
* a submitted delimiter and each value the number CSV fields found
|
||||
* when processing at most $limit CSV records with the given delimiter
|
||||
*
|
||||
*/
|
||||
function delimiter_detect(Reader $csv, array $delimiters, int $limit = 1): array
|
||||
{
|
||||
return Info::getDelimiterStats($csv, $delimiters, $limit);
|
||||
}
|
||||
14
system/vendor/league/csv/src/functions_include.php
vendored
Executable file
14
system/vendor/league/csv/src/functions_include.php
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* League.Csv (https://csv.thephpleague.com)
|
||||
*
|
||||
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (!function_exists('League\Csv\bom_match')) {
|
||||
require __DIR__.'/functions.php';
|
||||
}
|
||||
Reference in New Issue
Block a user