Your IP : 216.73.217.13


Current Path : /var/www/surf/TYPO3/vendor/typo3fluid/fluid/src/Core/ViewHelper/
Upload File :
Current File : /var/www/surf/TYPO3/vendor/typo3fluid/fluid/src/Core/ViewHelper/AbstractConditionViewHelper.php

<?php

/*
 * This file belongs to the package "TYPO3 Fluid".
 * See LICENSE.txt that was shipped with this package.
 */

namespace TYPO3Fluid\Fluid\Core\ViewHelper;

use TYPO3Fluid\Fluid\Core\Compiler\TemplateCompiler;
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\NodeInterface;
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;

/**
 * This view helper is an abstract ViewHelper which implements an if/else condition.
 *
 * = Usage =
 *
 * To create a custom Condition ViewHelper, you need to subclass this class, and
 * implement your own render() method. Inside there, you should call $this->renderThenChild()
 * if the condition evaluated to TRUE, and $this->renderElseChild() if the condition evaluated
 * to FALSE.
 *
 * Every Condition ViewHelper has a "then" and "else" argument, so it can be used like:
 * <[aConditionViewHelperName] .... then="condition true" else="condition false" />,
 * or as well use the "then" and "else" child nodes.
 *
 * @see TYPO3Fluid\Fluid\ViewHelpers\IfViewHelper for a more detailed explanation and a simple usage example.
 * Make sure to NOT OVERRIDE the constructor.
 *
 * @api
 */
abstract class AbstractConditionViewHelper extends AbstractViewHelper
{
    /**
     * @var bool
     */
    protected $escapeOutput = false;

    /**
     * Initializes the "then" and "else" arguments
     */
    public function initializeArguments()
    {
        $this->registerArgument('then', 'mixed', 'Value to be returned if the condition if met.', false, null, true);
        $this->registerArgument('else', 'mixed', 'Value to be returned if the condition if not met.', false, null, true);
    }

    /**
     * Renders <f:then> child if $condition is true, otherwise renders <f:else> child.
     * Method which only gets called if the template is not compiled. For static calling,
     * the then/else nodes are converted to closures and condition evaluation closures.
     *
     * @return string the rendered string
     * @api
     */
    public function render()
    {
        if (static::verdict($this->arguments, $this->renderingContext)) {
            return $this->renderThenChild();
        }
        return $this->renderElseChild();
    }

    /**
     * @param array<string, mixed> $arguments
     * @param \Closure $renderChildrenClosure
     * @param RenderingContextInterface $renderingContext
     * @return mixed
     */
    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
    {
        $mainConditionVerdict = static::verdict($arguments, $renderingContext);
        if ($mainConditionVerdict) {
            // The condition argument evaluated to true. Return the "then" argument as string,
            // execute f:then child or general body closure, or return empty string.
            if (isset($arguments['then'])) {
                return $arguments['then'];
            }
            if (isset($arguments['__then'])) {
                return $arguments['__then']();
            }
            return '';
        }
        if (!empty($arguments['__elseIf'])) {
            // The condition argument evaluated to false. For each "f:else if",
            // evaluate its condition and return its executed body closure if verdict is true.
            foreach ($arguments['__elseIf'] as $elseIf) {
                if ($elseIf['condition']()) {
                    return $elseIf['body']();
                }
            }
        }
        if (isset($arguments['else'])) {
            // The condition argument evaluated to false. If there
            // is an else argument, return as string.
            return $arguments['else'];
        }
        if (!empty($arguments['__else'])) {
            // The condition argument evaluated to false. If there is
            // an f:else body closure, return its executed body.
            return $arguments['__else']();
        }
        return '';
    }

    /**
     * Static method which can be overridden by subclasses. If a subclass
     * requires a different (or faster) decision then this method is the one
     * to override and implement.
     *
     * @param array<string, mixed> $arguments
     * @param RenderingContextInterface $renderingContext
     * @return bool
     */
    public static function verdict(array $arguments, RenderingContextInterface $renderingContext)
    {
        return static::evaluateCondition($arguments);
    }

    /**
     * Static method which can be overridden by subclasses. If a subclass
     * requires a different (or faster) decision then this method is the one
     * to override and implement.
     *
     * Note: method signature does not type-hint that an array is desired,
     * and as such, *appears* to accept any input type. There is no type hint
     * here for legacy reasons - the signature is kept compatible with third
     * party packages which depending on PHP version would error out if this
     * signature was not compatible with that of existing and in-production
     * subclasses that will be using this base class in the future. Let this
     * be a warning if someone considers changing this method signature!
     *
     * @deprecated Deprecated in favor of ClassName::verdict($arguments, renderingContext), will no longer be called in 3.0
     * @param array<string, mixed> $arguments
     * @return bool
     * @api
     */
    protected static function evaluateCondition($arguments = null)
    {
        return isset($arguments['condition']) && (bool)($arguments['condition']);
    }

    /**
     * Returns value of "then" attribute.
     * If then attribute is not set, iterates through child nodes and renders ThenViewHelper.
     * If then attribute is not set and no ThenViewHelper and no ElseViewHelper is found, all child nodes are rendered
     *
     * @return mixed rendered ThenViewHelper or contents of <f:if> if no ThenViewHelper was found
     * @api
     */
    protected function renderThenChild()
    {
        if ($this->hasArgument('then')) {
            return $this->arguments['then'];
        }

        $elseViewHelperEncountered = false;
        foreach ($this->viewHelperNode->getChildNodes() as $childNode) {
            if ($childNode instanceof ViewHelperNode
                && substr($childNode->getViewHelperClassName(), -14) === 'ThenViewHelper') {
                $data = $childNode->evaluate($this->renderingContext);
                return $data;
            }
            if ($childNode instanceof ViewHelperNode
                && substr($childNode->getViewHelperClassName(), -14) === 'ElseViewHelper') {
                $elseViewHelperEncountered = true;
            }
        }

        if ($elseViewHelperEncountered) {
            return '';
        }
        return $this->renderChildren();
    }

    /**
     * Returns value of "else" attribute.
     * If else attribute is not set, iterates through child nodes and renders ElseViewHelper.
     * If else attribute is not set and no ElseViewHelper is found, an empty string will be returned.
     *
     * @return string rendered ElseViewHelper or an empty string if no ThenViewHelper was found
     * @api
     */
    protected function renderElseChild()
    {
        if ($this->hasArgument('else')) {
            return $this->arguments['else'];
        }

        /** @var ViewHelperNode|null $elseNode */
        $elseNode = null;
        foreach ($this->viewHelperNode->getChildNodes() as $childNode) {
            if ($childNode instanceof ViewHelperNode
                && substr($childNode->getViewHelperClassName(), -14) === 'ElseViewHelper') {
                $arguments = $childNode->getArguments();
                if (isset($arguments['if'])) {
                    if ($arguments['if']->evaluate($this->renderingContext)) {
                        return $childNode->evaluate($this->renderingContext);
                    }
                } else {
                    $elseNode = $childNode;
                }
            }
        }

        return $elseNode instanceof ViewHelperNode ? $elseNode->evaluate($this->renderingContext) : '';
    }

    /**
     * Optimized version combining default convert() / compile() into one
     * method: The condition VHs dissect children and looks for then, else
     * and elseif nodes, separates them and puts them into special "__"
     * prefixed arguments. The default renderChildrenClosure is not needed
     * and skipped.
     */
    final public function convert(TemplateCompiler $templateCompiler): array
    {
        $node = $this->viewHelperNode;

        $argumentsVariableName = $templateCompiler->variableName('arguments');
        $argumentInitializationCode = sprintf('%s = [' . chr(10), $argumentsVariableName);

        $accumulatedArgumentInitializationCode = '';
        $arguments = $node->getArguments();
        $argumentDefinitions = $node->getArgumentDefinitions();
        foreach ($argumentDefinitions as $argumentName => $argumentDefinition) {
            if (!array_key_exists($argumentName, $arguments)) {
                // Argument *not* given to VH, use default value
                $defaultValue = $argumentDefinition->getDefaultValue();
                $argumentInitializationCode .= sprintf(
                    '\'%s\' => %s,' . chr(10),
                    $argumentName,
                    is_array($defaultValue) && empty($defaultValue) ? '[]' : var_export($defaultValue, true)
                );
            } elseif ($arguments[$argumentName] instanceof NodeInterface) {
                // Argument *is* given to VH and is a node, resolve
                $converted = $arguments[$argumentName]->convert($templateCompiler);
                $accumulatedArgumentInitializationCode .= $converted['initialization'];
                $argumentInitializationCode .= sprintf(
                    '\'%s\' => %s,' . chr(10),
                    $argumentName,
                    $converted['execution']
                );
            } else {
                // Argument *is* given to VH and is a simple type.
                // @todo: Why is this not a node object as well? See f:if inline syntax tests.
                $argumentInitializationCode .= sprintf(
                    '\'%s\' => %s,' . chr(10),
                    $argumentName,
                    $arguments[$argumentName]
                );
            }
        }

        $thenChildEncountered = false;
        $elseChildEncountered = false;
        $elseIfCounter = 0;
        $elseIfCode = '\'__elseIf\' => [' . chr(10);
        foreach ($node->getChildNodes() as $childNode) {
            if ($childNode instanceof ViewHelperNode) {
                $viewHelperClassName = $childNode->getViewHelperClassName();
                if (!$thenChildEncountered && str_ends_with($viewHelperClassName, 'ThenViewHelper')) {
                    // If there are multiple f:then children, we pick the first one only.
                    // This is in line with the non-compiled behavior.
                    $thenChildEncountered = true;
                    $argumentInitializationCode .= sprintf(
                        '\'__then\' => %s,' . chr(10),
                        $templateCompiler->wrapChildNodesInClosure($childNode)
                    );
                    continue;
                }
                if (str_ends_with($viewHelperClassName, 'ElseViewHelper')) {
                    if (isset($childNode->getArguments()['if'])) {
                        // This "f:else" has the "if" argument, indicating this is a secondary (elseif) condition.
                        // Compile a closure which will evaluate the condition.
                        $elseIfCode .= sprintf(
                            '    %s => [' . chr(10) .
                            '        \'condition\' => %s,' . chr(10) .
                            '        \'body\' => %s' . chr(10) .
                            '    ],' . chr(10),
                            $elseIfCounter,
                            $templateCompiler->wrapViewHelperNodeArgumentEvaluationInClosure($childNode, 'if'),
                            $templateCompiler->wrapChildNodesInClosure($childNode)
                        );
                        $elseIfCounter++;
                        continue;
                    }
                    if (!$elseChildEncountered) {
                        // If there are multiple f:else children, we pick the first one only.
                        // This is in line with the non-compiled behavior.
                        $elseChildEncountered = true;
                        $argumentInitializationCode .= sprintf(
                            '\'__else\' => %s,' . chr(10),
                            $templateCompiler->wrapChildNodesInClosure($childNode)
                        );
                    }
                }
            }
        }
        if (!$thenChildEncountered && $elseIfCounter === 0 && !$elseChildEncountered && !isset($node->getArguments()['then'])) {
            // If there is no then argument, and there are neither "f:then", "f:else" nor "f:else if" children,
            // then the entire body is considered the "then" child.
            $argumentInitializationCode .= sprintf(
                '\'__then\' => %s,' . chr(10),
                $templateCompiler->wrapChildNodesInClosure($node)
            );
        }

        if ($elseIfCounter > 0) {
            $elseIfCode .= '],' . chr(10);
            $argumentInitializationCode .= $elseIfCode;
        }
        $argumentInitializationCode .= '];' . chr(10);

        return [
            'initialization' => '// Rendering ViewHelper ' . $node->getViewHelperClassName() . chr(10) .
                $accumulatedArgumentInitializationCode . chr(10) .
                $argumentInitializationCode,
            'execution' => sprintf(
                '%s::renderStatic(%s, static fn() => \'\', $renderingContext)' . chr(10),
                get_class($this),
                $argumentsVariableName,
            )
        ];
    }
}