From 72b9b496d5df61d4ebb3bbf6aee85460de86efe2 Mon Sep 17 00:00:00 2001 From: Georg Ringer <georg.ringer@gmail.com> Date: Mon, 24 Apr 2023 21:05:41 +0200 Subject: [PATCH] [TASK] Add StringUtility::cast and ::filter Resolves: #100824 Relates: #100739 Releases: main, 12.4, 11.5 Change-Id: I91bf93adbf390e84beda871c399f4785a83d8571 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/78993 Tested-by: core-ci <typo3@b13.com> Tested-by: Oliver Hader <oliver.hader@typo3.org> Reviewed-by: Oliver Hader <oliver.hader@typo3.org> --- .../core/Classes/Utility/StringUtility.php | 32 +++++ .../Tests/Unit/Utility/StringUtilityTest.php | 123 ++++++++++++++++++ 2 files changed, 155 insertions(+) diff --git a/typo3/sysext/core/Classes/Utility/StringUtility.php b/typo3/sysext/core/Classes/Utility/StringUtility.php index f8ec246d1440..2b69abb8f38a 100644 --- a/typo3/sysext/core/Classes/Utility/StringUtility.php +++ b/typo3/sysext/core/Classes/Utility/StringUtility.php @@ -20,6 +20,38 @@ namespace TYPO3\CMS\Core\Utility; */ class StringUtility { + /** + * Casts applicable types (string, bool, finite numeric) to string. + * + * Any other type will be replaced by the `$default` value. + * + * @param mixed $value + */ + public static function cast($value, ?string $default = null): ?string + { + if (is_string($value)) { + return $value; + } + + if (is_bool($value) || (is_numeric($value) && is_finite($value))) { + return (string)$value; + } + + return $default; + } + + /** + * Keeps only string types (filters out non-strings). + * + * Any other non-string type will be replaced by the `$default` value. + * + * @param mixed $value + */ + public static function filter($value, ?string $default = null): ?string + { + return is_string($value) ? $value : $default; + } + /** * Returns TRUE if $haystack begins with $needle. * The input string is not trimmed before and search is done case sensitive. diff --git a/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php b/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php index b8a464245310..381f439b5582 100644 --- a/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php +++ b/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php @@ -25,6 +25,129 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase; */ class StringUtilityTest extends UnitTestCase { + /** + * @return \Generator<string, array{0: mixed}> + */ + public static function stringCastableValuesDataProvider(): \Generator + { + yield 'empty string' => ['']; + yield 'string' => ['value']; + yield 'int' => [1]; + yield 'float' => [1.2345]; + yield 'bool' => [true]; + } + + /** + * @param mixed $value + * + * @test + * @dataProvider stringCastableValuesDataProvider + */ + public function castWithStringCastableReturnsValueCastToString($value): void + { + $expected = (string)$value; + + self::assertSame($expected, StringUtility::cast($value, 'default')); + } + + /** + * @return \Generator<string, array{0: mixed}> + */ + public static function nonStringCastableValuesDataProvider(): \Generator + { + yield 'array' => [['1']]; + yield 'null' => [null]; + yield 'object' => [new \stdClass()]; + yield 'closure' => [static fn (): string => 'fn']; + // PHP interprets it as `lim(x→0) log(x) = -∞` + yield 'infinite' => [log(0)]; + // acos only supports values in range [-1; +1] + yield 'NaN' => [acos(2)]; + } + + /** + * @param mixed $value + * + * @test + * @dataProvider nonStringCastableValuesDataProvider + */ + public function castWithWithNonStringCastableReturnsDefault($value): void + { + $default = 'default'; + + self::assertSame($default, StringUtility::cast($value, $default)); + } + + /** + * @param mixed $value + * + * @test + * @dataProvider nonStringCastableValuesDataProvider + */ + public function castWithWithNonStringCastableAndNoDefaultProvidedReturnsNull($value): void + { + self::assertNull(StringUtility::cast($value)); + } + + /** + * @return \Generator<string, array{0: mixed}> + */ + public static function nonStringValueToFilterDataProvider(): \Generator + { + yield 'int' => [1]; + yield 'float' => [1.2345]; + yield 'bool' => [true]; + yield 'array' => [['1']]; + yield 'null' => [null]; + yield 'object' => [new \stdClass()]; + yield 'closure' => [static fn (): string => 'fn']; + // PHP interprets it as `lim(x→0) log(x) = -∞` + yield 'infinite' => [log(0)]; + // acos only supports values in range [-1; +1] + yield 'NaN' => [acos(2)]; + } + + /** + * @param mixed $value + * + * @test + * @dataProvider nonStringValueToFilterDataProvider + */ + public function filterForNonStringValueAndDefaultProvidedReturnsDefault($value): void + { + $default = 'default'; + + self::assertSame($default, StringUtility::filter($value, $default)); + } + + /** + * @param mixed $value + * + * @test + * @dataProvider nonStringValueToFilterDataProvider + */ + public function filterForNonStringValueAndNoDefaultProvidedReturnsNull($value): void + { + self::assertNull(StringUtility::filter($value)); + } + + /** + * @return \Generator<string, array{0: string}> + */ + public static function stringValueToFilterDataProvider(): \Generator + { + yield 'empty string' => ['']; + yield 'non-empty string' => ['value']; + } + /** + * @test + * @dataProvider stringValueToFilterDataProvider + */ + public function filterForStringValuesReturnsProvidedValue(string $value): void + { + self::assertSame($value, StringUtility::filter($value, 'some default')); + } + /** * @test */ -- GitLab