mirror of
https://github.com/Mibew/handlebars.php.git
synced 2025-03-22 08:21:23 +03:00
parent
cd8cec42a0
commit
a68318f4c5
@ -115,6 +115,7 @@ class Helpers
|
||||
* @param array $args The arguments passed the the helper
|
||||
* @param string $source The source
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @return mixed The helper return value
|
||||
*/
|
||||
public function call($name, Template $template, Context $context, $args, $source)
|
||||
|
@ -92,7 +92,7 @@ class Parser
|
||||
array_unshift($newNodes, $result);
|
||||
}
|
||||
} while (true);
|
||||
break;
|
||||
// There is no break here, since we need the end token to handle the whitespace trim
|
||||
default:
|
||||
array_push($stack, $token);
|
||||
}
|
||||
@ -101,7 +101,6 @@ class Parser
|
||||
} while ($tokens->valid());
|
||||
|
||||
return $stack;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -145,6 +145,7 @@ class Template
|
||||
list($index, $tree, $stop) = $topTree;
|
||||
|
||||
$buffer = '';
|
||||
$rTrim = false;
|
||||
while (array_key_exists($index, $tree)) {
|
||||
$current = $tree[$index];
|
||||
$index++;
|
||||
@ -155,44 +156,60 @@ class Template
|
||||
) {
|
||||
break;
|
||||
}
|
||||
if (isset($current[Tokenizer::TRIM_LEFT]) && $current[Tokenizer::TRIM_LEFT]) {
|
||||
$buffer = rtrim($buffer);
|
||||
}
|
||||
|
||||
$tmp = '';
|
||||
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));
|
||||
$buffer .= $this->_section($context, $current);
|
||||
$tmp = $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));
|
||||
$buffer .= $this->_inverted($context, $current);
|
||||
$tmp = $this->_inverted($context, $current);
|
||||
array_pop($this->_stack);
|
||||
break;
|
||||
case Tokenizer::T_COMMENT :
|
||||
$buffer .= '';
|
||||
$tmp = '';
|
||||
break;
|
||||
case Tokenizer::T_PARTIAL:
|
||||
case Tokenizer::T_PARTIAL_2:
|
||||
$buffer .= $this->_partial($context, $current);
|
||||
$tmp = $this->_partial($context, $current);
|
||||
break;
|
||||
case Tokenizer::T_UNESCAPED:
|
||||
case Tokenizer::T_UNESCAPED_2:
|
||||
$buffer .= $this->_get($context, $current, false);
|
||||
$tmp = $this->_get($context, $current, false);
|
||||
break;
|
||||
case Tokenizer::T_ESCAPED:
|
||||
|
||||
$buffer .= $this->_get($context, $current, true);
|
||||
$tmp = $this->_get($context, $current, true);
|
||||
break;
|
||||
case Tokenizer::T_TEXT:
|
||||
$buffer .= $current[Tokenizer::VALUE];
|
||||
$tmp = $current[Tokenizer::VALUE];
|
||||
break;
|
||||
default:
|
||||
throw new \RuntimeException(
|
||||
'Invalid node type : ' . json_encode($current)
|
||||
);
|
||||
}
|
||||
if ($rTrim) {
|
||||
$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.
|
||||
@ -273,25 +290,25 @@ class Template
|
||||
|
||||
// subexpression parsing loop
|
||||
$subexprs = array(); // will contain all subexpressions inside outermost brackets
|
||||
$inside_of = array( 'single' => false, 'double' => false );
|
||||
$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 == "'" ) {
|
||||
$inside_of['single'] = ! $inside_of['single'];
|
||||
$insideOf['single'] = ! $insideOf['single'];
|
||||
}
|
||||
if ($cur == '"' ) {
|
||||
$inside_of['double'] = ! $inside_of['double'];
|
||||
$insideOf['double'] = ! $insideOf['double'];
|
||||
}
|
||||
if ($cur == '(' && ! $inside_of['single'] && ! $inside_of['double']) {
|
||||
if ($cur == '(' && ! $insideOf['single'] && ! $insideOf['double']) {
|
||||
if ($lvl == 0) {
|
||||
$cur_start = $i+1;
|
||||
}
|
||||
$lvl++;
|
||||
continue;
|
||||
}
|
||||
if ($cur == ')' && ! $inside_of['single'] && ! $inside_of['double']) {
|
||||
if ($cur == ')' && ! $insideOf['single'] && ! $insideOf['double']) {
|
||||
$lvl--;
|
||||
if ($lvl == 0) {
|
||||
$subexprs[] = substr($current[Tokenizer::ARGS], $cur_start, $i - $cur_start);
|
||||
|
@ -60,6 +60,7 @@ class Tokenizer
|
||||
const T_ESCAPE = "\\";
|
||||
const T_SINGLE_Q = "'";
|
||||
const T_DOUBLE_Q = "\"";
|
||||
const T_TRIM = "~";
|
||||
|
||||
// Valid token types
|
||||
private static $_tagTypes = array(
|
||||
@ -93,6 +94,8 @@ class Tokenizer
|
||||
const NODES = 'nodes';
|
||||
const VALUE = 'value';
|
||||
const ARGS = 'args';
|
||||
const TRIM_LEFT = 'tleft';
|
||||
const TRIM_RIGHT = 'rleft';
|
||||
|
||||
protected $state;
|
||||
protected $tagType;
|
||||
@ -103,6 +106,10 @@ class Tokenizer
|
||||
protected $lineStart;
|
||||
protected $otag;
|
||||
protected $ctag;
|
||||
protected $escaped;
|
||||
protected $escaping;
|
||||
protected $trimLeft;
|
||||
protected $trimRight;
|
||||
|
||||
/**
|
||||
* Scan and tokenize template source.
|
||||
@ -132,10 +139,10 @@ class Tokenizer
|
||||
|
||||
// To play nice with helpers' arguments quote and apostrophe marks
|
||||
// should be additionally escaped only when they are not in a tag.
|
||||
$quote_in_tag = $this->state != self::IN_TEXT
|
||||
$quoteInTag = $this->state != self::IN_TEXT
|
||||
&& ($text[$i] == self::T_SINGLE_Q || $text[$i] == self::T_DOUBLE_Q);
|
||||
|
||||
if ($this->escaped && $text[$i] != self::T_UNESCAPED && !$quote_in_tag) {
|
||||
if ($this->escaped && $text[$i] != self::T_UNESCAPED && !$quoteInTag) {
|
||||
$this->buffer .= "\\";
|
||||
}
|
||||
|
||||
@ -145,6 +152,10 @@ class Tokenizer
|
||||
$this->buffer .= "{{{";
|
||||
$i += 2;
|
||||
continue;
|
||||
} elseif ($this->tagChange($this->otag. self::T_TRIM, $text, $i) and !$this->escaped) {
|
||||
$this->flushBuffer();
|
||||
$this->state = self::IN_TAG_TYPE;
|
||||
$this->trimLeft = true;
|
||||
} elseif ($this->tagChange($this->otag, $text, $i) and !$this->escaped) {
|
||||
$i--;
|
||||
$this->flushBuffer();
|
||||
@ -184,6 +195,10 @@ class Tokenizer
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($this->tagChange(self::T_TRIM . $this->ctag, $text, $i)) {
|
||||
$this->trimRight = true;
|
||||
continue;
|
||||
}
|
||||
if ($this->tagChange($this->ctag, $text, $i)) {
|
||||
// Sections (Helpers) can accept parameters
|
||||
// Same thing for Partials (little known fact)
|
||||
@ -206,6 +221,8 @@ class Tokenizer
|
||||
self::INDEX => ($this->tagType == self::T_END_SECTION) ?
|
||||
$this->seenTag - strlen($this->otag) :
|
||||
$i + strlen($this->ctag),
|
||||
self::TRIM_LEFT => $this->trimLeft,
|
||||
self::TRIM_RIGHT => $this->trimRight
|
||||
);
|
||||
if (isset($args)) {
|
||||
$t[self::ARGS] = $args;
|
||||
@ -214,6 +231,8 @@ class Tokenizer
|
||||
unset($t);
|
||||
unset($args);
|
||||
$this->buffer = '';
|
||||
$this->trimLeft = false;
|
||||
$this->trimRight = false;
|
||||
$i += strlen($this->ctag) - 1;
|
||||
$this->state = self::IN_TEXT;
|
||||
if ($this->tagType == self::T_UNESCAPED) {
|
||||
@ -262,6 +281,8 @@ class Tokenizer
|
||||
$this->lineStart = 0;
|
||||
$this->otag = '{{';
|
||||
$this->ctag = '}}';
|
||||
$this->trimLeft = false;
|
||||
$this->trimRight = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -337,7 +358,7 @@ class Tokenizer
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the current Mustache delimiters. Set new `otag` and `ctag` values.
|
||||
* Change the current Handlebars delimiters. Set new `otag` and `ctag` values.
|
||||
*
|
||||
* @param string $text Mustache template source
|
||||
* @param int $index Current tokenizer index
|
||||
@ -364,7 +385,7 @@ class Tokenizer
|
||||
* Test whether it's time to change tags.
|
||||
*
|
||||
* @param string $tag Current tag name
|
||||
* @param string $text Mustache template source
|
||||
* @param string $text Handlebars template source
|
||||
* @param int $index Current tokenizer index
|
||||
*
|
||||
* @return boolean True if this is a closing section tag
|
||||
|
@ -283,8 +283,32 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase
|
||||
'{{#if 0}}ok{{else}}fail{{/if}}',
|
||||
array(),
|
||||
'fail'
|
||||
)
|
||||
),
|
||||
array (
|
||||
' {{~#if 1}}OK {{~else~}} NO {{~/if~}} END',
|
||||
array(),
|
||||
'OKEND'
|
||||
),
|
||||
array(
|
||||
'XX {{~#bindAttr data}} XX',
|
||||
array(),
|
||||
'XXdata XX'
|
||||
),
|
||||
array(
|
||||
'{{#each data}}{{#if @last}}the last is
|
||||
{{~this}}{{/if}}{{/each}}',
|
||||
array('data' => array('one', 'two', 'three')),
|
||||
'the last isthree'
|
||||
),
|
||||
array(
|
||||
'{{#with data}}
|
||||
|
||||
{{~key~}}
|
||||
|
||||
{{/with}}',
|
||||
array('data' => array('key' => 'result')),
|
||||
'result'
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user