diff --git a/examples/Resources/Private/Singles/Variables.html b/examples/Resources/Private/Singles/Variables.html
index fc56707d7..fa99ab292 100644
--- a/examples/Resources/Private/Singles/Variables.html
+++ b/examples/Resources/Private/Singles/Variables.html
@@ -40,6 +40,12 @@
}
}"/>
+
+
+ First argument
+ Second argument
+
+
@@ -49,3 +55,8 @@
Received $array.xyz.foobar with value {array.xyz.foobar}
Received $myVariable with value {myVariable}
+
+
+Input variable "arg1" was: {arg1}
+Input variable "arg2" was: {arg2}
+
diff --git a/src/Core/ViewHelper/ViewHelperVariableContainer.php b/src/Core/ViewHelper/ViewHelperVariableContainer.php
index 5e60dc973..d197e31f4 100644
--- a/src/Core/ViewHelper/ViewHelperVariableContainer.php
+++ b/src/Core/ViewHelper/ViewHelperVariableContainer.php
@@ -6,6 +6,7 @@
* See LICENSE.txt that was shipped with this package.
*/
+use TYPO3Fluid\Fluid\Core\Variables\VariableProviderInterface;
use TYPO3Fluid\Fluid\View\ViewInterface;
/**
@@ -29,6 +30,53 @@ class ViewHelperVariableContainer
*/
protected $view;
+ /**
+ * Push a new delegate variable container to a stack. Done before you start
+ * sub-rendering with a ViewHelper that requires a special container for
+ * variables which is capable of being used in multidepth stacks without
+ * overriding the last instance or last set of variables.
+ *
+ * @param string $viewHelperClassName
+ * @param VariableProviderInterface $variableProvider
+ */
+ public function pushDelegateVariableContainer($viewHelperClassName, VariableProviderInterface $variableProvider)
+ {
+ if (!isset($this->objects[$viewHelperClassName]['delegateVariableProviderStack'])) {
+ $this->objects[$viewHelperClassName]['delegateVariableProviderStack'] = [];
+ }
+ $this->objects[$viewHelperClassName]['delegateVariableProviderStack'][] = $variableProvider;
+ }
+
+ /**
+ * Get the topmost delegate variable container that was previously pushed
+ * onto the stack by pushDelegateVariableContainer().
+ *
+ * @param string $viewHelperClassName
+ * @return VariableProviderInterface|null
+ */
+ public function getTopmostDelegateVariableContainer($viewHelperClassName)
+ {
+ if (!isset($this->objects[$viewHelperClassName]['delegateVariableProviderStack'])) {
+ return null;
+ }
+ return end($this->objects[$viewHelperClassName]['delegateVariableProviderStack']);
+ }
+
+ /**
+ * Return and remove the topmost delegate variable container. Done after
+ * completion of a sub-rendering that uses a delegate variable provider.
+ *
+ * @param string $viewHelperClassName
+ * @return VariableProviderInterface|null
+ */
+ public function popDelegateVariableContainer($viewHelperClassName)
+ {
+ if (isset($this->objects[$viewHelperClassName]['delegateVariableProviderStack'])) {
+ return array_pop($this->objects[$viewHelperClassName]['delegateVariableProviderStack']);
+ }
+ return null;
+ }
+
/**
* Add a variable to the Variable Container. Make sure that $viewHelperName is ALWAYS set
* to your fully qualified ViewHelper Class Name
diff --git a/src/ViewHelpers/RenderViewHelper.php b/src/ViewHelpers/RenderViewHelper.php
index ebcab45f4..e94bfd11b 100644
--- a/src/ViewHelpers/RenderViewHelper.php
+++ b/src/ViewHelpers/RenderViewHelper.php
@@ -121,7 +121,21 @@ public static function renderStatic(array $arguments, \Closure $renderChildrenCl
$delegate = $arguments['delegate'];
/** @var RenderableInterface $renderable */
$renderable = $arguments['renderable'];
+
+
+ // Prepare a delegate variable provider that will be possible to extract after rendering the child closure.
+ // Any variable defined therein gets used as argument and overrides any argument of the same name.
+ // Note: not using late static binding here is a conscious decision: if late static binding had been used
+ // then f:variable would not be able to reference this ViewHelper class' stack variable correctly.
+ $viewHelperVariableContainer = $renderingContext->getViewHelperVariableContainer();
+ $collector = $renderingContext->getVariableProvider()->getScopeCopy($variables);
+
+ $viewHelperVariableContainer->pushDelegateVariableContainer(self::class, $collector);
+
$tagContent = $renderChildrenClosure();
+
+ $variables = $viewHelperVariableContainer->popDelegateVariableContainer(self::class)->getAll();
+
if ($arguments['contentAs']) {
$variables[$arguments['contentAs']] = $tagContent;
}
diff --git a/src/ViewHelpers/VariableViewHelper.php b/src/ViewHelpers/VariableViewHelper.php
index 5f0debc41..429aa75a7 100644
--- a/src/ViewHelpers/VariableViewHelper.php
+++ b/src/ViewHelpers/VariableViewHelper.php
@@ -57,6 +57,9 @@ public static function renderStatic(
) {
$value = $renderChildrenClosure();
$renderingContext->getVariableProvider()->add($arguments['name'], $value);
+ if ($delegateVariableProvider = $renderingContext->getViewHelperVariableContainer()->getTopmostDelegateVariableContainer(RenderViewHelper::class)) {
+ $delegateVariableProvider->add($arguments['name'], $value);
+ }
}
}
diff --git a/tests/Functional/ExamplesTest.php b/tests/Functional/ExamplesTest.php
index e0659f1c2..b3a9b8d54 100644
--- a/tests/Functional/ExamplesTest.php
+++ b/tests/Functional/ExamplesTest.php
@@ -222,7 +222,9 @@ public function getExampleScriptTestValues()
'Received $array.printf with formatted string Formatted string, value: formatted',
'Received $array.baz with value 42',
'Received $array.xyz.foobar with value Escaped sub-string',
- 'Received $myVariable with value Nice string'
+ 'Received $myVariable with value Nice string',
+ 'Input variable "arg1" was: First argument',
+ 'Input variable "arg2" was: Second argument',
]
],
'example_variableprovider.php' => [