Commit 8d2e34ba authored by crell's avatar crell Committed by Stefan Bürk
Browse files

[!!!][TASK] Add native types to CsvUtility

Also, first class callable syntax is added for the csvValues
method.

Resolves: #97207
Related: #97210
Related: #97372
Releases: main
Change-Id: Ib37a497c8df38fa9ef2f071c10439de4d269b25c
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/74286

Tested-by: Nikita Hovratov's avatarNikita Hovratov <nikita.h@live.de>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Reviewed-by: Nikita Hovratov's avatarNikita Hovratov <nikita.h@live.de>
Reviewed-by: Oliver Klee's avatarOliver Klee <typo3-coding@oliverklee.de>
Reviewed-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
parent ffc5261b
<?php <?php
declare(strict_types=1);
/* /*
* This file is part of the TYPO3 CMS project. * This file is part of the TYPO3 CMS project.
* *
...@@ -49,7 +51,7 @@ class CsvUtility ...@@ -49,7 +51,7 @@ class CsvUtility
* @param int $maximumColumns The maximum amount of columns * @param int $maximumColumns The maximum amount of columns
* @return array * @return array
*/ */
public static function csvToArray($input, $fieldDelimiter = ',', $fieldEnclosure = '"', $maximumColumns = 0) public static function csvToArray(string $input, string $fieldDelimiter = ',', string $fieldEnclosure = '"', int $maximumColumns = 0): array
{ {
$multiArray = []; $multiArray = [];
$maximumCellCount = 0; $maximumCellCount = 0;
...@@ -97,33 +99,30 @@ class CsvUtility ...@@ -97,33 +99,30 @@ class CsvUtility
* @param int $type Output behaviour concerning potentially harmful control literals * @param int $type Output behaviour concerning potentially harmful control literals
* @return string A single line of CSV * @return string A single line of CSV
*/ */
public static function csvValues(array $row, string $delim = ',', string $quote = '"', int $type = self::TYPE_REMOVE_CONTROLS) public static function csvValues(array $row, string $delim = ',', string $quote = '"', int $type = self::TYPE_REMOVE_CONTROLS): string
{ {
$resource = fopen('php://temp', 'w'); $resource = fopen('php://temp', 'w');
if (!is_resource($resource)) { if (!is_resource($resource)) {
throw new \RuntimeException('Cannot open temporary data stream for writing', 1625556521); throw new \RuntimeException('Cannot open temporary data stream for writing', 1625556521);
} }
$modifier = CsvStreamFilter::applyStreamFilter($resource, false); $modifier = CsvStreamFilter::applyStreamFilter($resource, false);
array_map([self::class, 'assertCellValueType'], $row); array_map(self::assertCellValueType(...), $row);
if ($type === self::TYPE_REMOVE_CONTROLS) { if ($type === self::TYPE_REMOVE_CONTROLS) {
$row = array_map([self::class, 'removeControlLiterals'], $row); $row = array_map(self::removeControlLiterals(...), $row);
} elseif ($type === self::TYPE_PREFIX_CONTROLS) { } elseif ($type === self::TYPE_PREFIX_CONTROLS) {
$row = array_map([self::class, 'prefixControlLiterals'], $row); $row = array_map(self::prefixControlLiterals(...), $row);
} }
fputcsv($resource, $modifier($row), $delim, $quote); fputcsv($resource, $modifier($row), $delim, $quote);
fseek($resource, 0); fseek($resource, 0);
$content = stream_get_contents($resource); return stream_get_contents($resource);
return $content;
} }
/** /**
* Prefixes control literals at the beginning of a cell value with a single quote * Prefixes control literals at the beginning of a cell value with a single quote
* (e.g. `=+value` --> `'=+value`)
* *
* @param mixed $cellValue * (e.g. `=+value` --> `'=+value`)
* @return bool|int|float|string|null
*/ */
protected static function prefixControlLiterals($cellValue) protected static function prefixControlLiterals(bool|int|float|string|null $cellValue): bool|int|float|string|null
{ {
if (!self::shallFilterValue($cellValue)) { if (!self::shallFilterValue($cellValue)) {
return $cellValue; return $cellValue;
...@@ -134,12 +133,10 @@ class CsvUtility ...@@ -134,12 +133,10 @@ class CsvUtility
/** /**
* Removes control literals from the beginning of a cell value * Removes control literals from the beginning of a cell value
* (e.g. `=+value` --> `value`)
* *
* @param mixed $cellValue * (e.g. `=+value` --> `value`)
* @return bool|int|float|string|null
*/ */
protected static function removeControlLiterals($cellValue) protected static function removeControlLiterals(bool|int|float|string|null $cellValue): bool|int|float|string|null
{ {
if (!self::shallFilterValue($cellValue)) { if (!self::shallFilterValue($cellValue)) {
return $cellValue; return $cellValue;
...@@ -150,10 +147,8 @@ class CsvUtility ...@@ -150,10 +147,8 @@ class CsvUtility
/** /**
* Asserts scalar or null types for given cell value. * Asserts scalar or null types for given cell value.
*
* @param mixed $cellValue
*/ */
protected static function assertCellValueType($cellValue): void protected static function assertCellValueType(mixed $cellValue): void
{ {
// int, float, string, bool, null // int, float, string, bool, null
if ($cellValue === null || is_scalar($cellValue)) { if ($cellValue === null || is_scalar($cellValue)) {
...@@ -166,13 +161,12 @@ class CsvUtility ...@@ -166,13 +161,12 @@ class CsvUtility
} }
/** /**
* Whether cell value shall be filtered, applies to everything * Whether cell value shall be filtered.
* that is not or cannot be represented as boolean, integer or float.
* *
* @param mixed $cellValue * This applies to everything that is not or cannot be represented
* @return bool * as boolean, integer or float.
*/ */
protected static function shallFilterValue($cellValue): bool protected static function shallFilterValue(bool|int|float|string|null $cellValue): bool
{ {
return $cellValue !== null return $cellValue !== null
&& !is_bool($cellValue) && !is_bool($cellValue)
......
...@@ -15,6 +15,7 @@ expectations and existing behavior. ...@@ -15,6 +15,7 @@ expectations and existing behavior.
- :php:`\TYPO3\CMS\Core\Utility\ArrayUtility` - :php:`\TYPO3\CMS\Core\Utility\ArrayUtility`
- :php:`\TYPO3\CMS\Core\Utility\ClassNamingUtility` - :php:`\TYPO3\CMS\Core\Utility\ClassNamingUtility`
- :php:`\TYPO3\CMS\Core\Utility\CsvUtility`
- :php:`\TYPO3\CMS\Core\Utility\CommandUtility` - :php:`\TYPO3\CMS\Core\Utility\CommandUtility`
- :php:`\TYPO3\CMS\Core\Utility\DebugUtility` - :php:`\TYPO3\CMS\Core\Utility\DebugUtility`
- :php:`\TYPO3\CMS\Core\Utility\DiffUtility` - :php:`\TYPO3\CMS\Core\Utility\DiffUtility`
......
...@@ -92,7 +92,7 @@ class CsvUtilityTest extends UnitTestCase ...@@ -92,7 +92,7 @@ class CsvUtilityTest extends UnitTestCase
* @dataProvider csvToArrayDataProvider * @dataProvider csvToArrayDataProvider
* @test * @test
*/ */
public function csvToArraySplitsAsExpected($input, $fieldDelimiter, $fieldEnclosure, $maximumColumns, $expectedResult): void public function csvToArraySplitsAsExpected(string $input, string $fieldDelimiter, string $fieldEnclosure, int $maximumColumns, array $expectedResult): void
{ {
self::assertEquals($expectedResult, CsvUtility::csvToArray($input, $fieldDelimiter, $fieldEnclosure, $maximumColumns)); self::assertEquals($expectedResult, CsvUtility::csvToArray($input, $fieldDelimiter, $fieldEnclosure, $maximumColumns));
} }
...@@ -163,7 +163,7 @@ class CsvUtilityTest extends UnitTestCase ...@@ -163,7 +163,7 @@ class CsvUtilityTest extends UnitTestCase
* @dataProvider csvValuesDataProvider * @dataProvider csvValuesDataProvider
* @test * @test
*/ */
public function csvValuesReturnsExpectedResult($row, $delimiter, $quote, $expectedResult, $flag): void public function csvValuesReturnsExpectedResult(array $row, string $delimiter, string $quote, string $expectedResult, int $flag): void
{ {
self::assertEquals($expectedResult, CsvUtility::csvValues($row, $delimiter, $quote, $flag)); self::assertEquals($expectedResult, CsvUtility::csvValues($row, $delimiter, $quote, $flag));
} }
......
...@@ -77,7 +77,7 @@ class CommaSeparatedValueProcessor implements DataProcessorInterface ...@@ -77,7 +77,7 @@ class CommaSeparatedValueProcessor implements DataProcessorInterface
return $processedData; return $processedData;
} }
$originalValue = $cObj->data[$fieldName]; $originalValue = (string)$cObj->data[$fieldName];
// Set the target variable // Set the target variable
$targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, $fieldName); $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, $fieldName);
......
...@@ -860,7 +860,7 @@ class QueryGenerator ...@@ -860,7 +860,7 @@ class QueryGenerator
$valueArray[$key] = $this->getProcessedValueExtra($table, $key, $val, $conf, ';'); $valueArray[$key] = $this->getProcessedValueExtra($table, $key, $val, $conf, ';');
} }
} }
return CsvUtility::csvValues($valueArray, $delim, $quote); return CsvUtility::csvValues($valueArray, (string)$delim, (string)$quote);
} }
/** /**
......
...@@ -200,8 +200,8 @@ class RecordDownloadController ...@@ -200,8 +200,8 @@ class RecordDownloadController
array $records array $records
): ResponseInterface { ): ResponseInterface {
// Fetch csv related format options // Fetch csv related format options
$csvDelimiter = $this->getFormatOption($request, 'delimiter'); $csvDelimiter = (string)$this->getFormatOption($request, 'delimiter');
$csvQuote = $this->getFormatOption($request, 'quote'); $csvQuote = (string)$this->getFormatOption($request, 'quote');
// Create result // Create result
$result[] = CsvUtility::csvValues($headerRow, $csvDelimiter, $csvQuote); $result[] = CsvUtility::csvValues($headerRow, $csvDelimiter, $csvQuote);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment