diff --git a/examples/Resources/Private/Singles/Variables.html b/examples/Resources/Private/Singles/Variables.html
index fc56707d7..829b07bf0 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 argument "arg1" was: {arg1}
+Input argument "arg2" was: {arg2}
+
diff --git a/src/Core/ViewHelper/ViewHelperVariableContainer.php b/src/Core/ViewHelper/ViewHelperVariableContainer.php
index 5e60dc973..6a2d1493c 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,69 @@ class ViewHelperVariableContainer
*/
protected $view;
+ /**
+ * @var VariableProviderInterface[]
+ */
+ protected $delegates = [];
+
+ /**
+ * Push a new delegate variable container to a stack.
+ *
+ * If a ViewHelper requires a storage to collect variables which, for
+ * example, are filled by evaluating the child (tag content) closure,
+ * this method can be used to add a special delegate variable container
+ * stored in a stack. Once the variables you need to collect have been
+ * collected, calling `popDelegateVariableProvider` removes the delegate
+ * from the stack.
+ *
+ * The point of a stack is to avoid resetting a storage every time a
+ * ViewHelper is rendered. In the case of `f:render` it means one storage
+ * is created and filled for every one call to the ViewHelper.
+ *
+ * It is VITAL that you also "pop" any delegate you push to this stack!
+ *
+ * @param VariableProviderInterface $variableProvider
+ */
+ public function pushDelegateVariableProvider(VariableProviderInterface $variableProvider)
+ {
+ $this->delegates[] = $variableProvider;
+ }
+
+ /**
+ * Get the topmost delegate variable container that was previously pushed
+ * onto the stack by pushDelegateVariableContainer(). This method returns
+ * a reference to the storage that was last added to the stack without
+ * removing the variable provider from the stack.
+ *
+ * Is used in ViewHelpers that assign variables in variable providers in
+ * the stack - as a means to get the variable storage used by the "closest
+ * parent", e.g. when called in `f:argument` used inside `f:render`, will
+ * read the delegate variable provider inserted by that parent `f:render`.
+ *
+ * @return VariableProviderInterface|null
+ */
+ public function getTopmostDelegateVariableProvider()
+ {
+ return end($this->delegates) ?: null;
+ }
+
+ /**
+ * Return and REMOVE the topmost delegate variable provider. This method
+ * must be called after you finish sub-rendering with a delegated variable
+ * provider that was added with `pushDelegateVariableProvider`. Calling
+ * the method removes the delegate and returns the stack to the previous
+ * state it was in.
+ *
+ * To avoid removing from the stack, use `getTopmostDelegateVariableProvider`.
+ *
+ * @param string $viewHelperClassName
+ * @return VariableProviderInterface|null
+ */
+ public function popDelegateVariableProvider()
+ {
+ return array_pop($this->delegates);
+ }
+
/**
* 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/ArgumentViewHelper.php b/src/ViewHelpers/ArgumentViewHelper.php
new file mode 100644
index 000000000..f286d1fee
--- /dev/null
+++ b/src/ViewHelpers/ArgumentViewHelper.php
@@ -0,0 +1,68 @@
+
+ * Value1
+ * Value2
+ *
+ *
+ * Which is the equivalent of:
+ *
+ *
+ *
+ * But has the benefit that writing ViewHelper expressions or
+ * other more complex syntax becomes much easier because you
+ * can use tag syntax (tag content becomes argument value).
+ *
+ * @api
+ */
+class ArgumentViewHelper extends AbstractViewHelper
+{
+ use CompileWithContentArgumentAndRenderStatic;
+
+ /**
+ * @return void
+ */
+ public function initializeArguments()
+ {
+ $this->registerArgument('value', 'mixed', 'Value to assign. If not in arguments then taken from tag content');
+ $this->registerArgument('name', 'string', 'Name of variable to create', true);
+ }
+
+ /**
+ * @param array $arguments
+ * @param \Closure $renderChildrenClosure
+ * @param RenderingContextInterface $renderingContext
+ * @return null
+ */
+ public static function renderStatic(
+ array $arguments,
+ \Closure $renderChildrenClosure,
+ RenderingContextInterface $renderingContext
+ ) {
+ $delegateVariableProvider = $renderingContext->getViewHelperVariableContainer()->getTopmostDelegateVariableProvider();
+ if ($delegateVariableProvider) {
+ $delegateVariableProvider->add($arguments['name'], $renderChildrenClosure());
+ }
+ }
+
+}
diff --git a/src/ViewHelpers/RenderViewHelper.php b/src/ViewHelpers/RenderViewHelper.php
index ebcab45f4..18d2af408 100644
--- a/src/ViewHelpers/RenderViewHelper.php
+++ b/src/ViewHelpers/RenderViewHelper.php
@@ -87,7 +87,7 @@
class RenderViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
-
+
/**
* @var boolean
*/
@@ -121,7 +121,20 @@ 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->pushDelegateVariableProvider($collector);
+
$tagContent = $renderChildrenClosure();
+
+ $variables = $viewHelperVariableContainer->popDelegateVariableProvider()->getAll();
+
if ($arguments['contentAs']) {
$variables[$arguments['contentAs']] = $tagContent;
}
diff --git a/tests/Functional/ExamplesTest.php b/tests/Functional/ExamplesTest.php
index e0659f1c2..c87a3f3d0 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 argument "arg1" was: First argument',
+ 'Input argument "arg2" was: Second argument',
]
],
'example_variableprovider.php' => [