TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\IO;
|
||||
|
||||
/**
|
||||
* Inspired by https://csv.thephpleague.com/9.0/interoperability/enclose-field/
|
||||
*
|
||||
* A unique sequence is added to relevant CSV field values in order to trigger enclosure in fputcsv.
|
||||
* This stream filter is taking care of removing that sequence again when actually writing to stream.
|
||||
*/
|
||||
class CsvStreamFilter extends \php_user_filter
|
||||
{
|
||||
protected const NAME = 'csv.typo3';
|
||||
|
||||
/**
|
||||
* Implicitly handles stream filter when writing CSV data - example:
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```
|
||||
* $resource = fopen('file.csv', 'w');
|
||||
* $modifier = CsvUtility::applyStreamFilter($resource);
|
||||
* fputcsv($resource, $modifier($fieldValues));
|
||||
* fclose($resource);
|
||||
* ```
|
||||
*
|
||||
* @param resource $in
|
||||
* @param resource $out
|
||||
* @param int $consumed
|
||||
* @param bool $closing
|
||||
* @param-out int $consumed
|
||||
*/
|
||||
public function filter($in, $out, &$consumed, $closing): int
|
||||
{
|
||||
while ($bucket = stream_bucket_make_writeable($in)) {
|
||||
// removes sequence boundary indicator
|
||||
$bucket->data = str_replace(
|
||||
$this->params['sequence'],
|
||||
'',
|
||||
$bucket->data
|
||||
);
|
||||
if ($this->params['LF'] === false) {
|
||||
// remove line-feed added by `fputcsv` per default
|
||||
$bucket->data = preg_replace('#\r?\n$#', '', $bucket->data);
|
||||
}
|
||||
$consumed += (int)$bucket->datalen;
|
||||
stream_bucket_append($out, $bucket);
|
||||
}
|
||||
return PSFS_PASS_ON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $stream
|
||||
* @param bool $LF whether to apply line-feed
|
||||
*/
|
||||
public static function applyStreamFilter($stream, bool $LF = true): \Closure
|
||||
{
|
||||
self::registerStreamFilter();
|
||||
// must contain a spacing character to enforce enclosure
|
||||
$sequence = "\t\x1d\x1e\x1f";
|
||||
stream_filter_append(
|
||||
$stream,
|
||||
self::NAME,
|
||||
STREAM_FILTER_WRITE,
|
||||
['sequence' => $sequence, 'LF' => $LF]
|
||||
);
|
||||
return self::buildStreamFilterModifier($sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers stream filter
|
||||
*/
|
||||
protected static function registerStreamFilter()
|
||||
{
|
||||
if (in_array(self::NAME, stream_get_filters(), true)) {
|
||||
return;
|
||||
}
|
||||
stream_filter_register(
|
||||
self::NAME,
|
||||
static::class
|
||||
);
|
||||
}
|
||||
|
||||
protected static function buildStreamFilterModifier(string $sequence): \Closure
|
||||
{
|
||||
return static function ($element) use ($sequence) {
|
||||
foreach ($element as &$value) {
|
||||
if (is_numeric($value) || $value === '') {
|
||||
continue;
|
||||
}
|
||||
$value = $sequence . $value;
|
||||
}
|
||||
unset($value); // de-reference
|
||||
return $element;
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user