ソースを参照

Merge pull request #209 from smarden1/pr/even_triple_braces

Uneven braces in triples are incorrectly swallowing a character [repost of 208]
Justin Hileman 11 年 前
コミット
e8897b1228
2 ファイル変更114 行追加8 行削除
  1. 29 8
      src/Mustache/Tokenizer.php
  2. 85 0
      test/Mustache/Test/TokenizerTest.php

+ 29 - 8
src/Mustache/Tokenizer.php

@@ -84,6 +84,8 @@ class Mustache_Tokenizer
     /**
      * Scan and tokenize template source.
      *
+     * @throws Mustache_Exception_SyntaxException when mismatched section tags are encountered.
+     *
      * @param string $text       Mustache template source to tokenize
      * @param string $delimiters Optionally, pass initial opening and closing delimiters (default: null)
      *
@@ -151,7 +153,7 @@ class Mustache_Tokenizer
 
                 default:
                     if ($this->tagChange($this->ctag, $this->ctagLen, $text, $i)) {
-                        $this->tokens[] = array(
+                        $token = array(
                             self::TYPE  => $this->tagType,
                             self::NAME  => trim($this->buffer),
                             self::OTAG  => $this->otag,
@@ -160,20 +162,39 @@ class Mustache_Tokenizer
                             self::INDEX => ($this->tagType === self::T_END_SECTION) ? $this->seenTag - $this->otagLen : $i + $this->ctagLen
                         );
 
-                        $this->buffer = '';
-                        $i += $this->ctagLen - 1;
-                        $this->state = self::IN_TEXT;
                         if ($this->tagType === self::T_UNESCAPED) {
+                            // Clean up `{{{ tripleStache }}}` style tokens.
                             if ($this->ctag === '}}') {
-                                $i++;
+                                if (($i+2 < $len) && $text[$i + 2] === '}') {
+                                    $i++;
+                                } else {
+                                    $msg = sprintf(
+                                        'Uneven closing tag encountered: on line %d',
+                                        $token[self::LINE]
+                                    );
+
+                                    throw new Mustache_Exception_SyntaxException($msg, $token);
+                                }
                             } else {
-                                // Clean up `{{{ tripleStache }}}` style tokens.
-                                $lastName = $this->tokens[count($this->tokens) - 1][self::NAME];
+                                $lastName = $token[self::NAME];
                                 if (substr($lastName, -1) === '}') {
-                                    $this->tokens[count($this->tokens) - 1][self::NAME] = trim(substr($lastName, 0, -1));
+                                    $token[self::NAME] = trim(substr($lastName, 0, -1));
+                                } else {
+                                    $msg = sprintf(
+                                        'Uneven closing tag encountered: %s on line %d',
+                                        substr($lastName, -1),
+                                        $token[self::LINE]
+                                    );
+
+                                    throw new Mustache_Exception_SyntaxException($msg, $token);
                                 }
                             }
                         }
+
+                        $this->buffer = '';
+                        $i += $this->ctagLen - 1;
+                        $this->state = self::IN_TEXT;
+                        $this->tokens[] = $token;
                     } else {
                         $this->buffer .= $text[$i];
                     }

+ 85 - 0
test/Mustache/Test/TokenizerTest.php

@@ -24,6 +24,28 @@ class Mustache_Test_TokenizerTest extends PHPUnit_Framework_TestCase
         $this->assertSame($expected, $tokenizer->scan($text, $delimiters));
     }
 
+    /**
+     * @expectedException Mustache_Exception_SyntaxException
+     */
+    public function testUnevenBracesThrowExceptions()
+    {
+        $tokenizer = new Mustache_Tokenizer;
+
+        $text = "{{{ name }}";
+        $tokenizer->scan($text, null);
+    }
+
+    /**
+     * @expectedException Mustache_Exception_SyntaxException
+     */
+    public function testUnevenBracesWithCustomDelimiterThrowExceptions()
+    {
+        $tokenizer = new Mustache_Tokenizer;
+
+        $text = "<%{ name %>";
+        $tokenizer->scan($text, "<% %>");
+    }
+
     public function getTokens()
     {
         return array(
@@ -188,6 +210,69 @@ class Mustache_Test_TokenizerTest extends PHPUnit_Framework_TestCase
                     ),
                 )
             ),
+
+            // custom delimiters don't swallow the next character, even if it is a }, }}}, or the same delimiter
+            array(
+                "<% a %>} <% b %>%> <% c %>}}}",
+                "<% %>",
+                array(
+                    array(
+                        Mustache_Tokenizer::TYPE  => Mustache_Tokenizer::T_ESCAPED,
+                        Mustache_Tokenizer::NAME  => 'a',
+                        Mustache_Tokenizer::OTAG  => '<%',
+                        Mustache_Tokenizer::CTAG  => '%>',
+                        Mustache_Tokenizer::LINE  => 0,
+                        Mustache_Tokenizer::INDEX => 7,
+                    ),
+                    array(
+                        Mustache_Tokenizer::TYPE  => Mustache_Tokenizer::T_TEXT,
+                        Mustache_Tokenizer::LINE  => 0,
+                        Mustache_Tokenizer::VALUE => "} ",
+                    ),
+                    array(
+                        Mustache_Tokenizer::TYPE  => Mustache_Tokenizer::T_ESCAPED,
+                        Mustache_Tokenizer::NAME  => 'b',
+                        Mustache_Tokenizer::OTAG  => '<%',
+                        Mustache_Tokenizer::CTAG  => '%>',
+                        Mustache_Tokenizer::LINE  => 0,
+                        Mustache_Tokenizer::INDEX => 16,
+                    ),
+                    array(
+                        Mustache_Tokenizer::TYPE  => Mustache_Tokenizer::T_TEXT,
+                        Mustache_Tokenizer::LINE  => 0,
+                        Mustache_Tokenizer::VALUE => "%> ",
+                    ),
+                    array(
+                        Mustache_Tokenizer::TYPE  => Mustache_Tokenizer::T_ESCAPED,
+                        Mustache_Tokenizer::NAME  => 'c',
+                        Mustache_Tokenizer::OTAG  => '<%',
+                        Mustache_Tokenizer::CTAG  => '%>',
+                        Mustache_Tokenizer::LINE  => 0,
+                        Mustache_Tokenizer::INDEX => 26,
+                    ),
+                    array(
+                        Mustache_Tokenizer::TYPE  => Mustache_Tokenizer::T_TEXT,
+                        Mustache_Tokenizer::LINE  => 0,
+                        Mustache_Tokenizer::VALUE => "}}}",
+                    ),
+                )
+            ),
+
+            // unescaped custom delimiters are properly parsed
+            array(
+                "<%{ a }%>",
+                "<% %>",
+                array(
+                    array(
+                        Mustache_Tokenizer::TYPE  => Mustache_Tokenizer::T_UNESCAPED,
+                        Mustache_Tokenizer::NAME  => 'a',
+                        Mustache_Tokenizer::OTAG  => '<%',
+                        Mustache_Tokenizer::CTAG  => '%>',
+                        Mustache_Tokenizer::LINE  => 0,
+                        Mustache_Tokenizer::INDEX => 9,
+                    )
+                )
+            ),
         );
     }
 }