diff --git a/bin/build_bootstrap.php b/bin/build_bootstrap.php index f9fa2af..7863efc 100755 --- a/bin/build_bootstrap.php +++ b/bin/build_bootstrap.php @@ -20,13 +20,13 @@ * containing all Mustache library classes. This file can then be included in * your project, rather than requiring the Mustache Autoloader. */ -$baseDir = realpath(dirname(__FILE__).'/..'); +$baseDir = realpath(dirname(__FILE__) . '/..'); -require $baseDir.'/src/Mustache/Autoloader.php'; +require $baseDir . '/src/Mustache/Autoloader.php'; Mustache_Autoloader::register(); // delete the old file -$file = $baseDir.'/mustache.php'; +$file = $baseDir . '/mustache.php'; if (file_exists($file)) { unlink($file); } @@ -77,7 +77,7 @@ SymfonyClassCollectionLoader::load(array( */ class SymfonyClassCollectionLoader { - static private $loaded; + private static $loaded; const HEADER = <<\s*$/'), '', file_get_contents($r->getFileName())); } - $cache = $cacheDir.'/'.$name.$extension; + $cache = $cacheDir . '/' . $name . $extension; $header = sprintf(self::HEADER, strftime('%Y')); - self::writeCacheFile($cache, $header . substr(self::stripComments(' 1) { +if (count($argv) > 1) { // get the name of the example $example_name = $argv[1]; diff --git a/src/Mustache/Autoloader.php b/src/Mustache/Autoloader.php index 04b9e6f..8c8e906 100644 --- a/src/Mustache/Autoloader.php +++ b/src/Mustache/Autoloader.php @@ -14,7 +14,6 @@ */ class Mustache_Autoloader { - private $baseDir; /** diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index 06af266..c6fa24d 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -16,7 +16,6 @@ */ class Mustache_Compiler { - private $sections; private $source; private $indentNextLine; diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index 1966d25..fe25692 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -134,6 +134,9 @@ class Mustache_Context { for ($i = count($stack) - 1; $i >= 0; $i--) { if (is_object($stack[$i]) && !($stack[$i] instanceof Closure)) { + + // Note that is_callable() *will not work here* + // See https://github.com/bobthecow/mustache.php/wiki/Magic-Methods if (method_exists($stack[$i], $id)) { return $stack[$i]->$id(); } elseif (isset($stack[$i]->$id)) { diff --git a/src/Mustache/Engine.php b/src/Mustache/Engine.php index b395336..01a1a30 100644 --- a/src/Mustache/Engine.php +++ b/src/Mustache/Engine.php @@ -23,7 +23,7 @@ */ class Mustache_Engine { - const VERSION = '2.6.0'; + const VERSION = '2.6.1'; const SPEC_VERSION = '1.1.2'; const PRAGMA_FILTERS = 'FILTERS'; @@ -213,7 +213,7 @@ class Mustache_Engine */ public function getEntityFlags() { - return $this->entityFlags; + return $this->entityFlags; } /** diff --git a/src/Mustache/HelperCollection.php b/src/Mustache/HelperCollection.php index bbeb277..c7c7950 100644 --- a/src/Mustache/HelperCollection.php +++ b/src/Mustache/HelperCollection.php @@ -27,14 +27,16 @@ class Mustache_HelperCollection */ public function __construct($helpers = null) { - if ($helpers !== null) { - if (!is_array($helpers) && !$helpers instanceof Traversable) { - throw new Mustache_Exception_InvalidArgumentException('HelperCollection constructor expects an array of helpers'); - } + if ($helpers === null) { + return; + } - foreach ($helpers as $name => $helper) { - $this->add($name, $helper); - } + if (!is_array($helpers) && !$helpers instanceof Traversable) { + throw new Mustache_Exception_InvalidArgumentException('HelperCollection constructor expects an array of helpers'); + } + + foreach ($helpers as $name => $helper) { + $this->add($name, $helper); } } diff --git a/src/Mustache/Loader.php b/src/Mustache/Loader.php index 9378c2c..e75ee3f 100644 --- a/src/Mustache/Loader.php +++ b/src/Mustache/Loader.php @@ -14,7 +14,6 @@ */ interface Mustache_Loader { - /** * Load a Template by name. * diff --git a/src/Mustache/Loader/MutableLoader.php b/src/Mustache/Loader/MutableLoader.php index f59f95d..78901be 100644 --- a/src/Mustache/Loader/MutableLoader.php +++ b/src/Mustache/Loader/MutableLoader.php @@ -14,7 +14,6 @@ */ interface Mustache_Loader_MutableLoader { - /** * Set an associative array of Template sources for this loader. * diff --git a/src/Mustache/Loader/StringLoader.php b/src/Mustache/Loader/StringLoader.php index 5570cda..72d105d 100644 --- a/src/Mustache/Loader/StringLoader.php +++ b/src/Mustache/Loader/StringLoader.php @@ -25,7 +25,6 @@ */ class Mustache_Loader_StringLoader implements Mustache_Loader { - /** * Load a Template by source. * diff --git a/src/Mustache/Template.php b/src/Mustache/Template.php index c9621c8..35d652c 100644 --- a/src/Mustache/Template.php +++ b/src/Mustache/Template.php @@ -16,7 +16,6 @@ */ abstract class Mustache_Template { - /** * @var Mustache_Engine */ diff --git a/src/Mustache/Tokenizer.php b/src/Mustache/Tokenizer.php index 4b5a33c..9a9afab 100644 --- a/src/Mustache/Tokenizer.php +++ b/src/Mustache/Tokenizer.php @@ -16,7 +16,6 @@ */ class Mustache_Tokenizer { - // Finite state machine states const IN_TEXT = 0; const IN_TAG_TYPE = 1; @@ -85,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) * @@ -117,7 +118,7 @@ class Mustache_Tokenizer } else { $char = $text[$i]; $this->buffer .= $char; - if ($char == "\n") { + if ($char === "\n") { $this->flushBuffer(); $this->line++; } @@ -152,29 +153,49 @@ 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, self::CTAG => $this->ctag, self::LINE => $this->line, - self::INDEX => ($this->tagType == self::T_END_SECTION) ? $this->seenTag - $this->otagLen : $i + $this->ctagLen + self::INDEX => ($this->tagType === self::T_END_SECTION) ? $this->seenTag - $this->otagLen : $i + $this->ctagLen ); + if ($this->tagType === self::T_UNESCAPED) { + // Clean up `{{{ tripleStache }}}` style tokens. + if ($this->ctag === '}}') { + if (($i + 2 < $len) && $text[$i + 2] === '}') { + $i++; + } else { + $msg = sprintf( + 'Mismatched tag delimiters: %s on line %d', + $token[self::NAME], + $token[self::LINE] + ); + + throw new Mustache_Exception_SyntaxException($msg, $token); + } + } else { + $lastName = $token[self::NAME]; + if (substr($lastName, -1) === '}') { + $token[self::NAME] = trim(substr($lastName, 0, -1)); + } else { + $msg = sprintf( + 'Mismatched tag delimiters: %s on line %d', + $token[self::NAME], + $token[self::LINE] + ); + + throw new Mustache_Exception_SyntaxException($msg, $token); + } + } + } + $this->buffer = ''; $i += $this->ctagLen - 1; $this->state = self::IN_TEXT; - if ($this->tagType == self::T_UNESCAPED) { - if ($this->ctag == '}}') { - $i++; - } else { - // Clean up `{{{ tripleStache }}}` style tokens. - $lastName = $this->tokens[count($this->tokens) - 1][self::NAME]; - if (substr($lastName, -1) === '}') { - $this->tokens[count($this->tokens) - 1][self::NAME] = trim(substr($lastName, 0, -1)); - } - } - } + $this->tokens[] = $token; } else { $this->buffer .= $text[$i]; } diff --git a/test/Mustache/Test/Cache/FilesystemCacheTest.php b/test/Mustache/Test/Cache/FilesystemCacheTest.php index 5bfb18c..8256525 100644 --- a/test/Mustache/Test/Cache/FilesystemCacheTest.php +++ b/test/Mustache/Test/Cache/FilesystemCacheTest.php @@ -17,7 +17,7 @@ class Mustache_Test_Cache_FilesystemCacheTest extends Mustache_Test_FunctionalTe public function testCacheGetNone() { $key = 'some key'; - $cache = new Mustache_Cache_FilesystemCache(self::$tempDir);; + $cache = new Mustache_Cache_FilesystemCache(self::$tempDir); $loaded = $cache->load($key); $this->assertFalse($loaded); @@ -27,7 +27,7 @@ class Mustache_Test_Cache_FilesystemCacheTest extends Mustache_Test_FunctionalTe { $key = 'some key'; $value = 'cache($key, $value); $loaded = $cache->load($key); diff --git a/test/Mustache/Test/FiveThree/Functional/FiltersTest.php b/test/Mustache/Test/FiveThree/Functional/FiltersTest.php index a55a1b6..cd5deb7 100644 --- a/test/Mustache/Test/FiveThree/Functional/FiltersTest.php +++ b/test/Mustache/Test/FiveThree/Functional/FiltersTest.php @@ -46,7 +46,7 @@ class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_T array( '{{% FILTERS }}{{ date | longdate }}', $helpers, - (object) array('date' => new DateTime('1/1/2000')), + (object) array('date' => new DateTime('1/1/2000', new DateTimeZone("UTC"))), '2000-01-01 12:01:00' ), @@ -72,7 +72,7 @@ class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_T }); $foo = new \StdClass; - $foo->date = new DateTime('1/1/2000'); + $foo->date = new DateTime('1/1/2000', new DateTimeZone("UTC")); $this->assertEquals('[[2000-01-01 12:01:00]]', $tpl->render($foo)); } diff --git a/test/Mustache/Test/FiveThree/Functional/PartialLambdaIndentTest.php b/test/Mustache/Test/FiveThree/Functional/PartialLambdaIndentTest.php index ecc5e23..04fd56d 100644 --- a/test/Mustache/Test/FiveThree/Functional/PartialLambdaIndentTest.php +++ b/test/Mustache/Test/FiveThree/Functional/PartialLambdaIndentTest.php @@ -42,12 +42,12 @@ EOS; $tpl = $m->loadTemplate($src); - $data = new Mustache_Test_Functional_ClassWithLambda(); + $data = new Mustache_Test_FiveThree_Functional_ClassWithLambda(); $this->assertEquals($expected, $tpl->render($data)); } } -class Mustache_Test_Functional_ClassWithLambda +class Mustache_Test_FiveThree_Functional_ClassWithLambda { public function _t() { diff --git a/test/Mustache/Test/TokenizerTest.php b/test/Mustache/Test/TokenizerTest.php index 42df8e1..67df941 100644 --- a/test/Mustache/Test/TokenizerTest.php +++ b/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, + ) + ) + ), ); } } diff --git a/test/bootstrap.php b/test/bootstrap.php index 03a119f..99b6fcf 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -9,8 +9,8 @@ * file that was distributed with this source code. */ -require dirname(__FILE__).'/../src/Mustache/Autoloader.php'; +require dirname(__FILE__) . '/../src/Mustache/Autoloader.php'; Mustache_Autoloader::register(); -Mustache_Autoloader::register(dirname(__FILE__).'/../test'); +Mustache_Autoloader::register(dirname(__FILE__) . '/../test'); -require dirname(__FILE__).'/../vendor/yaml/lib/sfYamlParser.php'; +require dirname(__FILE__) . '/../vendor/yaml/lib/sfYamlParser.php';