diff --git a/src/ViewHelpers/Format/CaseViewHelper.php b/src/ViewHelpers/Format/CaseViewHelper.php new file mode 100644 index 000000000..2dc697caa --- /dev/null +++ b/src/ViewHelpers/Format/CaseViewHelper.php @@ -0,0 +1,151 @@ +Some Text with miXed case + * + * Output:: + * + * SOME TEXT WITH MIXED CASE + * + * Example with given mode + * ----------------------- + * + * :: + * + * someString + * + * Output:: + * + * SomeString + */ +final class CaseViewHelper extends AbstractViewHelper +{ + use CompileWithRenderStatic; + + /** + * Directs the input string being converted to "lowercase" + */ + private const CASE_LOWER = 'lower'; + + /** + * Directs the input string being converted to "UPPERCASE" + */ + private const CASE_UPPER = 'upper'; + + /** + * Directs the input string being converted to "Capital case" + */ + private const CASE_CAPITAL = 'capital'; + + /** + * Directs the input string being converted to "unCapital case" + */ + private const CASE_UNCAPITAL = 'uncapital'; + + /** + * Directs the input string being converted to "Capital Case For Each Word" + */ + private const CASE_CAPITAL_WORDS = 'capitalWords'; + + /** + * Output is escaped already. We must not escape children, to avoid double encoding. + * + * @var bool + */ + protected $escapeChildren = false; + + public function initializeArguments(): void + { + $this->registerArgument('value', 'string', 'The input value. If not given, the evaluated child nodes will be used.', false); + $this->registerArgument('mode', 'string', 'The case to apply, must be one of this\' CASE_* constants. Defaults to uppercase application.', false, self::CASE_UPPER); + } + + /** + * Changes the case of the input string + * @throws Exception + */ + public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext): string + { + $value = $arguments['value']; + $mode = $arguments['mode']; + + if ($value === null) { + $value = (string)$renderChildrenClosure(); + } + + switch ($mode) { + case self::CASE_LOWER: + $output = mb_strtolower($value, 'utf-8'); + break; + case self::CASE_UPPER: + $output = mb_strtoupper($value, 'utf-8'); + break; + case self::CASE_CAPITAL: + $firstChar = mb_substr($value, 0, 1, 'utf-8'); + $firstChar = mb_strtoupper($firstChar, 'utf-8'); + $remainder = mb_substr($value, 1, null, 'utf-8'); + $output = $firstChar . $remainder; + break; + case self::CASE_UNCAPITAL: + $firstChar = mb_substr($value, 0, 1, 'utf-8'); + $firstChar = mb_strtolower($firstChar, 'utf-8'); + $remainder = mb_substr($value, 1, null, 'utf-8'); + $output = $firstChar . $remainder; + break; + case self::CASE_CAPITAL_WORDS: + $output = mb_convert_case($value, MB_CASE_TITLE, 'utf-8'); + break; + default: + throw new Exception('The case mode "' . $mode . '" supplied to Fluid\'s format.case ViewHelper is not supported.', 1358349150); + } + + return $output; + } +} diff --git a/tests/Functional/ViewHelpers/Format/CaseViewHelperTest.php b/tests/Functional/ViewHelpers/Format/CaseViewHelperTest.php new file mode 100644 index 000000000..e9b1a0319 --- /dev/null +++ b/tests/Functional/ViewHelpers/Format/CaseViewHelperTest.php @@ -0,0 +1,96 @@ + [ + '', + '', + ], + 'value from child, uppercase default' => [ + 'foob4r', + 'FOOB4R', + ], + 'simple value' => [ + '', + 'FOO', + ], + 'mode lower' => [ + '', + 'foob4r', + ], + 'mode upper' => [ + '', + 'FOOB4R', + ], + 'mode capital' => [ + '', + 'Foo bar', + ], + 'mode uncapital' => [ + '', + 'fOO Bar', + ], + 'special chars 1' => [ + '', + 'SMØRREBRØD', + ], + 'special chars 2' => [ + '', + 'Smørrebrød', + ], + 'special chars 3' => [ + '', + 'RÖMTÖMTÖMTÖM', + ], + 'special chars 4' => [ + '', + 'ἝΛΛΆΣ Α Ω', + ], + ]; + } + + /** + * @test + * @dataProvider renderConvertsAValueDataProvider + */ + public function renderConvertsAValue(string $template, string $expected): void + { + $view = new TemplateView(); + $view->getRenderingContext()->setCache(self::$cache); + $view->getRenderingContext()->getTemplatePaths()->setTemplateSource($template); + self::assertSame($expected, $view->render()); + + $view = new TemplateView(); + $view->getRenderingContext()->setCache(self::$cache); + $view->getRenderingContext()->getTemplatePaths()->setTemplateSource($template); + self::assertSame($expected, $view->render()); + } + + /** + * @test + */ + public function viewHelperThrowsExceptionIfIncorrectModeIsGiven(): void + { + $this->expectException(Exception::class); + $this->expectExceptionCode(1358349150); + $view = new TemplateView(); + $view->getRenderingContext()->getTemplatePaths()->setTemplateSource(''); + $view->render(); + } +}