This commit is contained in:
Tim Bendt
2025-11-25 00:16:35 -05:00
commit 6b9ef7ca55
6757 changed files with 1003748 additions and 0 deletions

492
system/vendor/league/csv/src/AbstractCsv.php vendored Executable file
View 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
View 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";
}

View 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;
}
}

View 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;
}
}

View 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
View 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
View 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
View 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
View 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
View 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;
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}
}

View 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;
}

View 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
{
}

View 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');
}
}

View 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
View 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
View 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
View 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);
}

View 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';
}