From 4bdd92ac788863de106492101a98e16a925d95b8 Mon Sep 17 00:00:00 2001 From: Claus Due Date: Sun, 30 Jul 2017 17:11:34 +0200 Subject: [PATCH 1/2] [FEATURE] Support Vendor.Package as namespace name Using this format (or simply Package if no vendor name is used for the package) makes ViewHelperResolver look in that namespace + ViewHelpers, for example: ```xml ``` Will internally resolve to: ```php \TYPO3Fluid\Fluid\ViewHelpers\RenderViewHelper ``` Without the need to register the namespace. Note that the example uses the native Fluid namespace to demonstrate, although this namespace is always present (unless explicitly removed by a an override). --- src/Core/ViewHelper/ViewHelperResolver.php | 57 +++++++++++++------ tests/Functional/ExamplesTest.php | 2 +- .../ViewHelper/ViewHelperResolverTest.php | 21 +++++++ 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/src/Core/ViewHelper/ViewHelperResolver.php b/src/Core/ViewHelper/ViewHelperResolver.php index 0ac858a89..85cefe200 100644 --- a/src/Core/ViewHelper/ViewHelperResolver.php +++ b/src/Core/ViewHelper/ViewHelperResolver.php @@ -173,6 +173,10 @@ public function setNamespaces(array $namespaces) */ public function isNamespaceValid($namespaceIdentifier) { + if (strpos($namespaceIdentifier, '.')) { + return true; + } + if (!array_key_exists($namespaceIdentifier, $this->namespaces)) { return false; } @@ -245,18 +249,8 @@ public function isNamespaceIgnored($namespaceIdentifier) public function resolveViewHelperClassName($namespaceIdentifier, $methodIdentifier) { if (!isset($this->resolvedViewHelperClassNames[$namespaceIdentifier][$methodIdentifier])) { - $resolvedViewHelperClassName = $this->resolveViewHelperName($namespaceIdentifier, $methodIdentifier); - $actualViewHelperClassName = implode('\\', array_map('ucfirst', explode('.', $resolvedViewHelperClassName))); - if (false === class_exists($actualViewHelperClassName) || $actualViewHelperClassName === false) { - throw new ParserException(sprintf( - 'The ViewHelper "<%s:%s>" could not be resolved.' . chr(10) . - 'Based on your spelling, the system would load the class "%s", however this class does not exist.', - $namespaceIdentifier, - $methodIdentifier, - $resolvedViewHelperClassName - ), 1407060572); - } - $this->resolvedViewHelperClassNames[$namespaceIdentifier][$methodIdentifier] = $actualViewHelperClassName; + $this->resolvedViewHelperClassNames[$namespaceIdentifier][$methodIdentifier] = + $this->resolveViewHelperName($namespaceIdentifier, $methodIdentifier); } return $this->resolvedViewHelperClassNames[$namespaceIdentifier][$methodIdentifier]; } @@ -320,13 +314,42 @@ protected function resolveViewHelperName($namespaceIdentifier, $methodIdentifier } else { $className = ucfirst($explodedViewHelperName[0]); } - $className .= 'ViewHelper'; + $classNames = [ + $className . 'ViewHelper', + $className + ]; + + if (array_key_exists($namespaceIdentifier, $this->namespaces)) { + $namespaces = (array) $this->namespaces[$namespaceIdentifier]; + } else { + $namespacePrefix = $this->namespaces[$namespaceIdentifier] = str_replace('.', '\\', $namespaceIdentifier); + $namespaces = [ + $namespacePrefix . '\\ViewHelpers', + $namespacePrefix + ]; + } - $namespaces = (array) $this->namespaces[$namespaceIdentifier]; + $checked = []; + foreach (array_reverse($namespaces) as $namespace) { + $namespace = rtrim($namespace, '\\'); + foreach ($classNames as $className) { + $name = $namespace . '\\' . $className; + if (class_exists($name) && is_a($name, ViewHelperInterface::class, true)) { + return $name; + } + $checked[] = $name; + } + } - do { - $name = rtrim(array_pop($namespaces), '\\') . '\\' . $className; - } while (!class_exists($name) && count($namespaces)); + throw new ParserException( + sprintf( + 'The ViewHelper "<%s:%s>" could not be resolved. Fluid checked for "%s" but none of those classes exist.', + $namespaceIdentifier, + $methodIdentifier, + implode(', ', $checked) + ), + 1407060572 + ); return $name; } diff --git a/tests/Functional/ExamplesTest.php b/tests/Functional/ExamplesTest.php index 82b508c7b..33af29f9e 100644 --- a/tests/Functional/ExamplesTest.php +++ b/tests/Functional/ExamplesTest.php @@ -251,7 +251,7 @@ public function getExampleScriptTestValues() 'Section rendering error: Section "DoesNotExist" does not exist. Section rendering is mandatory; "optional" is false.', 'ViewHelper error: Undeclared arguments passed to ViewHelper TYPO3Fluid\Fluid\ViewHelpers\IfViewHelper: notregistered. Valid arguments are: then, else, condition - Offending code: ', 'Parser error: The ViewHelper "" could not be resolved.', - 'Based on your spelling, the system would load the class "TYPO3Fluid\Fluid\ViewHelpers\InvalidViewHelper", however this class does not exist. Offending code: ', + 'Fluid checked for "TYPO3Fluid\Fluid\ViewHelpers\InvalidViewHelper, TYPO3Fluid\Fluid\ViewHelpers\Invalid" but none of those classes exist. Offending code: ', 'Invalid expression: Invalid target conversion type "invalidtype" specified in casting expression "{foobar as invalidtype}".', ] ] diff --git a/tests/Unit/Core/ViewHelper/ViewHelperResolverTest.php b/tests/Unit/Core/ViewHelper/ViewHelperResolverTest.php index 48ca8aa06..176502d87 100644 --- a/tests/Unit/Core/ViewHelper/ViewHelperResolverTest.php +++ b/tests/Unit/Core/ViewHelper/ViewHelperResolverTest.php @@ -92,6 +92,16 @@ public function testResolveViewHelperClassNameSupportsMultipleNamespaces() $this->assertEquals('TYPO3Fluid\\Fluid\\ViewHelpers\\RenderViewHelper', $result); } + /** + * @test + */ + public function testResolveViewHelperClassNameSupportsDirectDottedNamespace() + { + $resolver = $this->getAccessibleMock(ViewHelperResolver::class, ['dummy']); + $result = $resolver->_call('resolveViewHelperName', 'TYPO3Fluid.Fluid', 'render'); + $this->assertEquals('TYPO3Fluid\\Fluid\\ViewHelpers\\RenderViewHelper', $result); + } + /** * @test */ @@ -103,6 +113,16 @@ public function testResolveViewHelperClassNameTrimsBackslashSuffixFromNamespace( $this->assertEquals('TYPO3Fluid\\Fluid\\ViewHelpers\\RenderViewHelper', $result); } + /** + * @test + */ + public function testResolveViewHelperClassNameSupportsVendorAndPackageNameAsNamespace() + { + $resolver = $this->getAccessibleMock(ViewHelperResolver::class, ['dummy']); + $result = $resolver->_call('resolveViewHelperName', 'TYPO3Fluid.Fluid', 'render'); + $this->assertEquals('TYPO3Fluid\\Fluid\\ViewHelpers\\RenderViewHelper', $result); + } + /** * @test */ @@ -255,6 +275,7 @@ public function getIsNamespaceValidTestValues() [['foo' => ['test']], 'foo', true], [['foo' => ['test']], 'foobar', false], [['foo*' => null], 'foo', false], + [[], 'Vendor.Namespace', true], ]; } From 3294e19ce34af4e3253292cccb0ebeb1a6b8ac4a Mon Sep 17 00:00:00 2001 From: Mathias Brodala Date: Wed, 11 Jul 2018 17:10:05 +0200 Subject: [PATCH 2/2] [TASK] Unify array commas --- src/Core/ViewHelper/ViewHelperResolver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/ViewHelper/ViewHelperResolver.php b/src/Core/ViewHelper/ViewHelperResolver.php index 85cefe200..a2f4280d0 100644 --- a/src/Core/ViewHelper/ViewHelperResolver.php +++ b/src/Core/ViewHelper/ViewHelperResolver.php @@ -316,7 +316,7 @@ protected function resolveViewHelperName($namespaceIdentifier, $methodIdentifier } $classNames = [ $className . 'ViewHelper', - $className + $className, ]; if (array_key_exists($namespaceIdentifier, $this->namespaces)) { @@ -325,7 +325,7 @@ protected function resolveViewHelperName($namespaceIdentifier, $methodIdentifier $namespacePrefix = $this->namespaces[$namespaceIdentifier] = str_replace('.', '\\', $namespaceIdentifier); $namespaces = [ $namespacePrefix . '\\ViewHelpers', - $namespacePrefix + $namespacePrefix, ]; }