-
Notifications
You must be signed in to change notification settings - Fork 91
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FEATURE] Proper support of local variables in ViewHelpers (#828)
In its current implementation, the `f:alias` ViewHelper has side effects: It adds variables to the global variable container, which are not restored properly after the ViewHelper call. If a variable name is used as alias which already exists, that variable is unset after the ViewHelper call. To resolve this, a new `ScopedVariableProvider` is introduced to handle local variables inside ViewHelper calls properly. After the local scope, global variables are restored to their original value in case they existed. At the same time, other modifications to the variable provider inside the `f:alias` call, such as a call to `f:variable`, should still be persistent after the ViewHelper call. This ensures that the change is non-breaking for the vast majority of usages. The only situation where this could be breaking is when you relied on the arguably broken behavior that `f:alias` unsets the variables afterwards. This is also the reason why we can't simple store and reset the whole variable container. This would be a breaking change and would also be a diversion from other ViewHelpers, such as `f:if`, where `f:variable` also leaks out. To change this behavior for select ViewHelpers would be inconsistent and hard to understand for users of Fluid. before: ```xml <f:variable name="myVariable" value="initialValue" /> <f:alias map="{myVariable: \'myValue\'}">...</f:alias> {myVariable} <!-- outputs empty string --> ``` after: ```xml <f:variable name="myVariable" value="initialValue" /> <f:alias map="{myVariable: \'myValue\'}">...</f:alias> {myVariable} <!-- outputs "initialValue" --> ``` This behavior also applies to other ViewHelpers that provide variables to their child nodes, such as `f:for`, `f:render`, `f:groupedFor`, `f:cycle`. These will be modified accordingly in separate PRs. Resolves #666
- Loading branch information
Showing
5 changed files
with
476 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file belongs to the package "TYPO3 Fluid". | ||
* See LICENSE.txt that was shipped with this package. | ||
*/ | ||
|
||
namespace TYPO3Fluid\Fluid\Core\Variables; | ||
|
||
/** | ||
* Variable provider to be used in cases where a specific | ||
* set of variables are only valid in a local context, while | ||
* another set of global variables should remain valid after | ||
* that context. This is used for example for AliasViewHelper | ||
* or ForViewHelper to differentiate the variables provided | ||
* for child elements from global variables that should still | ||
* be valid afterwards. | ||
*/ | ||
final class ScopedVariableProvider extends StandardVariableProvider implements VariableProviderInterface | ||
{ | ||
public function __construct( | ||
protected VariableProviderInterface $globalVariables, | ||
protected VariableProviderInterface $localVariables, | ||
) {} | ||
|
||
public function getGlobalVariableProvider(): VariableProviderInterface | ||
{ | ||
return $this->globalVariables; | ||
} | ||
|
||
public function getLocalVariableProvider(): VariableProviderInterface | ||
{ | ||
return $this->localVariables; | ||
} | ||
|
||
/** | ||
* @param string $identifier Identifier of the variable to add | ||
* @param mixed $value The variable's value | ||
*/ | ||
public function add($identifier, $value): void | ||
{ | ||
$this->globalVariables->add($identifier, $value); | ||
} | ||
|
||
/** | ||
* @param string $identifier The identifier to remove | ||
*/ | ||
public function remove($identifier): void | ||
{ | ||
$this->globalVariables->remove($identifier); | ||
$this->localVariables->remove($identifier); | ||
} | ||
|
||
/** | ||
* @param mixed $source | ||
*/ | ||
public function setSource($source): void | ||
{ | ||
$this->globalVariables->setSource($source); | ||
} | ||
|
||
public function getSource(): array | ||
{ | ||
return $this->getAll(); | ||
} | ||
|
||
public function getAll(): array | ||
{ | ||
return array_merge( | ||
$this->globalVariables->getAll(), | ||
$this->localVariables->getAll(), | ||
); | ||
} | ||
|
||
/** | ||
* @param string $identifier | ||
*/ | ||
public function exists($identifier): bool | ||
{ | ||
return $this->localVariables->exists($identifier) || $this->globalVariables->exists($identifier); | ||
} | ||
|
||
/** | ||
* @param string $identifier | ||
*/ | ||
public function get($identifier): mixed | ||
{ | ||
return $this->localVariables->get($identifier) ?? $this->globalVariables->get($identifier); | ||
} | ||
|
||
/** | ||
* @param string $path | ||
*/ | ||
public function getByPath($path): mixed | ||
{ | ||
return $this->localVariables->getByPath($path) ?? $this->globalVariables->getByPath($path); | ||
} | ||
|
||
public function getAllIdentifiers(): array | ||
{ | ||
return array_unique(array_merge( | ||
$this->globalVariables->getAllIdentifiers(), | ||
$this->localVariables->getAllIdentifiers(), | ||
)); | ||
} | ||
|
||
/** | ||
* @param array|\ArrayAccess $variables | ||
*/ | ||
public function getScopeCopy($variables): ScopedVariableProvider | ||
{ | ||
return new ScopedVariableProvider( | ||
$this->globalVariables->getScopeCopy($variables), | ||
clone $this->localVariables | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.