Your IP : 216.73.217.13


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

<?php

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

namespace TYPO3Fluid\Fluid\Core\Variables;

/**
 * Class StandardVariableProvider
 */
class StandardVariableProvider implements VariableProviderInterface
{
    /**
     * @deprecated Unused. Will be removed.
     */
    public const ACCESSOR_ARRAY = 'array';
    /**
     * @deprecated Unused. Will be removed.
     */
    public const ACCESSOR_GETTER = 'getter';
    /**
     * @deprecated Unused. Will be removed.
     */
    public const ACCESSOR_ASSERTER = 'asserter';
    /**
     * @deprecated Unused. Will be removed.
     */
    public const ACCESSOR_PUBLICPROPERTY = 'public';

    /**
     * Variables stored in context
     *
     * @var mixed
     */
    protected $variables = [];

    /**
     * Variables, if any, with which to initialize this
     * VariableProvider.
     *
     * @param array $variables
     */
    public function __construct(array $variables = [])
    {
        $this->variables = $variables;
    }

    /**
     * @param array|\ArrayAccess $variables
     * @return VariableProviderInterface
     */
    public function getScopeCopy($variables)
    {
        if (!array_key_exists('settings', $variables) && array_key_exists('settings', $this->variables)) {
            $variables['settings'] = $this->variables['settings'];
        }
        $className = get_class($this);
        return new $className($variables);
    }

    /**
     * Set the source data used by this VariableProvider. The
     * source can be any type, but the type must of course be
     * supported by the VariableProvider itself.
     *
     * @param mixed $source
     */
    public function setSource($source)
    {
        $this->variables = $source;
    }

    /**
     * @return mixed
     */
    public function getSource()
    {
        return $this->variables;
    }

    /**
     * Get every variable provisioned by the VariableProvider
     * implementing the interface. Must return an array or
     * ArrayAccess instance!
     *
     * @return array|\ArrayAccess
     */
    public function getAll()
    {
        return $this->variables;
    }

    /**
     * Add a variable to the context
     *
     * @param string $identifier Identifier of the variable to add
     * @param mixed $value The variable's value
     * @api
     */
    public function add($identifier, $value)
    {
        $this->variables[$identifier] = $value;
    }

    /**
     * Get a variable from the context.
     *
     * If "_all" is given as identifier, all variables are returned in an array,
     * if one of the other reserved variables are given, their appropriate value
     * they're representing is returned.
     *
     * @param string $identifier
     * @return mixed The variable value identified by $identifier
     * @api
     */
    public function get($identifier)
    {
        return $this->getByPath($identifier);
    }

    /**
     * Get a variable by dotted path expression, retrieving the
     * variable from nested arrays/objects one segment at a time.
     *
     * @param string $path
     * @return mixed
     */
    public function getByPath($path)
    {
        $subject = $this->variables;
        $subVariableReferences = explode('.', $this->resolveSubVariableReferences($path));
        foreach ($subVariableReferences as $pathSegment) {
            if ((is_array($subject) && array_key_exists($pathSegment, $subject))
                || ($subject instanceof \ArrayAccess && $subject->offsetExists($pathSegment))
            ) {
                $subject = $subject[$pathSegment];
                continue;
            }
            if (is_object($subject)) {
                $upperCasePropertyName = ucfirst($pathSegment);
                $getMethod = 'get' . $upperCasePropertyName;
                if (method_exists($subject, $getMethod)) {
                    $subject = $subject->$getMethod();
                    continue;
                }
                $isMethod = 'is' . $upperCasePropertyName;
                if (method_exists($subject, $isMethod)) {
                    $subject = $subject->$isMethod();
                    continue;
                }
                $hasMethod = 'has' . $upperCasePropertyName;
                if (method_exists($subject, $hasMethod)) {
                    $subject = $subject->$hasMethod();
                    continue;
                }
                if (property_exists($subject, $pathSegment)) {
                    $subject = $subject->$pathSegment;
                    continue;
                }
            }
            return null;
        }
        return $subject;
    }

    /**
     * Remove a variable from context.
     *
     * @param string $identifier The identifier to remove
     * @api
     */
    public function remove($identifier)
    {
        if (array_key_exists($identifier, $this->variables)) {
            unset($this->variables[$identifier]);
        }
    }

    /**
     * Returns an array of all identifiers available in the context.
     *
     * @return array Array of identifier strings
     */
    public function getAllIdentifiers()
    {
        return array_keys($this->variables);
    }

    /**
     * Checks if this property exists in the VariableContainer.
     *
     * @param string $identifier
     * @return bool TRUE if $identifier exists, FALSE otherwise
     * @api
     */
    public function exists($identifier)
    {
        return array_key_exists($identifier, $this->variables);
    }

    /**
     * Clean up for serializing.
     *
     * @return string[]
     */
    public function __sleep()
    {
        return ['variables'];
    }

    /**
     * Adds a variable to the context.
     *
     * @param string $identifier Identifier of the variable to add
     * @param mixed $value The variable's value
     */
    public function offsetSet($identifier, $value)
    {
        $this->add($identifier, $value);
    }

    /**
     * Remove a variable from context.
     *
     * @param string $identifier The identifier to remove
     */
    public function offsetUnset($identifier)
    {
        $this->remove($identifier);
    }

    /**
     * Checks if this property exists in the VariableContainer.
     *
     * @param string $identifier
     * @return bool TRUE if $identifier exists, FALSE otherwise
     */
    public function offsetExists($identifier)
    {
        return $this->exists($identifier);
    }

    /**
     * Get a variable from the context.
     *
     * @param string $identifier
     * @return mixed The variable identified by $identifier
     */
    public function offsetGet($identifier)
    {
        return $this->get($identifier);
    }

    protected function resolveSubVariableReferences(string $propertyPath): string
    {
        if (strpos($propertyPath, '{') !== false) {
            // https://www.pcre.org/original/doc/html/pcrepattern.html#SEC1
            // https://stackoverflow.com/questions/546433/regular-expression-to-match-balanced-parentheses
            // https://stackoverflow.com/questions/524548/regular-expression-to-detect-semi-colon-terminated-c-for-while-loops/524624#524624
            // @todo: We're dealing with both *parallel* and *nested* curly braces here. It *might* be better to
            //        substitute the regex with a char-based parser that counts opening vs. closing braces as
            //        mentioned in the links above. Instead, we're currently using a backtracking recursive regex.
            preg_match_all('/{[^}{]*+(?:(?R)[^}{]*)*+}/', $propertyPath, $matches);
            foreach ($matches[0] as $match) {
                $subPropertyPath = substr($match, 1, -1);
                $subPropertyValue = $this->getByPath($subPropertyPath);
                if ($subPropertyValue !== null) {
                    $propertyPath = str_replace($match, $subPropertyValue, $propertyPath);
                }
            }
        }
        return $propertyPath;
    }
}