mirror of
				https://github.com/Mibew/handlebars.php.git
				synced 2025-11-04 12:05:09 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			657 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			657 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * This file is part of Handlebars-php
 | 
						|
 * Base on mustache-php https://github.com/bobthecow/mustache.php
 | 
						|
 *
 | 
						|
 * PHP version 5.3
 | 
						|
 *
 | 
						|
 * @category  Xamin
 | 
						|
 * @package   Handlebars
 | 
						|
 * @author    fzerorubigd <fzerorubigd@gmail.com>
 | 
						|
 * @author    Behrooz Shabani <everplays@gmail.com>
 | 
						|
 * @author    Chris Gray <chris.w.gray@gmail.com>
 | 
						|
 * @author    Dmitriy Simushev <simushevds@gmail.com>
 | 
						|
 * @author    majortom731 <majortom731@googlemail.com>
 | 
						|
 * @author    Jeff Turcotte <jeff.turcotte@gmail.com>
 | 
						|
 * @author    John Slegers <slegersjohn@gmail.com>
 | 
						|
 * @copyright 2010-2012 (c) Justin Hileman
 | 
						|
 * @copyright 2012 (c) ParsPooyesh Co
 | 
						|
 * @copyright 2013 (c) Behrooz Shabani
 | 
						|
 * @license   MIT <http://opensource.org/licenses/MIT>
 | 
						|
 * @version   GIT: $Id$
 | 
						|
 * @link      http://xamin.ir
 | 
						|
 */
 | 
						|
 | 
						|
namespace Handlebars;
 | 
						|
 | 
						|
/**
 | 
						|
 * Handlebars base template
 | 
						|
 * contain some utility method to get context and helpers
 | 
						|
 *
 | 
						|
 * @category  Xamin
 | 
						|
 * @package   Handlebars
 | 
						|
 * @author    fzerorubigd <fzerorubigd@gmail.com>
 | 
						|
 * @copyright 2010-2012 (c) Justin Hileman
 | 
						|
 * @copyright 2012 (c) ParsPooyesh Co
 | 
						|
 * @license   MIT <http://opensource.org/licenses/MIT>
 | 
						|
 * @version   Release: @package_version@
 | 
						|
 * @link      http://xamin.ir
 | 
						|
 */
 | 
						|
 | 
						|
class Template
 | 
						|
{
 | 
						|
    /**
 | 
						|
     * @var Handlebars
 | 
						|
     */
 | 
						|
    protected $handlebars;
 | 
						|
 | 
						|
 | 
						|
    protected $tree = array();
 | 
						|
 | 
						|
    protected $source = '';
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var array Run stack
 | 
						|
     */
 | 
						|
    private $_stack = array();
 | 
						|
 | 
						|
    /**
 | 
						|
     * Handlebars template constructor
 | 
						|
     *
 | 
						|
     * @param Handlebars $engine handlebar engine
 | 
						|
     * @param array      $tree   Parsed tree
 | 
						|
     * @param string     $source Handlebars source
 | 
						|
     */
 | 
						|
    public function __construct(Handlebars $engine, $tree, $source)
 | 
						|
    {
 | 
						|
        $this->handlebars = $engine;
 | 
						|
        $this->tree = $tree;
 | 
						|
        $this->source = $source;
 | 
						|
        array_push($this->_stack, array(0, $this->getTree(), false));
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get current tree
 | 
						|
     *
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
    public function getTree()
 | 
						|
    {
 | 
						|
        return $this->tree;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get current source
 | 
						|
     *
 | 
						|
     * @return string
 | 
						|
     */
 | 
						|
    public function getSource()
 | 
						|
    {
 | 
						|
        return $this->source;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get current engine associated with this object
 | 
						|
     *
 | 
						|
     * @return Handlebars
 | 
						|
     */
 | 
						|
    public function getEngine()
 | 
						|
    {
 | 
						|
        return $this->handlebars;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * set stop token for render and discard method
 | 
						|
     *
 | 
						|
     * @param string $token token to set as stop token or false to remove
 | 
						|
     *
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
 | 
						|
    public function setStopToken($token)
 | 
						|
    {
 | 
						|
        $topStack = array_pop($this->_stack);
 | 
						|
        $topStack[2] = $token;
 | 
						|
        array_push($this->_stack, $topStack);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * get current stop token
 | 
						|
     *
 | 
						|
     * @return string|bool
 | 
						|
     */
 | 
						|
 | 
						|
    public function getStopToken()
 | 
						|
    {
 | 
						|
        $topStack = end($this->_stack);
 | 
						|
 | 
						|
        return $topStack[2];
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Render top tree
 | 
						|
     *
 | 
						|
     * @param mixed $context current context
 | 
						|
     *
 | 
						|
     * @throws \RuntimeException
 | 
						|
     * @return string
 | 
						|
     */
 | 
						|
    public function render($context)
 | 
						|
    {
 | 
						|
        if (!$context instanceof Context) {
 | 
						|
            $context = new Context($context);
 | 
						|
        }
 | 
						|
        $topTree = end($this->_stack); // never pop a value from stack
 | 
						|
        list($index, $tree, $stop) = $topTree;
 | 
						|
 | 
						|
        $buffer = '';
 | 
						|
        $rTrim = false;
 | 
						|
        while (array_key_exists($index, $tree)) {
 | 
						|
            $current = $tree[$index];
 | 
						|
            $index++;
 | 
						|
            //if the section is exactly like waitFor
 | 
						|
            if (is_string($stop)
 | 
						|
                && $current[Tokenizer::TYPE] == Tokenizer::T_ESCAPED
 | 
						|
                && $current[Tokenizer::NAME] === $stop
 | 
						|
            ) {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            if (isset($current[Tokenizer::TRIM_LEFT]) && $current[Tokenizer::TRIM_LEFT]) {
 | 
						|
                $buffer = rtrim($buffer);
 | 
						|
            }
 | 
						|
 | 
						|
            $tmp = $this->_renderInternal($current, $context);
 | 
						|
 | 
						|
            if (isset($current[Tokenizer::TRIM_LEFT]) && $current[Tokenizer::TRIM_LEFT]) {
 | 
						|
                $tmp = rtrim($tmp);
 | 
						|
            }
 | 
						|
 | 
						|
            if ($rTrim  || (isset($current[Tokenizer::TRIM_RIGHT]) && $current[Tokenizer::TRIM_RIGHT])) {
 | 
						|
                $tmp = ltrim($tmp);
 | 
						|
            }
 | 
						|
 | 
						|
            $buffer .= $tmp;
 | 
						|
            // Some time, there is more than one string token (first is empty),
 | 
						|
            //so we need to trim all of them in one shot
 | 
						|
 | 
						|
            $rTrim = (empty($tmp) && $rTrim) ||
 | 
						|
                isset($current[Tokenizer::TRIM_RIGHT]) && $current[Tokenizer::TRIM_RIGHT];
 | 
						|
        }
 | 
						|
        if ($stop) {
 | 
						|
            //Ok break here, the helper should be aware of this.
 | 
						|
            $newStack = array_pop($this->_stack);
 | 
						|
            $newStack[0] = $index;
 | 
						|
            $newStack[2] = false; //No stop token from now on
 | 
						|
            array_push($this->_stack, $newStack);
 | 
						|
        }
 | 
						|
 | 
						|
        return $buffer;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Render tokens base on type of tokens
 | 
						|
     *
 | 
						|
     * @param array $current current token
 | 
						|
     * @param mixed $context current context
 | 
						|
     *
 | 
						|
     * @return string
 | 
						|
     */
 | 
						|
    private function _renderInternal($current, $context)
 | 
						|
    {
 | 
						|
        $result = '';
 | 
						|
        switch ($current[Tokenizer::TYPE]) {
 | 
						|
        case Tokenizer::T_END_SECTION:
 | 
						|
            break; // Its here just for handling whitespace trim.
 | 
						|
        case Tokenizer::T_SECTION :
 | 
						|
            $newStack = isset($current[Tokenizer::NODES])
 | 
						|
                ? $current[Tokenizer::NODES] : array();
 | 
						|
            array_push($this->_stack, array(0, $newStack, false));
 | 
						|
            $result = $this->_section($context, $current);
 | 
						|
            array_pop($this->_stack);
 | 
						|
            break;
 | 
						|
        case Tokenizer::T_INVERTED :
 | 
						|
            $newStack = isset($current[Tokenizer::NODES]) ?
 | 
						|
                $current[Tokenizer::NODES] : array();
 | 
						|
            array_push($this->_stack, array(0, $newStack, false));
 | 
						|
            $result = $this->_inverted($context, $current);
 | 
						|
            array_pop($this->_stack);
 | 
						|
            break;
 | 
						|
        case Tokenizer::T_COMMENT :
 | 
						|
            $result = '';
 | 
						|
            break;
 | 
						|
        case Tokenizer::T_PARTIAL:
 | 
						|
        case Tokenizer::T_PARTIAL_2:
 | 
						|
            $result = $this->_partial($context, $current);
 | 
						|
            break;
 | 
						|
        case Tokenizer::T_UNESCAPED:
 | 
						|
        case Tokenizer::T_UNESCAPED_2:
 | 
						|
            $result = $this->_get($context, $current, false);
 | 
						|
            break;
 | 
						|
        case Tokenizer::T_ESCAPED:
 | 
						|
            $result = $this->_get($context, $current, true);
 | 
						|
            break;
 | 
						|
        case Tokenizer::T_TEXT:
 | 
						|
            $result = $current[Tokenizer::VALUE];
 | 
						|
            break;
 | 
						|
            /* How we could have another type of token? this part of code
 | 
						|
            is not used at all.
 | 
						|
            default:
 | 
						|
                throw new \RuntimeException(
 | 
						|
                    'Invalid node type : ' . json_encode($current)
 | 
						|
                );
 | 
						|
            */
 | 
						|
        }
 | 
						|
 | 
						|
        return $result;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Discard top tree
 | 
						|
     *
 | 
						|
     * @return string
 | 
						|
     */
 | 
						|
    public function discard()
 | 
						|
    {
 | 
						|
        $topTree = end($this->_stack); //This method never pop a value from stack
 | 
						|
        list($index, $tree, $stop) = $topTree;
 | 
						|
        while (array_key_exists($index, $tree)) {
 | 
						|
            $current = $tree[$index];
 | 
						|
            $index++;
 | 
						|
            //if the section is exactly like waitFor
 | 
						|
            if (is_string($stop)
 | 
						|
                && $current[Tokenizer::TYPE] == Tokenizer::T_ESCAPED
 | 
						|
                && $current[Tokenizer::NAME] === $stop
 | 
						|
            ) {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if ($stop) {
 | 
						|
            //Ok break here, the helper should be aware of this.
 | 
						|
            $newStack = array_pop($this->_stack);
 | 
						|
            $newStack[0] = $index;
 | 
						|
            $newStack[2] = false;
 | 
						|
            array_push($this->_stack, $newStack);
 | 
						|
        }
 | 
						|
 | 
						|
        return '';
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Rewind top tree index to the first element
 | 
						|
     *
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function rewind()
 | 
						|
    {
 | 
						|
        $topStack = array_pop($this->_stack);
 | 
						|
        $topStack[0] = 0;
 | 
						|
        array_push($this->_stack, $topStack);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Process handlebars section style
 | 
						|
     *
 | 
						|
     * @param Context $context current context
 | 
						|
     * @param array   $current section node data
 | 
						|
     *
 | 
						|
     * @return mixed|string
 | 
						|
     */
 | 
						|
    private function _handlebarsStyleSection(Context $context, $current)
 | 
						|
    {
 | 
						|
        $helpers = $this->handlebars->getHelpers();
 | 
						|
        $sectionName = $current[Tokenizer::NAME];
 | 
						|
 | 
						|
        if (isset($current[Tokenizer::END])) {
 | 
						|
            $source = substr(
 | 
						|
                $this->getSource(),
 | 
						|
                $current[Tokenizer::INDEX],
 | 
						|
                $current[Tokenizer::END] - $current[Tokenizer::INDEX]
 | 
						|
            );
 | 
						|
        } else {
 | 
						|
            $source = '';
 | 
						|
        }
 | 
						|
 | 
						|
        // subexpression parsing loop
 | 
						|
        $subexprs = array(); // will contain all subexpressions inside outermost brackets
 | 
						|
        $insideOf = array( 'single' => false, 'double' => false );
 | 
						|
        $lvl = 0;
 | 
						|
        $cur_start = 0;
 | 
						|
        for ($i=0; $i < strlen($current[Tokenizer::ARGS]); $i++) {
 | 
						|
            $cur = substr($current[Tokenizer::ARGS], $i, 1);
 | 
						|
            if ($cur == "'" ) {
 | 
						|
                $insideOf['single'] = ! $insideOf['single'];
 | 
						|
            }
 | 
						|
            if ($cur == '"' ) {
 | 
						|
                $insideOf['double'] = ! $insideOf['double'];
 | 
						|
            }
 | 
						|
            if ($cur == '(' && ! $insideOf['single'] && ! $insideOf['double']) {
 | 
						|
                if ($lvl == 0) {
 | 
						|
                    $cur_start = $i+1;
 | 
						|
                }
 | 
						|
                $lvl++;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            if ($cur == ')' && ! $insideOf['single'] && ! $insideOf['double']) {
 | 
						|
                $lvl--;
 | 
						|
                if ($lvl == 0) {
 | 
						|
                    $subexprs[] = substr($current[Tokenizer::ARGS], $cur_start, $i - $cur_start);
 | 
						|
                }
 | 
						|
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (! empty($subexprs)) {
 | 
						|
            foreach ($subexprs as $expr) {
 | 
						|
                $cmd = explode(" ", $expr);
 | 
						|
                $name = trim($cmd[0]);
 | 
						|
                // construct artificial section node
 | 
						|
                $section_node = array(
 | 
						|
                    Tokenizer::TYPE => Tokenizer::T_ESCAPED,
 | 
						|
                    Tokenizer::NAME => $name,
 | 
						|
                    Tokenizer::OTAG => $current[Tokenizer::OTAG],
 | 
						|
                    Tokenizer::CTAG => $current[Tokenizer::CTAG],
 | 
						|
                    Tokenizer::INDEX => $current[Tokenizer::INDEX],
 | 
						|
                    Tokenizer::ARGS => implode(" ", array_slice($cmd, 1))
 | 
						|
                );
 | 
						|
                // resolve the node recursively
 | 
						|
                $resolved = $this->_handlebarsStyleSection($context, $section_node);
 | 
						|
                // replace original subexpression with result
 | 
						|
                $current[Tokenizer::ARGS] = str_replace('('.$expr.')', $resolved, $current[Tokenizer::ARGS]);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        $return = $helpers->call($sectionName, $this, $context, $current[Tokenizer::ARGS], $source);
 | 
						|
 | 
						|
        if ($return instanceof String) {
 | 
						|
            return $this->handlebars->loadString($return)->render($context);
 | 
						|
        } else {
 | 
						|
            return $return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Process Mustache section style
 | 
						|
     *
 | 
						|
     * @param Context $context current context
 | 
						|
     * @param array   $current section node data
 | 
						|
     *
 | 
						|
     * @throws \RuntimeException
 | 
						|
     * @return mixed|string
 | 
						|
     */
 | 
						|
    private function _mustacheStyleSection(Context $context, $current)
 | 
						|
    {
 | 
						|
        $sectionName = $current[Tokenizer::NAME];
 | 
						|
 | 
						|
        // fallback to mustache style each/with/for just if there is
 | 
						|
        // no argument at all.
 | 
						|
        try {
 | 
						|
            $sectionVar = $context->get($sectionName, true);
 | 
						|
        } catch (\InvalidArgumentException $e) {
 | 
						|
            throw new \RuntimeException(
 | 
						|
                $sectionName . ' is not registered as a helper'
 | 
						|
            );
 | 
						|
        }
 | 
						|
        $buffer = '';
 | 
						|
        if (is_array($sectionVar) || $sectionVar instanceof \Traversable) {
 | 
						|
            $isList = is_array($sectionVar) &&
 | 
						|
                (array_keys($sectionVar) === range(0, count($sectionVar) - 1));
 | 
						|
            $index = 0;
 | 
						|
            $lastIndex = $isList ? (count($sectionVar) - 1) : false;
 | 
						|
 | 
						|
            foreach ($sectionVar as $key => $d) {
 | 
						|
                $specialVariables = array(
 | 
						|
                    '@index' => $index,
 | 
						|
                    '@first' => ($index === 0),
 | 
						|
                    '@last' => ($index === $lastIndex),
 | 
						|
                );
 | 
						|
                if (!$isList) {
 | 
						|
                    $specialVariables['@key'] = $key;
 | 
						|
                }
 | 
						|
                $context->pushSpecialVariables($specialVariables);
 | 
						|
                $context->push($d);
 | 
						|
                $buffer .= $this->render($context);
 | 
						|
                $context->pop();
 | 
						|
                $context->popSpecialVariables();
 | 
						|
                $index++;
 | 
						|
            }
 | 
						|
        } elseif (is_object($sectionVar)) {
 | 
						|
            //Act like with
 | 
						|
            $context->push($sectionVar);
 | 
						|
            $buffer = $this->render($context);
 | 
						|
            $context->pop();
 | 
						|
        } elseif ($sectionVar) {
 | 
						|
            $buffer = $this->render($context);
 | 
						|
        }
 | 
						|
 | 
						|
        return $buffer;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Process section nodes
 | 
						|
     *
 | 
						|
     * @param Context $context current context
 | 
						|
     * @param array   $current section node data
 | 
						|
     *
 | 
						|
     * @throws \RuntimeException
 | 
						|
     * @return string the result
 | 
						|
     */
 | 
						|
    private function _section(Context $context, $current)
 | 
						|
    {
 | 
						|
        $helpers = $this->handlebars->getHelpers();
 | 
						|
        $sectionName = $current[Tokenizer::NAME];
 | 
						|
        if ($helpers->has($sectionName)) {
 | 
						|
            return $this->_handlebarsStyleSection($context, $current);
 | 
						|
        } elseif (trim($current[Tokenizer::ARGS]) == '') {
 | 
						|
            return $this->_mustacheStyleSection($context, $current);
 | 
						|
        } else {
 | 
						|
            throw new \RuntimeException(
 | 
						|
                $sectionName . ' is not registered as a helper'
 | 
						|
            );
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Process inverted section
 | 
						|
     *
 | 
						|
     * @param Context $context current context
 | 
						|
     * @param array   $current section node data
 | 
						|
     *
 | 
						|
     * @return string the result
 | 
						|
     */
 | 
						|
    private function _inverted(Context $context, $current)
 | 
						|
    {
 | 
						|
        $sectionName = $current[Tokenizer::NAME];
 | 
						|
        $data = $context->get($sectionName);
 | 
						|
        if (!$data) {
 | 
						|
            return $this->render($context);
 | 
						|
        } else {
 | 
						|
            //No need to discard here, since it has no else
 | 
						|
            return '';
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Process partial section
 | 
						|
     *
 | 
						|
     * @param Context $context current context
 | 
						|
     * @param array   $current section node data
 | 
						|
     *
 | 
						|
     * @return string the result
 | 
						|
     */
 | 
						|
    private function _partial(Context $context, $current)
 | 
						|
    {
 | 
						|
        $partial = $this->handlebars->loadPartial($current[Tokenizer::NAME]);
 | 
						|
 | 
						|
        if ($current[Tokenizer::ARGS]) {
 | 
						|
            $context = $context->get($current[Tokenizer::ARGS]);
 | 
						|
        }
 | 
						|
 | 
						|
        return $partial->render($context);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * Check if there is a helper with this variable name available or not.
 | 
						|
     *
 | 
						|
     * @param array $current current token
 | 
						|
     *
 | 
						|
     * @return boolean
 | 
						|
     */
 | 
						|
    private function _isSection($current)
 | 
						|
    {
 | 
						|
        $helpers = $this->getEngine()->getHelpers();
 | 
						|
        // Tokenizer doesn't process the args -if any- so be aware of that
 | 
						|
        $name = explode(' ', $current[Tokenizer::NAME], 2);
 | 
						|
 | 
						|
        return $helpers->has(reset($name));
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * get replacing value of a tag
 | 
						|
     *
 | 
						|
     * will process the tag as section, if a helper with the same name could be
 | 
						|
     * found, so {{helper arg}} can be used instead of {{#helper arg}}.
 | 
						|
     *
 | 
						|
     * @param Context $context current context
 | 
						|
     * @param array   $current section node data
 | 
						|
     * @param boolean $escaped escape result or not
 | 
						|
     *
 | 
						|
     * @return string the string to be replaced with the tag
 | 
						|
     */
 | 
						|
    private function _get(Context $context, $current, $escaped)
 | 
						|
    {
 | 
						|
        if ($this->_isSection($current)) {
 | 
						|
            return $this->_getSection($context, $current, $escaped);
 | 
						|
        } else {
 | 
						|
            return $this->_getVariable($context, $current, $escaped);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Process section
 | 
						|
     *
 | 
						|
     * @param Context $context current context
 | 
						|
     * @param array   $current section node data
 | 
						|
     * @param boolean $escaped escape result or not
 | 
						|
     *
 | 
						|
     * @return string the result
 | 
						|
     */
 | 
						|
    private function _getSection(Context $context, $current, $escaped)
 | 
						|
    {
 | 
						|
        $args = explode(' ', $current[Tokenizer::NAME], 2);
 | 
						|
        $name = array_shift($args);
 | 
						|
        $current[Tokenizer::NAME] = $name;
 | 
						|
        $current[Tokenizer::ARGS] = implode(' ', $args);
 | 
						|
        $result = $this->_section($context, $current);
 | 
						|
 | 
						|
        if ($escaped && !($result instanceof SafeString)) {
 | 
						|
            $escape_args = $this->handlebars->getEscapeArgs();
 | 
						|
            array_unshift($escape_args, $result);
 | 
						|
            $result = call_user_func_array(
 | 
						|
                $this->handlebars->getEscape(),
 | 
						|
                array_values($escape_args)
 | 
						|
            );
 | 
						|
        }
 | 
						|
 | 
						|
        return $result;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Process variable
 | 
						|
     *
 | 
						|
     * @param Context $context current context
 | 
						|
     * @param array   $current section node data
 | 
						|
     * @param boolean $escaped escape result or not
 | 
						|
     *
 | 
						|
     * @return string the result
 | 
						|
     */
 | 
						|
    private function _getVariable(Context $context, $current, $escaped)
 | 
						|
    {
 | 
						|
        $name = $current[Tokenizer::NAME];
 | 
						|
        $value = $context->get($name);
 | 
						|
        if ($escaped) {
 | 
						|
            $args = $this->handlebars->getEscapeArgs();
 | 
						|
            array_unshift($args, $value);
 | 
						|
            $value = call_user_func_array(
 | 
						|
                $this->handlebars->getEscape(),
 | 
						|
                array_values($args)
 | 
						|
            );
 | 
						|
        }
 | 
						|
 | 
						|
        return $value;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Break an argument string into an array of named arguments
 | 
						|
     *
 | 
						|
     * @param string $string Argument String as passed to a helper
 | 
						|
     *
 | 
						|
     * @return array the argument list as an array
 | 
						|
     */
 | 
						|
    public function parseNamedArguments($string)
 | 
						|
    {
 | 
						|
        $variableName = '(?:(?:[^\'"\[\]\s]|\[.+?\])+)';
 | 
						|
        $escapedValue = '(?:(?<!\\\\)".*?(?<!\\\\)"|(?<!\\\\)\'.*?(?<!\\\\)\')';
 | 
						|
 | 
						|
        // Get list of named arguemnts
 | 
						|
        $matches = array();
 | 
						|
        preg_match_all(
 | 
						|
            '#(' . $variableName . ')\s*=\s*(' . $escapedValue . '|' . $variableName . ')#',
 | 
						|
            $string,
 | 
						|
            $matches,
 | 
						|
            PREG_SET_ORDER
 | 
						|
        );
 | 
						|
 | 
						|
        $args = array();
 | 
						|
        for ($x = 0, $c = count($matches); $x < $c; $x++) {
 | 
						|
            $name = $matches[$x][1];
 | 
						|
            $value = $matches[$x][2];
 | 
						|
 | 
						|
            // Check if argument's name is a segment
 | 
						|
            if ($name[0] == '[') {
 | 
						|
                $name = substr($name, 1, -1);
 | 
						|
            }
 | 
						|
 | 
						|
            // Check if argument's value is a quoted string literal
 | 
						|
            if ($value[0] == "'" || $value[0] == '"') {
 | 
						|
                // Remove enclosing quotes and unescape
 | 
						|
                $value = new \Handlebars\String(stripcslashes(substr($value, 1, -1)));
 | 
						|
            }
 | 
						|
 | 
						|
            $args[$name] = $value;
 | 
						|
        }
 | 
						|
 | 
						|
        return $args;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Break an argument string into an array of strings
 | 
						|
     *
 | 
						|
     * @param string $string Argument String as passed to a helper
 | 
						|
     *
 | 
						|
     * @throws \RuntimeException
 | 
						|
     * @return array the argument list as an array
 | 
						|
     */
 | 
						|
    public function parseArguments($string)
 | 
						|
    {
 | 
						|
        $args = array();
 | 
						|
        preg_match_all('#(?:[^\'"\[\]\s]|\[.+?\])+|(?<!\\\\)("|\')(?:[^\\\\]|\\\\.)*?\1|\S+#s', $string, $args);
 | 
						|
        $args = isset($args[0]) ? $args[0] : array();
 | 
						|
 | 
						|
        for ($x = 0, $argc = count($args); $x < $argc; $x++) {
 | 
						|
            // check to see if argument is a quoted string literal
 | 
						|
            if ($args[$x][0] == "'" || $args[$x][0] == '"') {
 | 
						|
                if ($args[$x][0] === substr($args[$x], -1)) {
 | 
						|
                    // remove enclosing quotes and unescape
 | 
						|
                    $args[$x] = new \Handlebars\String(stripcslashes(substr($args[$x], 1, strlen($args[$x]) - 2)));
 | 
						|
                } else {
 | 
						|
                    throw new \RuntimeException("Malformed string: " . $args);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        return $args;
 | 
						|
    }
 | 
						|
}
 |