Skip to content

Commit

Permalink
[FEATURE] Support Vendor.Package as namespace name
Browse files Browse the repository at this point in the history
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
<TYPO3Fluid.Fluid:render partial="Partial" />
```

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 TemplatePaths override).
  • Loading branch information
NamelessCoder committed Aug 27, 2017
1 parent f583bad commit ea20c2f
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 18 deletions.
57 changes: 40 additions & 17 deletions src/Core/ViewHelper/ViewHelperResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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];
}
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Functional/ExamplesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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: <f:if notregistered="1" />',
'Parser error: The ViewHelper "<f:invalid>" 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: <f:invalid />',
'Fluid checked for "TYPO3Fluid\Fluid\ViewHelpers\InvalidViewHelper, TYPO3Fluid\Fluid\ViewHelpers\Invalid" but none of those classes exist. Offending code: <f:invalid />',
'Invalid expression: Invalid target conversion type "invalidtype" specified in casting expression "{foobar as invalidtype}".',
]
]
Expand Down
21 changes: 21 additions & 0 deletions tests/Unit/Core/ViewHelper/ViewHelperResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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
*/
Expand Down Expand Up @@ -255,6 +275,7 @@ public function getIsNamespaceValidTestValues()
[['foo' => ['test']], 'foo', true],
[['foo' => ['test']], 'foobar', false],
[['foo*' => null], 'foo', false],
[[], 'Vendor.Namespace', true],
];
}

Expand Down

0 comments on commit ea20c2f

Please sign in to comment.