From db14a6c8633f1686a81c2c5dcc1890c656ec754e Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 4 Apr 2014 18:29:26 +0000 Subject: [PATCH 01/43] hogan.js style template inheritance --- src/Mustache/Compiler.php | 127 +++++++- src/Mustache/Context.php | 12 + src/Mustache/Parser.php | 16 +- src/Mustache/Tokenizer.php | 7 +- .../Test/Functional/InheritanceTest.php | 303 ++++++++++++++++++ .../Test/Functional/MustacheSpecTest.php | 15 + test/Mustache/Test/ParserTest.php | 113 +++++++ test/Mustache/Test/TokenizerTest.php | 29 ++ 8 files changed, 614 insertions(+), 8 deletions(-) create mode 100644 test/Mustache/Test/Functional/InheritanceTest.php diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index c13e60f..d7daa95 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -17,6 +17,7 @@ class Mustache_Compiler { + private $pragmas; private $sections; private $source; private $indentNextLine; @@ -24,7 +25,6 @@ class Mustache_Compiler private $entityFlags; private $charset; private $strictCallables; - private $pragmas; /** * Compile a Mustache token parse tree into PHP source code. @@ -94,7 +94,6 @@ class Mustache_Compiler break; case Mustache_Tokenizer::T_PARTIAL: - case Mustache_Tokenizer::T_PARTIAL_2: $code .= $this->partial( $node[Mustache_Tokenizer::NAME], isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '', @@ -102,6 +101,39 @@ class Mustache_Compiler ); break; + case Mustache_Tokenizer::T_PARENT: + $code .= $this->parent( + $node[Mustache_Tokenizer::NAME], + isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '', + $level, + $node['nodes'] + ); + break; + + case Mustache_Tokenizer::T_PARENT_ARG: + $code .= $this->parentArg( + $node[Mustache_Tokenizer::NODES], + $node[Mustache_Tokenizer::NAME], + $node[Mustache_Tokenizer::INDEX], + $node[Mustache_Tokenizer::END], + $node[Mustache_Tokenizer::OTAG], + $node[Mustache_Tokenizer::CTAG], + $level + ); + break; + + case Mustache_Tokenizer::T_PARENT_VAR: + $code .= $this->parentVar( + $node[Mustache_Tokenizer::NODES], + $node[Mustache_Tokenizer::NAME], + $node[Mustache_Tokenizer::INDEX], + $node[Mustache_Tokenizer::END], + $node[Mustache_Tokenizer::OTAG], + $node[Mustache_Tokenizer::CTAG], + $level + ); + break; + case Mustache_Tokenizer::T_UNESCAPED: case Mustache_Tokenizer::T_UNESCAPED_2: $code .= $this->variable($node[Mustache_Tokenizer::NAME], false, $level); @@ -136,6 +168,7 @@ class Mustache_Compiler { $this->lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context); $buffer = \'\'; + $new_context = array(); %s return $buffer; @@ -150,6 +183,7 @@ class Mustache_Compiler public function renderInternal(Mustache_Context $context, $indent = \'\') { $buffer = \'\'; + $new_context = array(); %s return $buffer; @@ -171,11 +205,71 @@ class Mustache_Compiler $code = $this->walk($tree); $sections = implode("\n", $this->sections); $klass = empty($this->sections) ? self::KLASS_NO_LAMBDAS : self::KLASS; + $callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : ''; return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections); } + const PARENT_VAR = ' + $value = $this->resolveValue($context->%s(%s), $context, $indent); + if($value && !is_array($value) && !is_object($value)) { + $buffer .= %s; + } else { + %s + } + '; + + private function parentVar($nodes, $id, $start, $end, $otag, $ctag, $level) + { + $method = 'findFromParent'; + $id_str = var_export($id, true); + $value = $this->getEscape(); + + return sprintf($this->prepare(self::PARENT_VAR, $level), $method, $id_str, $value, $this->walk($nodes, 2)); + } + + function parentArgSection($nodes, $start, $end, $otag, $ctag, $level) + { + $source = var_export(substr($this->source, $start, $end - $start), true); + $callable = $this->getCallable(); + + if ($otag !== '{{' || $ctag !== '}}') { + $delims = ', '.var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true); + } else { + $delims = ''; + } + + $key = ucfirst(md5($delims."\n".$source)); + + if (!isset($this->sections[$key])) { + $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $delims, $this->walk($nodes, 2)); + } + + return $key; + } + + const PARENT_ARG = ' + // %s parent_arg + $value = $this->section%s($context, $indent, true); + $new_context[%s] = %s$value; + '; + + private function parentArg($nodes, $id, $start, $end, $otag, $ctag, $level) + { + $key = $this->parentArgSection($nodes, $start, $end, $otag, $ctag, $level); + $filters = ''; + + if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { + list($id, $filters) = $this->getFilters($id, $level); + } + + $method = $this->getFindMethod($id); + $id = var_export($id, true); + + return sprintf($this->prepare(self::PARENT_ARG, $level), $key, $key, $id, $this->flushIndent()); + } + const SECTION_CALL = ' // %s section $value = $context->%s(%s);%s @@ -199,7 +293,8 @@ class Mustache_Compiler } elseif (!empty($value)) { $values = $this->isIterable($value) ? $value : array($value); foreach ($values as $value) { - $context->push($value);%s + $context->push($value); + %s $context->pop(); } } @@ -302,6 +397,32 @@ class Mustache_Compiler ); } + const PARENT = ' + + if ($parent = $this->mustache->LoadPartial(%s)) { + $context->push($new_context); + $buffer .= $parent->renderInternal($context, $indent); + $context->pop(); + } + '; + + private function parent($id, $indent, $level, $children) + { + $block = ''; + + $real_children = array_filter($children, function($ch) { + return $ch[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_PARENT_ARG; + }); + + $block = $this->walk($real_children, $level); + + return $block. sprintf( + $this->prepare(self::PARENT, $level), + var_export($id, true), + var_export($indent, true) + ); + } + const VARIABLE = ' $value = $this->resolveValue($context->%s(%s), $context, $indent);%s $buffer .= %s%s; diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index c6900d7..fbb22e0 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -120,6 +120,18 @@ class Mustache_Context return $value; } + public function findFromParent($id) + { + $stack = $this->stack; + foreach($stack as $context) { + if (is_array($context) && array_key_exists($id, $context)) { + return $context[$id]; + } + } + + return ''; + } + /** * Helper function to find a variable in the Context stack. * diff --git a/src/Mustache/Parser.php b/src/Mustache/Parser.php index f44153c..83f71f1 100644 --- a/src/Mustache/Parser.php +++ b/src/Mustache/Parser.php @@ -95,17 +95,27 @@ class Mustache_Parser $parent[Mustache_Tokenizer::NODES] = $nodes; return $parent; - break; case Mustache_Tokenizer::T_PARTIAL: - case Mustache_Tokenizer::T_PARTIAL_2: - // store the whitespace prefix for laters! + //store the whitespace prefix for laters! if ($indent = $this->clearStandaloneLines($nodes, $tokens)) { $token[Mustache_Tokenizer::INDENT] = $indent[Mustache_Tokenizer::VALUE]; } $nodes[] = $token; break; + case Mustache_Tokenizer::T_PARENT: + $nodes[] = $this->buildTree($tokens, $token); + break; + + case Mustache_Tokenizer::T_PARENT_VAR: + if ($parent[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_PARENT) { + $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_PARENT_ARG; + } + $this->clearStandaloneLines($nodes, $tokens); + $nodes[] = $this->buildTree($tokens, $token); + break; + case Mustache_Tokenizer::T_PRAGMA: case Mustache_Tokenizer::T_COMMENT: $this->clearStandaloneLines($nodes, $tokens); diff --git a/src/Mustache/Tokenizer.php b/src/Mustache/Tokenizer.php index c701b25..1dac502 100644 --- a/src/Mustache/Tokenizer.php +++ b/src/Mustache/Tokenizer.php @@ -28,13 +28,15 @@ class Mustache_Tokenizer const T_END_SECTION = '/'; const T_COMMENT = '!'; const T_PARTIAL = '>'; - const T_PARTIAL_2 = '<'; + const T_PARENT = '<'; const T_DELIM_CHANGE = '='; const T_ESCAPED = '_v'; const T_UNESCAPED = '{'; const T_UNESCAPED_2 = '&'; const T_TEXT = '_t'; const T_PRAGMA = '%'; + const T_PARENT_VAR = '$'; + const T_PARENT_ARG = '$arg'; // Valid token types private static $tagTypes = array( @@ -43,12 +45,13 @@ class Mustache_Tokenizer self::T_END_SECTION => true, self::T_COMMENT => true, self::T_PARTIAL => true, - self::T_PARTIAL_2 => true, + self::T_PARENT => true, self::T_DELIM_CHANGE => true, self::T_ESCAPED => true, self::T_UNESCAPED => true, self::T_UNESCAPED_2 => true, self::T_PRAGMA => true, + self::T_PARENT_VAR => true, ); // Interpolated tags diff --git a/test/Mustache/Test/Functional/InheritanceTest.php b/test/Mustache/Test/Functional/InheritanceTest.php new file mode 100644 index 0000000..799246b --- /dev/null +++ b/test/Mustache/Test/Functional/InheritanceTest.php @@ -0,0 +1,303 @@ +mustache = new Mustache_Engine; + } + + public function testDefaultContent() + { + $tpl = $this->mustache->loadTemplate('{{$title}}Default title{{/title}}'); + + $data = array(); + + $this->assertEquals('Default title', $tpl->render($data)); + } + + public function testDefaultContentRendersVariables() + { + $tpl = $this->mustache->loadTemplate('{{$foo}}default {{bar}} content{{/foo}}'); + + $data = array( + 'bar' => 'baz' + ); + + $this->assertEquals('default baz content', $tpl->render($data)); + } + + public function testDefaultContentRendersTripleMustacheVariables() + { + $tpl = $this->mustache->loadTemplate('{{$foo}}default {{{bar}}} content{{/foo}}'); + + $data = array( + 'bar' => '' + ); + + $this->assertEquals('default content', $tpl->render($data)); + } + + public function testDefaultContentRendersSections() + { + $tpl = $this->mustache->loadTemplate( + '{{$foo}}default {{#bar}}{{baz}}{{/bar}} content{{/foo}}' + ); + + $data = array( + 'bar' => array('baz' => 'qux') + ); + + $this->assertEquals('default qux content', $tpl->render($data)); + } + + public function testDefaultContentRendersNegativeSections() + { + $tpl = $this->mustache->loadTemplate( + '{{$foo}}default {{^bar}}{{baz}}{{/bar}} content{{/foo}}' + ); + + $data = array( + 'foo' => array('bar' => 'qux'), + 'baz' => 'three' + ); + + $this->assertEquals('default three content', $tpl->render($data)); + + } + + public function testMustacheInjectionInDefaultContent() + { + $tpl = $this->mustache->loadTemplate( + '{{$foo}}default {{#bar}}{{baz}}{{/bar}} content{{/foo}}' + ); + + $data = array( + 'bar' => array('baz' => '{{qux}}') + ); + + $this->assertEquals('default {{qux}} content', $tpl->render($data)); + } + + public function testDefaultContentRenderedInsideIncludedTemplates() + { + $partials = array( + 'include' => '{{$foo}}default content{{/foo}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{assertEquals('default content', $tpl->render($data)); + } + + public function testOverriddenContent() + { + $partials = array( + 'super' => '...{{$title}}Default title{{/title}}...' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{assertEquals('...sub template title...', $tpl->render($data)); + } + + public function testOverriddenPartial() + { + $partials = array( + 'partial' => '|{{$stuff}}...{{/stuff}}{{$default}} default{{/default}}|' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + 'test {{assertEquals('test |override1 default| |override2 default|', $tpl->render($data)); + } + + public function testOverridePartialWithNewlines() + { + $partials = array( + 'partial' => '{{$ballmer}}peaking{{/ballmer}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + "{{assertEquals("peaked\n\n:(\n", $tpl->render($data)); + } + + public function testInheritIndentationWhenOverridingAPartial() + { + $partials = array( + 'partial' => + 'stop: + {{$nineties}}collaborate and listen{{/nineties}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{assertEquals( + 'stop: + hammer time', + $tpl->render($data) + ); + } + + public function testOverrideOneSubstitutionButNotTheOther() + { + $partials = array( + 'partial' => '{{$stuff}}default one{{/stuff}}, {{$stuff2}}default two{{/stuff2}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{assertEquals('default one, override two', $tpl->render($data)); + } + + public function testSuperTemplatesWithNoParameters() + { + $partials = array( + 'include' => '{{$foo}}default content{{/foo}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{>include}}|{{assertEquals('default content|default content', $tpl->render($data)); + } + + public function testRecursionInInheritedTemplates() + { + $partials = array( + 'include' => '{{$foo}}default content{{/foo}} {{$bar}}{{ '{{$foo}}include2 default content{{/foo}} {{mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{assertEquals('override override override don\'t recurse', $tpl->render($data)); + } + + public function testTopLevelSubstitutionsTakePrecedenceInMultilevelInheritance() + { + $partials = array( + 'parent' => '{{ '{{ '{{$a}}g{{/a}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{assertEquals('c', $tpl->render($data)); + } + + public function testMultiLevelInheritanceNoSubChild() + { + $partials = array( + 'parent' => '{{ '{{ '{{$a}}g{{/a}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{assertEquals('p', $tpl->render($data)); + } + + public function testIgnoreTextInsideSuperTemplatesButParseArgs() + { + $partials = array( + 'include' => '{{$foo}}default content{{/foo}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{assertEquals('hmm', $tpl->render($data)); + } + + public function IgnoreTextInsideSuperTemplates() + { + $partials = array( + 'include' => '{{$foo}}default content{{/foo}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{assertEquals('default content', $tpl->render($data)); + } +} diff --git a/test/Mustache/Test/Functional/MustacheSpecTest.php b/test/Mustache/Test/Functional/MustacheSpecTest.php index 312a4c9..c128278 100644 --- a/test/Mustache/Test/Functional/MustacheSpecTest.php +++ b/test/Mustache/Test/Functional/MustacheSpecTest.php @@ -127,6 +127,21 @@ class Mustache_Test_Functional_MustacheSpecTest extends PHPUnit_Framework_TestCa return $this->loadSpec('sections'); } + /** + * @group inheritance + * @dataProvider loadInheritanceSpec + */ + public function testInheritanceSpec($desc, $source, $partials, $data, $expected) + { + $template = self::loadTemplate($source, $partials); + $this->assertEquals($expected, $template->render($data), $desc); + } + + public function loadInheritanceSpec() + { + return $this->loadSpec('inheritance'); + } + /** * Data provider for the mustache spec test. * diff --git a/test/Mustache/Test/ParserTest.php b/test/Mustache/Test/ParserTest.php index 57e418a..b780dca 100644 --- a/test/Mustache/Test/ParserTest.php +++ b/test/Mustache/Test/ParserTest.php @@ -88,6 +88,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase Mustache_Tokenizer::VALUE => 'bar' ), ), + array( array( Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_TEXT, @@ -116,6 +117,118 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase ), ), + array( + array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::INDEX => 8 + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::NAME => 'bar', + 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 => 'baz' + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_END_SECTION, + Mustache_Tokenizer::NAME => 'bar', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::INDEX => 19 + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_END_SECTION, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::INDEX => 27 + ) + ), + array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::INDEX => 8, + Mustache_Tokenizer::END => 27, + Mustache_Tokenizer::NODES => array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_ARG, + Mustache_Tokenizer::NAME => 'bar', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::INDEX => 16, + Mustache_Tokenizer::END => 19, + Mustache_Tokenizer::NODES => array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_TEXT, + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::VALUE => 'baz' + ) + ) + ) + ) + ) + ) + ), + + array( + array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_TEXT, + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::VALUE => 'bar' + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_END_SECTION, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::INDEX => 11, + ), + ), + array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::END => 11, + Mustache_Tokenizer::NODES => array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_TEXT, + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::VALUE => 'bar' + ) + ) + ) + ) + ) ); } diff --git a/test/Mustache/Test/TokenizerTest.php b/test/Mustache/Test/TokenizerTest.php index 63a94d4..dab379d 100644 --- a/test/Mustache/Test/TokenizerTest.php +++ b/test/Mustache/Test/TokenizerTest.php @@ -188,6 +188,35 @@ class Mustache_Test_TokenizerTest extends PHPUnit_Framework_TestCase ), ) ), + + // Ensure that $arg token is not picked up during tokenization + array( + '{{$arg}}default{{/arg}}', + null, + array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::NAME => 'arg', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::INDEX => 8 + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_TEXT, + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::VALUE => "default", + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_END_SECTION, + Mustache_Tokenizer::NAME => 'arg', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::INDEX => 15, + ) + ) + ), ); } } From c6c67ce609903765e0a4807b4cd556b6f895f75e Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 4 Apr 2014 19:53:06 +0000 Subject: [PATCH 02/43] referencing nodes by class constant rather than string literal --- src/Mustache/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index d7daa95..da634d3 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -106,7 +106,7 @@ class Mustache_Compiler $node[Mustache_Tokenizer::NAME], isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '', $level, - $node['nodes'] + $node[Mustache_Tokenizer::NODES] ); break; From f47a09538e4deb70da9a58bb48de2f2da0efe836 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 4 Apr 2014 20:07:50 +0000 Subject: [PATCH 03/43] php 5.2.x doesn't support closures, so added a private static funtion to be used from array_filter instead --- src/Mustache/Compiler.php | 9 ++++++--- vendor/spec | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index da634d3..f7091cc 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -410,9 +410,7 @@ class Mustache_Compiler { $block = ''; - $real_children = array_filter($children, function($ch) { - return $ch[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_PARENT_ARG; - }); + $real_children = array_filter($children, array(__CLASS__, 'return_only_parent_args')); $block = $this->walk($real_children, $level); @@ -423,6 +421,11 @@ class Mustache_Compiler ); } + private static function return_only_parent_args($child) + { + return $child[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_PARENT_ARG; + } + const VARIABLE = ' $value = $this->resolveValue($context->%s(%s), $context, $indent);%s $buffer .= %s%s; diff --git a/vendor/spec b/vendor/spec index bf6288e..7c3923d 160000 --- a/vendor/spec +++ b/vendor/spec @@ -1 +1 @@ -Subproject commit bf6288ed6bd0ce8ccea6f1dac070b3d779132c3b +Subproject commit 7c3923d0079a345136325f7d1895f5d98efa9701 From 31800de88f7b240a013cb98396a371db2ef935ed Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 4 Apr 2014 20:42:56 +0000 Subject: [PATCH 04/43] removing unnecessary local variable in findFromParent function --- src/Mustache/Context.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index fbb22e0..58461e5 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -122,8 +122,7 @@ class Mustache_Context public function findFromParent($id) { - $stack = $this->stack; - foreach($stack as $context) { + foreach($this->stack as $context) { if (is_array($context) && array_key_exists($id, $context)) { return $context[$id]; } From a631de174ca807281b3d00f0448160b34b8f38ad Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 4 Apr 2014 20:43:47 +0000 Subject: [PATCH 05/43] s/new_context/newContext/g --- src/Mustache/Compiler.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index f7091cc..a5ff392 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -168,7 +168,7 @@ class Mustache_Compiler { $this->lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context); $buffer = \'\'; - $new_context = array(); + $newContext = array(); %s return $buffer; @@ -183,7 +183,7 @@ class Mustache_Compiler public function renderInternal(Mustache_Context $context, $indent = \'\') { $buffer = \'\'; - $new_context = array(); + $newContext = array(); %s return $buffer; @@ -252,7 +252,7 @@ class Mustache_Compiler const PARENT_ARG = ' // %s parent_arg $value = $this->section%s($context, $indent, true); - $new_context[%s] = %s$value; + $newContext[%s] = %s$value; '; private function parentArg($nodes, $id, $start, $end, $otag, $ctag, $level) @@ -400,7 +400,7 @@ class Mustache_Compiler const PARENT = ' if ($parent = $this->mustache->LoadPartial(%s)) { - $context->push($new_context); + $context->push($newContext); $buffer .= $parent->renderInternal($context, $indent); $context->pop(); } From ebb7fda2e56bfba4d238c7f0f892bd02d1e7b7fc Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 4 Apr 2014 20:45:21 +0000 Subject: [PATCH 06/43] removing deprecated code for loading non-existent inheritance tests from the mustache spec --- .../Mustache/Test/Functional/MustacheSpecTest.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/test/Mustache/Test/Functional/MustacheSpecTest.php b/test/Mustache/Test/Functional/MustacheSpecTest.php index c128278..312a4c9 100644 --- a/test/Mustache/Test/Functional/MustacheSpecTest.php +++ b/test/Mustache/Test/Functional/MustacheSpecTest.php @@ -127,21 +127,6 @@ class Mustache_Test_Functional_MustacheSpecTest extends PHPUnit_Framework_TestCa return $this->loadSpec('sections'); } - /** - * @group inheritance - * @dataProvider loadInheritanceSpec - */ - public function testInheritanceSpec($desc, $source, $partials, $data, $expected) - { - $template = self::loadTemplate($source, $partials); - $this->assertEquals($expected, $template->render($data), $desc); - } - - public function loadInheritanceSpec() - { - return $this->loadSpec('inheritance'); - } - /** * Data provider for the mustache spec test. * From 3c57d5f2205e3148f7253b4cc9b750d67d8fded2 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 4 Apr 2014 20:57:07 +0000 Subject: [PATCH 07/43] reverting vendor spec change --- vendor/spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/spec b/vendor/spec index 7c3923d..bf6288e 160000 --- a/vendor/spec +++ b/vendor/spec @@ -1 +1 @@ -Subproject commit 7c3923d0079a345136325f7d1895f5d98efa9701 +Subproject commit bf6288ed6bd0ce8ccea6f1dac070b3d779132c3b From 63b835bd0963d65f00e958f37739b361ae669f82 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Mon, 7 Apr 2014 18:11:43 +0000 Subject: [PATCH 08/43] DRYing parentArgSection into the section method --- src/Mustache/Compiler.php | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index a5ff392..f0b6b71 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -229,26 +229,6 @@ class Mustache_Compiler return sprintf($this->prepare(self::PARENT_VAR, $level), $method, $id_str, $value, $this->walk($nodes, 2)); } - function parentArgSection($nodes, $start, $end, $otag, $ctag, $level) - { - $source = var_export(substr($this->source, $start, $end - $start), true); - $callable = $this->getCallable(); - - if ($otag !== '{{' || $ctag !== '}}') { - $delims = ', '.var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true); - } else { - $delims = ''; - } - - $key = ucfirst(md5($delims."\n".$source)); - - if (!isset($this->sections[$key])) { - $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $delims, $this->walk($nodes, 2)); - } - - return $key; - } - const PARENT_ARG = ' // %s parent_arg $value = $this->section%s($context, $indent, true); @@ -257,7 +237,7 @@ class Mustache_Compiler private function parentArg($nodes, $id, $start, $end, $otag, $ctag, $level) { - $key = $this->parentArgSection($nodes, $start, $end, $otag, $ctag, $level); + $key = $this->section($nodes, $id, $start, $end, $otag, $ctag, $level, true); $filters = ''; if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { @@ -315,7 +295,7 @@ class Mustache_Compiler * * @return string Generated section PHP source code */ - private function section($nodes, $id, $start, $end, $otag, $ctag, $level) + private function section($nodes, $id, $start, $end, $otag, $ctag, $level, $arg=false) { $filters = ''; @@ -323,8 +303,6 @@ class Mustache_Compiler list($id, $filters) = $this->getFilters($id, $level); } - $method = $this->getFindMethod($id); - $id = var_export($id, true); $source = var_export(substr($this->source, $start, $end - $start), true); $callable = $this->getCallable(); @@ -340,7 +318,13 @@ class Mustache_Compiler $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $delims, $this->walk($nodes, 2)); } - return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $method, $id, $filters, $key); + if ($arg === true) { + return $key; + } else { + $method = $this->getFindMethod($id); + $id = var_export($id, true); + return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $method, $id, $filters, $key); + } } const INVERTED_SECTION = ' From af8a62c5e22cf126801e839ad578998ef1967271 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Mon, 7 Apr 2014 20:05:33 +0000 Subject: [PATCH 09/43] Separate stacks are necessary for blocks to be resolved independently of variables. Renamed parent_* to block_*. --- src/Mustache/Compiler.php | 35 +++++++++-------- src/Mustache/Context.php | 15 +++++++- src/Mustache/Parser.php | 4 +- src/Mustache/Template.php | 5 ++- src/Mustache/Tokenizer.php | 6 +-- .../Test/Functional/InheritanceTest.php | 38 +++++++++++++++++++ test/Mustache/Test/ParserTest.php | 8 ++-- test/Mustache/Test/TokenizerTest.php | 2 +- 8 files changed, 82 insertions(+), 31 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index f0b6b71..9c32fa0 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -110,8 +110,8 @@ class Mustache_Compiler ); break; - case Mustache_Tokenizer::T_PARENT_ARG: - $code .= $this->parentArg( + case Mustache_Tokenizer::T_BLOCK_ARG: + $code .= $this->blockArg( $node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::INDEX], @@ -122,8 +122,8 @@ class Mustache_Compiler ); break; - case Mustache_Tokenizer::T_PARENT_VAR: - $code .= $this->parentVar( + case Mustache_Tokenizer::T_BLOCK_VAR: + $code .= $this->blockVar( $node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::INDEX], @@ -154,7 +154,6 @@ class Mustache_Compiler throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node); } } - return $code; } @@ -211,7 +210,7 @@ class Mustache_Compiler return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections); } - const PARENT_VAR = ' + const BLOCK_VAR = ' $value = $this->resolveValue($context->%s(%s), $context, $indent); if($value && !is_array($value) && !is_object($value)) { $buffer .= %s; @@ -220,22 +219,22 @@ class Mustache_Compiler } '; - private function parentVar($nodes, $id, $start, $end, $otag, $ctag, $level) + private function blockVar($nodes, $id, $start, $end, $otag, $ctag, $level) { - $method = 'findFromParent'; + $method = 'findInBlock'; $id_str = var_export($id, true); $value = $this->getEscape(); - return sprintf($this->prepare(self::PARENT_VAR, $level), $method, $id_str, $value, $this->walk($nodes, 2)); + return sprintf($this->prepare(self::BLOCK_VAR, $level), $method, $id_str, $value, $this->walk($nodes, 2)); } - const PARENT_ARG = ' - // %s parent_arg + const BLOCK_ARG = ' + // %s block_arg $value = $this->section%s($context, $indent, true); $newContext[%s] = %s$value; '; - private function parentArg($nodes, $id, $start, $end, $otag, $ctag, $level) + private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level) { $key = $this->section($nodes, $id, $start, $end, $otag, $ctag, $level, true); $filters = ''; @@ -247,7 +246,7 @@ class Mustache_Compiler $method = $this->getFindMethod($id); $id = var_export($id, true); - return sprintf($this->prepare(self::PARENT_ARG, $level), $key, $key, $id, $this->flushIndent()); + return sprintf($this->prepare(self::BLOCK_ARG, $level), $key, $key, $id, $this->flushIndent()); } const SECTION_CALL = ' @@ -384,9 +383,9 @@ class Mustache_Compiler const PARENT = ' if ($parent = $this->mustache->LoadPartial(%s)) { - $context->push($newContext); + $context->pushBlockContext($newContext); $buffer .= $parent->renderInternal($context, $indent); - $context->pop(); + $context->popBlockContext(); } '; @@ -394,7 +393,7 @@ class Mustache_Compiler { $block = ''; - $real_children = array_filter($children, array(__CLASS__, 'return_only_parent_args')); + $real_children = array_filter($children, array(__CLASS__, 'return_only_block_args')); $block = $this->walk($real_children, $level); @@ -405,9 +404,9 @@ class Mustache_Compiler ); } - private static function return_only_parent_args($child) + private static function return_only_block_args($child) { - return $child[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_PARENT_ARG; + return $child[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_BLOCK_ARG; } const VARIABLE = ' diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index 58461e5..ca82c35 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -15,6 +15,7 @@ class Mustache_Context { private $stack = array(); + private $block_stack = array(); /** * Mustache rendering Context constructor. @@ -38,6 +39,11 @@ class Mustache_Context array_push($this->stack, $value); } + public function pushBlockContext($value) + { + array_push($this->block_stack, $value); + } + /** * Pop the last Context frame from the stack. * @@ -48,6 +54,11 @@ class Mustache_Context return array_pop($this->stack); } + public function popBlockContext() + { + return array_pop($this->block_stack); + } + /** * Get the last Context frame. * @@ -120,9 +131,9 @@ class Mustache_Context return $value; } - public function findFromParent($id) + public function findInBlock($id) { - foreach($this->stack as $context) { + foreach($this->block_stack as $context) { if (is_array($context) && array_key_exists($id, $context)) { return $context[$id]; } diff --git a/src/Mustache/Parser.php b/src/Mustache/Parser.php index 83f71f1..448e229 100644 --- a/src/Mustache/Parser.php +++ b/src/Mustache/Parser.php @@ -108,9 +108,9 @@ class Mustache_Parser $nodes[] = $this->buildTree($tokens, $token); break; - case Mustache_Tokenizer::T_PARENT_VAR: + case Mustache_Tokenizer::T_BLOCK_VAR: if ($parent[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_PARENT) { - $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_PARENT_ARG; + $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG; } $this->clearStandaloneLines($nodes, $tokens); $nodes[] = $this->buildTree($tokens, $token); diff --git a/src/Mustache/Template.php b/src/Mustache/Template.php index 8c76916..07f49ab 100644 --- a/src/Mustache/Template.php +++ b/src/Mustache/Template.php @@ -61,10 +61,13 @@ abstract class Mustache_Template * @param mixed $context Array or object rendering context (default: array()) * * @return string Rendered template + * TODO construct a parent context stack here and pass it in */ public function render($context = array()) { - return $this->renderInternal($this->prepareContextStack($context)); + return $this->renderInternal( + $this->prepareContextStack($context) + ); } /** diff --git a/src/Mustache/Tokenizer.php b/src/Mustache/Tokenizer.php index 1dac502..c851434 100644 --- a/src/Mustache/Tokenizer.php +++ b/src/Mustache/Tokenizer.php @@ -35,8 +35,8 @@ class Mustache_Tokenizer const T_UNESCAPED_2 = '&'; const T_TEXT = '_t'; const T_PRAGMA = '%'; - const T_PARENT_VAR = '$'; - const T_PARENT_ARG = '$arg'; + const T_BLOCK_VAR = '$'; + const T_BLOCK_ARG = '$arg'; // Valid token types private static $tagTypes = array( @@ -51,7 +51,7 @@ class Mustache_Tokenizer self::T_UNESCAPED => true, self::T_UNESCAPED_2 => true, self::T_PRAGMA => true, - self::T_PARENT_VAR => true, + self::T_BLOCK_VAR => true, ); // Interpolated tags diff --git a/test/Mustache/Test/Functional/InheritanceTest.php b/test/Mustache/Test/Functional/InheritanceTest.php index 799246b..d1e01af 100644 --- a/test/Mustache/Test/Functional/InheritanceTest.php +++ b/test/Mustache/Test/Functional/InheritanceTest.php @@ -137,6 +137,44 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas $this->assertEquals('test |override1 default| |override2 default|', $tpl->render($data)); } + public function testDataDoesNotOverrideBlock() + { + $partials = array( + 'include' => '{{$var}}var in include{{/var}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{ 'var in data' + ); + + $this->assertEquals('var in template', $tpl->render($data)); + } + + public function testDataDoesNotOverrideDefaultBlockValue() + { + $partials = array( + 'include' => '{{$var}}var in include{{/var}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{ 'var in data' + ); + + $this->assertEquals('var in include', $tpl->render($data)); + } + public function testOverridePartialWithNewlines() { $partials = array( diff --git a/test/Mustache/Test/ParserTest.php b/test/Mustache/Test/ParserTest.php index b780dca..b54e427 100644 --- a/test/Mustache/Test/ParserTest.php +++ b/test/Mustache/Test/ParserTest.php @@ -128,7 +128,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase Mustache_Tokenizer::INDEX => 8 ), array( - Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR, Mustache_Tokenizer::NAME => 'bar', Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::CTAG => '}}', @@ -168,7 +168,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase Mustache_Tokenizer::END => 27, Mustache_Tokenizer::NODES => array( array( - Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_ARG, + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_ARG, Mustache_Tokenizer::NAME => 'bar', Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::CTAG => '}}', @@ -191,7 +191,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase array( array( array( - Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR, Mustache_Tokenizer::NAME => 'foo', Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::CTAG => '}}', @@ -213,7 +213,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase ), array( array( - Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR, Mustache_Tokenizer::NAME => 'foo', Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::CTAG => '}}', diff --git a/test/Mustache/Test/TokenizerTest.php b/test/Mustache/Test/TokenizerTest.php index dab379d..629e543 100644 --- a/test/Mustache/Test/TokenizerTest.php +++ b/test/Mustache/Test/TokenizerTest.php @@ -195,7 +195,7 @@ class Mustache_Test_TokenizerTest extends PHPUnit_Framework_TestCase null, array( array( - Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_PARENT_VAR, + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR, Mustache_Tokenizer::NAME => 'arg', Mustache_Tokenizer::OTAG => '{{', Mustache_Tokenizer::CTAG => '}}', From d27840f8efa25248bd0c0eb3be5578686c00bc2a Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Wed, 9 Apr 2014 15:09:01 +0000 Subject: [PATCH 10/43] Throw exception when something other than text or block tags are encountered inside of a parent tag. Other small cleanups --- src/Mustache/Compiler.php | 2 +- src/Mustache/Context.php | 2 +- src/Mustache/Parser.php | 13 +++++++++- .../Test/Functional/InheritanceTest.php | 25 ++++++++++++++++++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index 9c32fa0..dcc9469 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -246,7 +246,7 @@ class Mustache_Compiler $method = $this->getFindMethod($id); $id = var_export($id, true); - return sprintf($this->prepare(self::BLOCK_ARG, $level), $key, $key, $id, $this->flushIndent()); + return sprintf($this->prepare(self::BLOCK_ARG, $level), $id, $key, $id, $this->flushIndent()); } const SECTION_CALL = ' diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index ca82c35..96089ce 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -134,7 +134,7 @@ class Mustache_Context public function findInBlock($id) { foreach($this->block_stack as $context) { - if (is_array($context) && array_key_exists($id, $context)) { + if (array_key_exists($id, $context)) { return $context[$id]; } } diff --git a/src/Mustache/Parser.php b/src/Mustache/Parser.php index 448e229..d331db2 100644 --- a/src/Mustache/Parser.php +++ b/src/Mustache/Parser.php @@ -60,11 +60,13 @@ class Mustache_Parser switch ($token[Mustache_Tokenizer::TYPE]) { case Mustache_Tokenizer::T_DELIM_CHANGE: + $this->checkIfTokenIsAllowedInParent($parent, $token); $this->clearStandaloneLines($nodes, $tokens); break; case Mustache_Tokenizer::T_SECTION: case Mustache_Tokenizer::T_INVERTED: + $this->checkIfTokenIsAllowedInParent($parent, $token); $this->clearStandaloneLines($nodes, $tokens); $nodes[] = $this->buildTree($tokens, $token); break; @@ -97,6 +99,7 @@ class Mustache_Parser return $parent; case Mustache_Tokenizer::T_PARTIAL: + $this->checkIfTokenIsAllowedInParent($parent, $token); //store the whitespace prefix for laters! if ($indent = $this->clearStandaloneLines($nodes, $tokens)) { $token[Mustache_Tokenizer::INDENT] = $indent[Mustache_Tokenizer::VALUE]; @@ -105,11 +108,12 @@ class Mustache_Parser break; case Mustache_Tokenizer::T_PARENT: + $this->checkIfTokenIsAllowedInParent($parent, $token); $nodes[] = $this->buildTree($tokens, $token); break; case Mustache_Tokenizer::T_BLOCK_VAR: - if ($parent[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_PARENT) { + if ($parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG; } $this->clearStandaloneLines($nodes, $tokens); @@ -215,4 +219,11 @@ class Mustache_Parser return false; } + + private function checkIfTokenIsAllowedInParent($parent, $token) + { + if ($parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { + throw new Mustache_Exception_SyntaxException('Illegal content in < parent tag', $token); + } + } } diff --git a/test/Mustache/Test/Functional/InheritanceTest.php b/test/Mustache/Test/Functional/InheritanceTest.php index d1e01af..bbd4969 100644 --- a/test/Mustache/Test/Functional/InheritanceTest.php +++ b/test/Mustache/Test/Functional/InheritanceTest.php @@ -322,7 +322,7 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas $this->assertEquals('hmm', $tpl->render($data)); } - public function IgnoreTextInsideSuperTemplates() + public function testIgnoreTextInsideSuperTemplates() { $partials = array( 'include' => '{{$foo}}default content{{/foo}}' @@ -338,4 +338,27 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas $this->assertEquals('default content', $tpl->render($data)); } + + /** + * @expectedException Mustache_Exception_SyntaxException + * @expectedExceptionMessage Illegal content in < parent tag + */ + public function testOnlyBlockTagsAllowedInParent() + { + $partials = array( + 'foo' => '{{$baz}}default content{{/baz}}' + ); + + $this->mustache->setPartials($partials); + + $tpl = $this->mustache->loadTemplate( + '{{< foo }}{{# bar }}{{$ baz }}{{/ baz }}{{/ bar }}{{/ foo }}' + ); + + $data = array( + 'bar' => 'set by user' + ); + + $tpl->render($data); + } } From efae42978dbce89b2989800649ba444982c4f621 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 11 Apr 2014 16:46:27 +0000 Subject: [PATCH 11/43] Adding data provider to test legal and illegal syntax inside of a parent tag --- .../Test/Functional/InheritanceTest.php | 94 ++++++++++++++++--- 1 file changed, 80 insertions(+), 14 deletions(-) diff --git a/test/Mustache/Test/Functional/InheritanceTest.php b/test/Mustache/Test/Functional/InheritanceTest.php index bbd4969..2c57936 100644 --- a/test/Mustache/Test/Functional/InheritanceTest.php +++ b/test/Mustache/Test/Functional/InheritanceTest.php @@ -14,6 +14,71 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas $this->mustache = new Mustache_Engine; } + public function getIllegalInheritanceExamples() + { + return array( + array( + array( + 'foo' => '{{$baz}}default content{{/baz}}', + ), + array( + 'bar' => 'set by user' + ), + '{{< foo }}{{# bar }}{{$ baz }}{{/ baz }}{{/ bar }}{{/ foo }}', + ), + array( + array( + 'foo' => '{{$baz}}default content{{/baz}}' + ), + array( + ), + '{{ '{{$baz}}default content{{/baz}}', + 'qux' => 'I am a partial' + ), + array( + ), + '{{qux}}{{$baz}}set by template{{/baz}}{{/foo}}' + ), + array( + array( + 'foo' => '{{$baz}}default content{{/baz}}' + ), + array( + ), + '{{=}}<%={{ }}=%>{{/foo}}' + ) + ); + } + + public function getLegalInheritanceExamples() + { + return array( + array( + array( + 'foo' => '{{$baz}}default content{{/baz}}', + ), + array( + 'bar' => 'set by user' + ), + '{{ '{{$baz}}default content{{/baz}}' + ), + array( + ), + '{{mustache->loadTemplate('{{$title}}Default title{{/title}}'); @@ -339,26 +404,27 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas $this->assertEquals('default content', $tpl->render($data)); } + + /** + * @dataProvider getIllegalInheritanceExamples * @expectedException Mustache_Exception_SyntaxException * @expectedExceptionMessage Illegal content in < parent tag */ - public function testOnlyBlockTagsAllowedInParent() + public function testIllegalInheritanceExamples($partials, $data, $template) { - $partials = array( - 'foo' => '{{$baz}}default content{{/baz}}' - ); - $this->mustache->setPartials($partials); - - $tpl = $this->mustache->loadTemplate( - '{{< foo }}{{# bar }}{{$ baz }}{{/ baz }}{{/ bar }}{{/ foo }}' - ); - - $data = array( - 'bar' => 'set by user' - ); - + $tpl = $this->mustache->loadTemplate($template); $tpl->render($data); } + + /** + * @dataProvider getLegalInheritanceExamples + */ + public function testLegalInheritanceExamples($partials, $data, $template, $expect) + { + $this->mustache->setPartials($partials); + $tpl = $this->mustache->loadTemplate($template); + $this->assertSame($expect, $tpl->render($data)); + } } From 56713e560f0ec1f90c75c23ccd879f89a90f77f0 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Mon, 14 Apr 2014 17:20:14 +0000 Subject: [PATCH 12/43] ensure that text is ignored outside of a block inside of a parent tag --- test/Mustache/Test/Functional/InheritanceTest.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/Mustache/Test/Functional/InheritanceTest.php b/test/Mustache/Test/Functional/InheritanceTest.php index 2c57936..1f5dd97 100644 --- a/test/Mustache/Test/Functional/InheritanceTest.php +++ b/test/Mustache/Test/Functional/InheritanceTest.php @@ -47,8 +47,7 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas array( 'foo' => '{{$baz}}default content{{/baz}}' ), - array( - ), + array(), '{{=}}<%={{ }}=%>{{/foo}}' ) ); @@ -75,6 +74,14 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas ), '{{ '{{$baz}}defualt content{{/baz}}' + ), + array(), + '{{ Date: Mon, 14 Apr 2014 19:51:42 +0000 Subject: [PATCH 13/43] Don\'t escape html from blocks, removing unneeded variable assigns --- src/Mustache/Compiler.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index dcc9469..db17d90 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -211,9 +211,9 @@ class Mustache_Compiler } const BLOCK_VAR = ' - $value = $this->resolveValue($context->%s(%s), $context, $indent); + $value = $this->resolveValue($context->findInBlock(%s), $context, $indent); if($value && !is_array($value) && !is_object($value)) { - $buffer .= %s; + $buffer .= $value; } else { %s } @@ -221,11 +221,9 @@ class Mustache_Compiler private function blockVar($nodes, $id, $start, $end, $otag, $ctag, $level) { - $method = 'findInBlock'; $id_str = var_export($id, true); - $value = $this->getEscape(); - return sprintf($this->prepare(self::BLOCK_VAR, $level), $method, $id_str, $value, $this->walk($nodes, 2)); + return sprintf($this->prepare(self::BLOCK_VAR, $level), $id_str, $this->walk($nodes, 2)); } const BLOCK_ARG = ' From 9cb926a607e4350e42c58e8fb7345a67b36cd713 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Tue, 15 Apr 2014 14:35:08 +0000 Subject: [PATCH 14/43] removing old todo --- src/Mustache/Template.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mustache/Template.php b/src/Mustache/Template.php index 07f49ab..b64cd26 100644 --- a/src/Mustache/Template.php +++ b/src/Mustache/Template.php @@ -61,7 +61,6 @@ abstract class Mustache_Template * @param mixed $context Array or object rendering context (default: array()) * * @return string Rendered template - * TODO construct a parent context stack here and pass it in */ public function render($context = array()) { From 7aee02640379f723775c88218f5d9c86f792995d Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Thu, 26 Jun 2014 22:22:07 -0700 Subject: [PATCH 15/43] Add default pragma option to Mustache_Engine. Pragmas specified in the Mustache_Engine constructor will be enabled in all templates, regardless of the presence of pragma tags in individual templates. --- src/Mustache/Compiler.php | 20 +++++++- src/Mustache/Engine.php | 42 ++++++++++++++-- src/Mustache/Parser.php | 23 +++++++++ test/Mustache/Test/EngineTest.php | 2 + .../Test/FiveThree/Functional/EngineTest.php | 50 +++++++++++++++++++ 5 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 test/Mustache/Test/FiveThree/Functional/EngineTest.php diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index 579c392..c34f992 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -18,6 +18,7 @@ class Mustache_Compiler { private $pragmas; + private $defaultPragmas = array(); private $sections; private $source; private $indentNextLine; @@ -41,7 +42,7 @@ class Mustache_Compiler */ public function compile($source, array $tree, $name, $customEscape = false, $charset = 'UTF-8', $strictCallables = false, $entityFlags = ENT_COMPAT) { - $this->pragmas = array(); + $this->pragmas = $this->defaultPragmas; $this->sections = array(); $this->source = $source; $this->indentNextLine = true; @@ -53,6 +54,23 @@ class Mustache_Compiler return $this->writeCode($tree, $name); } + /** + * Enable pragmas across all templates, regardless of the presence of pragma + * tags in the individual templates. + * + * @internal Users should set global pragmas in Mustache_Engine, not here :) + * + * @param array $pragmas + */ + public function setPragmas(array $pragmas) + { + $this->pragmas = array(); + foreach ($pragmas as $pragma) { + $this->pragmas[$pragma] = true; + } + $this->defaultPragmas = $this->pragmas; + } + /** * Helper function for walking the Mustache token parse tree. * diff --git a/src/Mustache/Engine.php b/src/Mustache/Engine.php index 01a1a30..cbfe77a 100644 --- a/src/Mustache/Engine.php +++ b/src/Mustache/Engine.php @@ -28,6 +28,11 @@ class Mustache_Engine const PRAGMA_FILTERS = 'FILTERS'; + // Known pragmas + private static $knownPragmas = array( + self::PRAGMA_FILTERS => true, + ); + // Template cache private $templates = array(); @@ -44,6 +49,7 @@ class Mustache_Engine private $charset = 'UTF-8'; private $logger; private $strictCallables = false; + private $pragmas = array(); // Services private $tokenizer; @@ -110,6 +116,10 @@ class Mustache_Engine * // helps protect against arbitrary code execution when user input is passed directly into the template. * // This currently defaults to false, but will default to true in v3.0. * 'strict_callables' => true, + * + * // Enable pragmas across all templates, regardless of the presence of pragma tags in the individual + * // templates. + * 'pragmas' => [Mustache_Engine::PRAGMA_FILTERS], * ); * * @throws Mustache_Exception_InvalidArgumentException If `escape` option is not callable. @@ -176,6 +186,15 @@ class Mustache_Engine if (isset($options['strict_callables'])) { $this->strictCallables = $options['strict_callables']; } + + if (isset($options['pragmas'])) { + foreach ($options['pragmas'] as $pragma) { + if (!isset(self::$knownPragmas[$pragma])) { + throw new Mustache_Exception_InvalidArgumentException(sprintf('Unknown pragma: "%s".', $pragma)); + } + $this->pragmas[$pragma] = true; + } + } } /** @@ -226,6 +245,16 @@ class Mustache_Engine return $this->charset; } + /** + * Get the current globally enabled pragmas. + * + * @return array + */ + public function getPragmas() + { + return array_keys($this->pragmas); + } + /** * Set the Mustache template Loader instance. * @@ -563,12 +592,13 @@ class Mustache_Engine public function getTemplateClassName($source) { return $this->templateClassPrefix . md5(sprintf( - 'version:%s,escape:%s,entity_flags:%i,charset:%s,strict_callables:%s,source:%s', + 'version:%s,escape:%s,entity_flags:%i,charset:%s,strict_callables:%s,pragmas:%s,source:%s', self::VERSION, isset($this->escape) ? 'custom' : 'default', $this->entityFlags, $this->charset, $this->strictCallables ? 'true' : 'false', + implode(' ', array_keys($this->pragmas)), $source )); } @@ -705,7 +735,10 @@ class Mustache_Engine */ private function parse($source) { - return $this->getParser()->parse($this->tokenize($source)); + $parser = $this->getParser(); + $parser->setPragmas($this->getPragmas()); + + return $parser->parse($this->tokenize($source)); } /** @@ -728,7 +761,10 @@ class Mustache_Engine array('className' => $name) ); - return $this->getCompiler()->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags); + $compiler = $this->getCompiler(); + $compiler->setPragmas($this->getPragmas()); + + return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags); } /** diff --git a/src/Mustache/Parser.php b/src/Mustache/Parser.php index bea2dcd..a056dfc 100644 --- a/src/Mustache/Parser.php +++ b/src/Mustache/Parser.php @@ -18,6 +18,8 @@ class Mustache_Parser { private $lineNum; private $lineTokens; + private $pragmas; + private $defaultPragmas = array(); /** * Process an array of Mustache tokens and convert them into a parse tree. @@ -30,10 +32,28 @@ class Mustache_Parser { $this->lineNum = -1; $this->lineTokens = 0; + $this->pragmas = $this->defaultPragmas; return $this->buildTree($tokens); } + /** + * Enable pragmas across all templates, regardless of the presence of pragma + * tags in the individual templates. + * + * @internal Users should set global pragmas in Mustache_Engine, not here :) + * + * @param array $pragmas + */ + public function setPragmas(array $pragmas) + { + $this->pragmas = array(); + foreach ($pragmas as $pragma) { + $this->pragmas[$pragma] = true; + } + $this->defaultPragmas = $this->pragmas; + } + /** * Helper method for recursively building a parse tree. * @@ -121,6 +141,9 @@ class Mustache_Parser break; case Mustache_Tokenizer::T_PRAGMA: + $this->pragmas[$token[Mustache_Tokenizer::NAME]] = true; + // no break + case Mustache_Tokenizer::T_COMMENT: $this->clearStandaloneLines($nodes, $tokens); $nodes[] = $token; diff --git a/test/Mustache/Test/EngineTest.php b/test/Mustache/Test/EngineTest.php index 6904d18..6aca877 100644 --- a/test/Mustache/Test/EngineTest.php +++ b/test/Mustache/Test/EngineTest.php @@ -36,6 +36,7 @@ class Mustache_Test_EngineTest extends Mustache_Test_FunctionalTestCase 'escape' => 'strtoupper', 'entity_flags' => ENT_QUOTES, 'charset' => 'ISO-8859-1', + 'pragmas' => array(Mustache_Engine::PRAGMA_FILTERS), )); $this->assertSame($logger, $mustache->getLogger()); @@ -50,6 +51,7 @@ class Mustache_Test_EngineTest extends Mustache_Test_FunctionalTestCase $this->assertTrue($mustache->hasHelper('bar')); $this->assertFalse($mustache->hasHelper('baz')); $this->assertInstanceOf('Mustache_Cache_FilesystemCache', $mustache->getCache()); + $this->assertEquals(array(Mustache_Engine::PRAGMA_FILTERS), $mustache->getPragmas()); } public static function getFoo() diff --git a/test/Mustache/Test/FiveThree/Functional/EngineTest.php b/test/Mustache/Test/FiveThree/Functional/EngineTest.php new file mode 100644 index 0000000..3c80b2b --- /dev/null +++ b/test/Mustache/Test/FiveThree/Functional/EngineTest.php @@ -0,0 +1,50 @@ + $pragmas, + 'helpers' => $helpers, + )); + + $this->assertEquals($expect, $mustache->render($tpl, $data)); + } + + public function pragmaData() + { + $helpers = array( + 'longdate' => function (\DateTime $value) { + return $value->format('Y-m-d h:m:s'); + } + ); + + $data = array( + 'date' => new DateTime('1/1/2000', new DateTimeZone('UTC')), + ); + + $tpl = '{{ date | longdate }}'; + + return array( + array(array(Mustache_Engine::PRAGMA_FILTERS), $helpers, $data, $tpl, '2000-01-01 12:01:00'), + array(array(), $helpers, $data, $tpl, '' ), + ); + } +} From dd53137ae16f23d44738ee100ceb92a5aba89eeb Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Thu, 26 Jun 2014 22:58:27 -0700 Subject: [PATCH 16/43] Add missing method docs, minor code cleanup. --- src/Mustache/Compiler.php | 74 ++++++++++++++++--- src/Mustache/Context.php | 2 +- src/Mustache/Parser.php | 12 ++- src/Mustache/Tokenizer.php | 12 +-- .../Test/Functional/InheritanceTest.php | 2 - 5 files changed, 79 insertions(+), 23 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index c34f992..50d335e 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -123,8 +123,8 @@ class Mustache_Compiler $code .= $this->parent( $node[Mustache_Tokenizer::NAME], isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '', - $level, - $node[Mustache_Tokenizer::NODES] + $node[Mustache_Tokenizer::NODES], + $level ); break; @@ -172,6 +172,7 @@ class Mustache_Compiler throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node); } } + return $code; } @@ -230,13 +231,26 @@ class Mustache_Compiler const BLOCK_VAR = ' $value = $this->resolveValue($context->findInBlock(%s), $context, $indent); - if($value && !is_array($value) && !is_object($value)) { + if ($value && !is_array($value) && !is_object($value)) { $buffer .= $value; } else { %s } '; + /** + * Generate Mustache Template inheritance block variable PHP source. + * + * @param array $nodes Array of child tokens + * @param string $id Section name + * @param int $start Section start offset + * @param int $end Section end offset + * @param string $otag Current Mustache opening tag + * @param string $ctag Current Mustache closing tag + * @param int $level + * + * @return string Generated PHP source code + */ private function blockVar($nodes, $id, $start, $end, $otag, $ctag, $level) { $id_str = var_export($id, true); @@ -250,6 +264,19 @@ class Mustache_Compiler $newContext[%s] = %s$value; '; + /** + * Generate Mustache Template inheritance block argument PHP source. + * + * @param array $nodes Array of child tokens + * @param string $id Section name + * @param int $start Section start offset + * @param int $end Section end offset + * @param string $otag Current Mustache opening tag + * @param string $ctag Current Mustache closing tag + * @param int $level + * + * @return string Generated PHP source code + */ private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level) { $key = $this->section($nodes, $id, $start, $end, $otag, $ctag, $level, true); @@ -338,6 +365,7 @@ class Mustache_Compiler } else { $method = $this->getFindMethod($id); $id = var_export($id, true); + return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $method, $id, $filters, $key); } } @@ -397,6 +425,7 @@ class Mustache_Compiler } const PARENT = ' + %s if ($parent = $this->mustache->LoadPartial(%s)) { $context->pushBlockContext($newContext); @@ -405,24 +434,38 @@ class Mustache_Compiler } '; - private function parent($id, $indent, $level, $children) + /** + * Generate Mustache Template inheritance parent call PHP source. + * + * @param string $id Parent tag name + * @param string $indent Whitespace indent to apply to parent + * @param array $children Child nodes + * @param int $level + * + * @return string Generated PHP source code + */ + private function parent($id, $indent, array $children, $level) { - $block = ''; + $realChildren = array_filter($children, array(__CLASS__, 'onlyBlockArgs')); - $real_children = array_filter($children, array(__CLASS__, 'return_only_block_args')); - - $block = $this->walk($real_children, $level); - - return $block. sprintf( + return sprintf( $this->prepare(self::PARENT, $level), + $this->walk($realChildren, $level), var_export($id, true), var_export($indent, true) ); } - private static function return_only_block_args($child) + /** + * Helper method for filtering out non-block-arg tokens. + * + * @param array $node + * + * @return boolean True if $node is a block arg token. + */ + private static function onlyBlockArgs(array $node) { - return $child[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_BLOCK_ARG; + return $node[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_BLOCK_ARG; } const VARIABLE = ' @@ -590,6 +633,13 @@ class Mustache_Compiler const IS_CALLABLE = '!is_string(%s) && is_callable(%s)'; const STRICT_IS_CALLABLE = 'is_object(%s) && is_callable(%s)'; + /** + * Helper function to compile strict vs lax "is callable" logic. + * + * @param string $variable (default: '$value') + * + * @return string "is callable" logic + */ private function getCallable($variable = '$value') { $tpl = $this->strictCallables ? self::STRICT_IS_CALLABLE : self::IS_CALLABLE; diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index 2ec73b2..6918be4 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -133,7 +133,7 @@ class Mustache_Context public function findInBlock($id) { - foreach($this->block_stack as $context) { + foreach ($this->block_stack as $context) { if (array_key_exists($id, $context)) { return $context[$id]; } diff --git a/src/Mustache/Parser.php b/src/Mustache/Parser.php index a056dfc..f23f03a 100644 --- a/src/Mustache/Parser.php +++ b/src/Mustache/Parser.php @@ -235,14 +235,22 @@ class Mustache_Parser */ private function tokenIsWhitespace(array $token) { - if ($token[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_TEXT) { + if ($token[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_TEXT) { return preg_match('/^\s*$/', $token[Mustache_Tokenizer::VALUE]); } return false; } - private function checkIfTokenIsAllowedInParent($parent, $token) + /** + * Check whether a token is allowed inside a parent tag. + * + * @throws Mustache_Exception_SyntaxException if an invalid token is found inside a parent tag. + * + * @param array|null $parent + * @param array $token + */ + private function checkIfTokenIsAllowedInParent($parent, array $token) { if ($parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { throw new Mustache_Exception_SyntaxException('Illegal content in < parent tag', $token); diff --git a/src/Mustache/Tokenizer.php b/src/Mustache/Tokenizer.php index 8b0b805..eef1580 100644 --- a/src/Mustache/Tokenizer.php +++ b/src/Mustache/Tokenizer.php @@ -34,8 +34,8 @@ class Mustache_Tokenizer const T_UNESCAPED_2 = '&'; const T_TEXT = '_t'; const T_PRAGMA = '%'; - const T_BLOCK_VAR = '$'; - const T_BLOCK_ARG = '$arg'; + const T_BLOCK_VAR = '$'; + const T_BLOCK_ARG = '$arg'; // Valid token types private static $tagTypes = array( @@ -50,14 +50,14 @@ class Mustache_Tokenizer self::T_UNESCAPED => true, self::T_UNESCAPED_2 => true, self::T_PRAGMA => true, - self::T_BLOCK_VAR => true, + self::T_BLOCK_VAR => true, ); // Interpolated tags private static $interpolatedTags = array( - self::T_ESCAPED => true, - self::T_UNESCAPED => true, - self::T_UNESCAPED_2 => true, + self::T_ESCAPED => true, + self::T_UNESCAPED => true, + self::T_UNESCAPED_2 => true, ); // Token properties diff --git a/test/Mustache/Test/Functional/InheritanceTest.php b/test/Mustache/Test/Functional/InheritanceTest.php index 1f5dd97..0e61fdd 100644 --- a/test/Mustache/Test/Functional/InheritanceTest.php +++ b/test/Mustache/Test/Functional/InheritanceTest.php @@ -411,8 +411,6 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas $this->assertEquals('default content', $tpl->render($data)); } - - /** * @dataProvider getIllegalInheritanceExamples * @expectedException Mustache_Exception_SyntaxException From 349b58b8cae054058fe9a98446541106aa436647 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Thu, 26 Jun 2014 23:31:41 -0700 Subject: [PATCH 17/43] Throw inheritance behind a BLOCKS pragma. Update tests, known pragmas, and parsing. --- src/Mustache/Engine.php | 2 + src/Mustache/Parser.php | 17 +++- .../Test/Functional/InheritanceTest.php | 4 +- test/Mustache/Test/ParserTest.php | 77 ++++++++++++++++++- 4 files changed, 94 insertions(+), 6 deletions(-) diff --git a/src/Mustache/Engine.php b/src/Mustache/Engine.php index cbfe77a..1556caa 100644 --- a/src/Mustache/Engine.php +++ b/src/Mustache/Engine.php @@ -27,10 +27,12 @@ class Mustache_Engine const SPEC_VERSION = '1.1.2'; const PRAGMA_FILTERS = 'FILTERS'; + const PRAGMA_BLOCKS = 'BLOCKS'; // Known pragmas private static $knownPragmas = array( self::PRAGMA_FILTERS => true, + self::PRAGMA_BLOCKS => true, ); // Template cache diff --git a/src/Mustache/Parser.php b/src/Mustache/Parser.php index f23f03a..ce3a880 100644 --- a/src/Mustache/Parser.php +++ b/src/Mustache/Parser.php @@ -133,11 +133,20 @@ class Mustache_Parser break; case Mustache_Tokenizer::T_BLOCK_VAR: - if ($parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { - $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG; + if (isset($this->pragmas[Mustache_Engine::PRAGMA_BLOCKS])) { + // BLOCKS pragma is enabled, let's do this! + if ($parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { + $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG; + } + $this->clearStandaloneLines($nodes, $tokens); + $nodes[] = $this->buildTree($tokens, $token); + } else { + // pretend this was just a normal "escaped" token... + $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_ESCAPED; + // TODO: figure out how to figure out if there was a space after this dollar: + $token[Mustache_Tokenizer::NAME] = '$' . $token[Mustache_Tokenizer::NAME]; + $nodes[] = $token; } - $this->clearStandaloneLines($nodes, $tokens); - $nodes[] = $this->buildTree($tokens, $token); break; case Mustache_Tokenizer::T_PRAGMA: diff --git a/test/Mustache/Test/Functional/InheritanceTest.php b/test/Mustache/Test/Functional/InheritanceTest.php index 0e61fdd..dd524a5 100644 --- a/test/Mustache/Test/Functional/InheritanceTest.php +++ b/test/Mustache/Test/Functional/InheritanceTest.php @@ -11,7 +11,9 @@ class Mustache_Test_Functional_InheritanceTest extends PHPUnit_Framework_TestCas public function setUp() { - $this->mustache = new Mustache_Engine; + $this->mustache = new Mustache_Engine(array( + 'pragmas' => array(Mustache_Engine::PRAGMA_BLOCKS), + )); } public function getIllegalInheritanceExamples() diff --git a/test/Mustache/Test/ParserTest.php b/test/Mustache/Test/ParserTest.php index e6ee120..a739930 100644 --- a/test/Mustache/Test/ParserTest.php +++ b/test/Mustache/Test/ParserTest.php @@ -117,6 +117,54 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase ), ), + // This *would* be an invalid inheritance parse tree, but that pragma + // isn't enabled so it'll thunk it back into an "escaped" token: + array( + array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_TEXT, + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::VALUE => 'bar' + ), + ), + array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_ESCAPED, + Mustache_Tokenizer::NAME => '$foo', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_TEXT, + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::VALUE => 'bar' + ), + ), + ), + ); + } + + /** + * @dataProvider getInheritanceTokenSets + */ + public function testParseWithInheritance($tokens, $expected) + { + $parser = new Mustache_Parser; + $parser->setPragmas(array(Mustache_Engine::PRAGMA_BLOCKS)); + $this->assertEquals($expected, $parser->parse($tokens)); + } + + public function getInheritanceTokenSets() + { + return array( array( array( array( @@ -228,7 +276,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase ) ) ) - ) + ), ); } @@ -310,6 +358,33 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase ), ), ), + + // This *would* be a valid inheritance parse tree, but that pragma + // isn't enabled here so it's going to fail :) + array( + array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_BLOCK_VAR, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_TEXT, + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::VALUE => 'bar' + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_END_SECTION, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '{{', + Mustache_Tokenizer::CTAG => '}}', + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::INDEX => 11, + ), + ), + ), ); } } From 1fe5b0dfc28f964a8c7a713b5af04c5abb3d81d5 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Fri, 27 Jun 2014 00:18:41 -0700 Subject: [PATCH 18/43] Whoops. Missed some docs and cleanup :) --- src/Mustache/Compiler.php | 9 +++++---- src/Mustache/Context.php | 27 ++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index 50d335e..649c5ec 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -334,10 +334,11 @@ class Mustache_Compiler * @param string $otag Current Mustache opening tag * @param string $ctag Current Mustache closing tag * @param int $level + * @param bool $arg (default: false) * * @return string Generated section PHP source code */ - private function section($nodes, $id, $start, $end, $otag, $ctag, $level, $arg=false) + private function section($nodes, $id, $start, $end, $otag, $ctag, $level, $arg = false) { $filters = ''; @@ -354,7 +355,7 @@ class Mustache_Compiler $delims = ''; } - $key = ucfirst(md5($delims."\n".$source)); + $key = ucfirst(md5($delims."\n".$source)); if (!isset($this->sections[$key])) { $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $delims, $this->walk($nodes, 2)); @@ -363,8 +364,8 @@ class Mustache_Compiler if ($arg === true) { return $key; } else { - $method = $this->getFindMethod($id); - $id = var_export($id, true); + $method = $this->getFindMethod($id); + $id = var_export($id, true); return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $method, $id, $filters, $key); } diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index 6918be4..7d1dc90 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -14,8 +14,8 @@ */ class Mustache_Context { - private $stack = array(); - private $block_stack = array(); + private $stack = array(); + private $blockStack = array(); /** * Mustache rendering Context constructor. @@ -39,9 +39,14 @@ class Mustache_Context array_push($this->stack, $value); } + /** + * Push a new Context frame onto the block context stack. + * + * @param mixed $value Object or array to use for block context + */ public function pushBlockContext($value) { - array_push($this->block_stack, $value); + array_push($this->blockStack, $value); } /** @@ -54,9 +59,14 @@ class Mustache_Context return array_pop($this->stack); } + /** + * Pop the last block Context frame from the stack. + * + * @return mixed Last block Context frame (object or array) + */ public function popBlockContext() { - return array_pop($this->block_stack); + return array_pop($this->blockStack); } /** @@ -131,9 +141,16 @@ class Mustache_Context return $value; } + /** + * Find an argument in the block context stack. + * + * @param string $id + * + * @return mixed Variable value, or '' if not found. + */ public function findInBlock($id) { - foreach ($this->block_stack as $context) { + foreach ($this->blockStack as $context) { if (array_key_exists($id, $context)) { return $context[$id]; } From 6381604484166af3f91bd20c79b1c01fdaefa194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Wed, 23 Jul 2014 20:13:24 +0200 Subject: [PATCH 19/43] .php_cs add file for php-cs-fixer configuration --- .php_cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .php_cs diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..8d3b7d8 --- /dev/null +++ b/.php_cs @@ -0,0 +1,11 @@ +finder(Symfony\CS\Finder\DefaultFinder::create() + ->notName('LICENSE') + ->notName('README.md') + ->notName('composer.*') + ->notName('phpunit.xml.*') + ->notName('*.phar') + ->exclude('vendor') + ->in(__DIR__) +); From 3d25e12fe11f22d8a3491987b8657af183a617af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Wed, 23 Jul 2014 20:15:57 +0200 Subject: [PATCH 20/43] cs fixes --- bin/build_bootstrap.php | 8 ++++---- test/Mustache/Test/Functional/ExamplesTest.php | 2 +- .../section_magic_objects/SectionMagicObjects.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/build_bootstrap.php b/bin/build_bootstrap.php index 7863efc..b40b501 100755 --- a/bin/build_bootstrap.php +++ b/bin/build_bootstrap.php @@ -95,10 +95,10 @@ EOS; /** * Loads a list of classes and caches them in one big file. * - * @param array $classes An array of classes to load - * @param string $cacheDir A cache directory - * @param string $name The cache name prefix - * @param string $extension File extension of the resulting file + * @param array $classes An array of classes to load + * @param string $cacheDir A cache directory + * @param string $name The cache name prefix + * @param string $extension File extension of the resulting file * * @throws InvalidArgumentException When class can't be loaded */ diff --git a/test/Mustache/Test/Functional/ExamplesTest.php b/test/Mustache/Test/Functional/ExamplesTest.php index 43eab61..cc5b09d 100644 --- a/test/Mustache/Test/Functional/ExamplesTest.php +++ b/test/Mustache/Test/Functional/ExamplesTest.php @@ -92,7 +92,7 @@ class Mustache_Test_Functional_ExamplesTest extends PHPUnit_Framework_TestCase // load other files switch ($info['extension']) { case 'php': - require_once($fullpath); + require_once $fullpath; $context = new $info['filename']; break; diff --git a/test/fixtures/examples/section_magic_objects/SectionMagicObjects.php b/test/fixtures/examples/section_magic_objects/SectionMagicObjects.php index c03e228..2d9b464 100644 --- a/test/fixtures/examples/section_magic_objects/SectionMagicObjects.php +++ b/test/fixtures/examples/section_magic_objects/SectionMagicObjects.php @@ -21,7 +21,7 @@ class MagicObject public function __get($key) { - return isset($this->_data[$key]) ? $this->_data[$key] : NULL; + return isset($this->_data[$key]) ? $this->_data[$key] : null; } public function __isset($key) From 80d724f5f3ba762af16bc8f85470e0f2fbd9925f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Thu, 24 Jul 2014 19:03:27 +0200 Subject: [PATCH 21/43] .php_cs - simplify --- .php_cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.php_cs b/.php_cs index 8d3b7d8..f34252f 100644 --- a/.php_cs +++ b/.php_cs @@ -1,11 +1,6 @@ finder(Symfony\CS\Finder\DefaultFinder::create() - ->notName('LICENSE') - ->notName('README.md') - ->notName('composer.*') - ->notName('phpunit.xml.*') - ->notName('*.phar') - ->exclude('vendor') - ->in(__DIR__) -); +$config = new Symfony\CS\Config\Config(); +$config->getFinder()->exclude('bin'); + +return $config; From 114477811c0e7ed4a25bc789f0cbd39bc87b61d9 Mon Sep 17 00:00:00 2001 From: Mike Sherov Date: Thu, 7 Aug 2014 12:58:42 -0400 Subject: [PATCH 22/43] Add failing test For extra whitespace in nested partials. --- test/fixtures/examples/nested_partials/nested_partials.txt | 3 +++ .../examples/nested_partials/partials/fourth_inline.mustache | 1 + test/fixtures/examples/nested_partials/partials/third.mustache | 3 +++ 3 files changed, 7 insertions(+) create mode 100644 test/fixtures/examples/nested_partials/partials/fourth_inline.mustache diff --git a/test/fixtures/examples/nested_partials/nested_partials.txt b/test/fixtures/examples/nested_partials/nested_partials.txt index 62776f9..badd575 100644 --- a/test/fixtures/examples/nested_partials/nested_partials.txt +++ b/test/fixtures/examples/nested_partials/nested_partials.txt @@ -3,5 +3,8 @@ FOURTH! + + FOURTH!FOURTH! + \ No newline at end of file diff --git a/test/fixtures/examples/nested_partials/partials/fourth_inline.mustache b/test/fixtures/examples/nested_partials/partials/fourth_inline.mustache new file mode 100644 index 0000000..d796ae0 --- /dev/null +++ b/test/fixtures/examples/nested_partials/partials/fourth_inline.mustache @@ -0,0 +1 @@ +{{ val }} \ No newline at end of file diff --git a/test/fixtures/examples/nested_partials/partials/third.mustache b/test/fixtures/examples/nested_partials/partials/third.mustache index f33301a..30f8fa8 100644 --- a/test/fixtures/examples/nested_partials/partials/third.mustache +++ b/test/fixtures/examples/nested_partials/partials/third.mustache @@ -1,3 +1,6 @@ {{> fourth }} + + {{> fourth_inline }}{{> fourth_inline }} + From 5d77f4de9a9be441515412bb103e55cd3cb6112d Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Thu, 7 Aug 2014 10:38:44 -0700 Subject: [PATCH 23/43] Move failing nested partial example into a test case. See #214 --- .../Functional/NestedPartialIndentTest.php | 45 +++++++++++++++++++ .../nested_partials/nested_partials.txt | 3 -- .../partials/fourth_inline.mustache | 1 - .../nested_partials/partials/third.mustache | 3 -- 4 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 test/Mustache/Test/Functional/NestedPartialIndentTest.php delete mode 100644 test/fixtures/examples/nested_partials/partials/fourth_inline.mustache diff --git a/test/Mustache/Test/Functional/NestedPartialIndentTest.php b/test/Mustache/Test/Functional/NestedPartialIndentTest.php new file mode 100644 index 0000000..a484cf5 --- /dev/null +++ b/test/Mustache/Test/Functional/NestedPartialIndentTest.php @@ -0,0 +1,45 @@ + $partials + )); + $tpl = $m->loadTemplate($src); + $this->assertEquals($expected, $tpl->render()); + } + + public function partialsAndStuff() + { + $partials = array( + 'a' => ' {{> b }}', + 'b' => ' {{> d }}', + 'c' => ' {{> d }}{{> d }}', + 'd' => 'D!', + ); + + return array( + array(' {{> a }}', $partials, ' D!'), + array(' {{> b }}', $partials, ' D!'), + array(' {{> c }}', $partials, ' D!D!'), + ); + } +} diff --git a/test/fixtures/examples/nested_partials/nested_partials.txt b/test/fixtures/examples/nested_partials/nested_partials.txt index badd575..62776f9 100644 --- a/test/fixtures/examples/nested_partials/nested_partials.txt +++ b/test/fixtures/examples/nested_partials/nested_partials.txt @@ -3,8 +3,5 @@ FOURTH! - - FOURTH!FOURTH! - \ No newline at end of file diff --git a/test/fixtures/examples/nested_partials/partials/fourth_inline.mustache b/test/fixtures/examples/nested_partials/partials/fourth_inline.mustache deleted file mode 100644 index d796ae0..0000000 --- a/test/fixtures/examples/nested_partials/partials/fourth_inline.mustache +++ /dev/null @@ -1 +0,0 @@ -{{ val }} \ No newline at end of file diff --git a/test/fixtures/examples/nested_partials/partials/third.mustache b/test/fixtures/examples/nested_partials/partials/third.mustache index 30f8fa8..f33301a 100644 --- a/test/fixtures/examples/nested_partials/partials/third.mustache +++ b/test/fixtures/examples/nested_partials/partials/third.mustache @@ -1,6 +1,3 @@ {{> fourth }} - - {{> fourth_inline }}{{> fourth_inline }} - From 35456764a1ef25e453eb9387654a1c5bafe232dc Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Thu, 7 Aug 2014 10:45:33 -0700 Subject: [PATCH 24/43] Only pass indent through to standalone nested partials. Fixes #214 --- src/Mustache/Compiler.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index c6fa24d..e157e2e 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -277,9 +277,10 @@ class Mustache_Compiler return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $filters, $this->walk($nodes, $level)); } + const PARTIAL_INDENT = ', $indent . %s'; const PARTIAL = ' if ($partial = $this->mustache->loadPartial(%s)) { - $buffer .= $partial->renderInternal($context, $indent . %s); + $buffer .= $partial->renderInternal($context%s); } '; @@ -294,10 +295,16 @@ class Mustache_Compiler */ private function partial($id, $indent, $level) { + if ($indent !== '') { + $indentParam = sprintf(self::PARTIAL_INDENT, var_export($indent, true)); + } else { + $indentParam = ''; + } + return sprintf( $this->prepare(self::PARTIAL, $level), var_export($id, true), - var_export($indent, true) + $indentParam ); } From d07cac322ce76e4f62cdd6d36f16a65b4fbde200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Sun, 17 Aug 2014 00:28:56 +0200 Subject: [PATCH 25/43] update CS by php-cs-fixer --- src/Mustache/Context.php | 2 +- src/Mustache/Engine.php | 14 +++++------ src/Mustache/Template.php | 2 +- test/Mustache/Test/CompilerTest.php | 4 ++-- test/Mustache/Test/ContextTest.php | 18 +++++++------- test/Mustache/Test/EngineTest.php | 24 +++++++++---------- .../Functional/ClosureQuirksTest.php | 2 +- .../Test/FiveThree/Functional/FiltersTest.php | 4 ++-- .../Functional/HigherOrderSectionsTest.php | 6 ++--- .../FiveThree/Functional/LambdaHelperTest.php | 4 ++-- .../Functional/StrictCallablesTest.php | 2 +- test/Mustache/Test/Functional/CallTest.php | 2 +- .../Mustache/Test/Functional/ExamplesTest.php | 2 +- .../Functional/HigherOrderSectionsTest.php | 14 +++++------ .../Test/Functional/MustacheInjectionTest.php | 2 +- .../Test/Functional/ObjectSectionTest.php | 14 +++++------ test/Mustache/Test/HelperCollectionTest.php | 4 ++-- test/Mustache/Test/Loader/ArrayLoaderTest.php | 2 +- .../Mustache/Test/Loader/StringLoaderTest.php | 2 +- .../Test/Logger/AbstractLoggerTest.php | 2 +- test/Mustache/Test/ParserTest.php | 6 ++--- test/Mustache/Test/SpecTestCase.php | 4 ++-- test/Mustache/Test/TemplateTest.php | 6 ++--- test/Mustache/Test/TokenizerTest.php | 6 ++--- .../section_objects/SectionObjects.php | 2 +- 25 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index 7d1dc90..0115ff0 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -42,7 +42,7 @@ class Mustache_Context /** * Push a new Context frame onto the block context stack. * - * @param mixed $value Object or array to use for block context + * @param mixed $value Object or array to use for block context */ public function pushBlockContext($value) { diff --git a/src/Mustache/Engine.php b/src/Mustache/Engine.php index 1556caa..1564ae1 100644 --- a/src/Mustache/Engine.php +++ b/src/Mustache/Engine.php @@ -278,7 +278,7 @@ class Mustache_Engine public function getLoader() { if (!isset($this->loader)) { - $this->loader = new Mustache_Loader_StringLoader; + $this->loader = new Mustache_Loader_StringLoader(); } return $this->loader; @@ -305,7 +305,7 @@ class Mustache_Engine public function getPartialsLoader() { if (!isset($this->partialsLoader)) { - $this->partialsLoader = new Mustache_Loader_ArrayLoader; + $this->partialsLoader = new Mustache_Loader_ArrayLoader(); } return $this->partialsLoader; @@ -321,7 +321,7 @@ class Mustache_Engine public function setPartials(array $partials = array()) { if (!isset($this->partialsLoader)) { - $this->partialsLoader = new Mustache_Loader_ArrayLoader; + $this->partialsLoader = new Mustache_Loader_ArrayLoader(); } if (!$this->partialsLoader instanceof Mustache_Loader_MutableLoader) { @@ -365,7 +365,7 @@ class Mustache_Engine public function getHelpers() { if (!isset($this->helpers)) { - $this->helpers = new Mustache_HelperCollection; + $this->helpers = new Mustache_HelperCollection(); } return $this->helpers; @@ -474,7 +474,7 @@ class Mustache_Engine public function getTokenizer() { if (!isset($this->tokenizer)) { - $this->tokenizer = new Mustache_Tokenizer; + $this->tokenizer = new Mustache_Tokenizer(); } return $this->tokenizer; @@ -500,7 +500,7 @@ class Mustache_Engine public function getParser() { if (!isset($this->parser)) { - $this->parser = new Mustache_Parser; + $this->parser = new Mustache_Parser(); } return $this->parser; @@ -526,7 +526,7 @@ class Mustache_Engine public function getCompiler() { if (!isset($this->compiler)) { - $this->compiler = new Mustache_Compiler; + $this->compiler = new Mustache_Compiler(); } return $this->compiler; diff --git a/src/Mustache/Template.php b/src/Mustache/Template.php index 4d1273d..00b074c 100644 --- a/src/Mustache/Template.php +++ b/src/Mustache/Template.php @@ -140,7 +140,7 @@ abstract class Mustache_Template */ protected function prepareContextStack($context = null) { - $stack = new Mustache_Context; + $stack = new Mustache_Context(); $helpers = $this->mustache->getHelpers(); if (!$helpers->isEmpty()) { diff --git a/test/Mustache/Test/CompilerTest.php b/test/Mustache/Test/CompilerTest.php index a9c7b3a..8fefec8 100644 --- a/test/Mustache/Test/CompilerTest.php +++ b/test/Mustache/Test/CompilerTest.php @@ -20,7 +20,7 @@ class Mustache_Test_CompilerTest extends PHPUnit_Framework_TestCase */ public function testCompile($source, array $tree, $name, $customEscaper, $entityFlags, $charset, $expected) { - $compiler = new Mustache_Compiler; + $compiler = new Mustache_Compiler(); $compiled = $compiler->compile($source, $tree, $name, $customEscaper, $charset, false, $entityFlags); foreach ($expected as $contains) { @@ -138,7 +138,7 @@ class Mustache_Test_CompilerTest extends PHPUnit_Framework_TestCase */ public function testCompilerThrowsSyntaxException() { - $compiler = new Mustache_Compiler; + $compiler = new Mustache_Compiler(); $compiler->compile('', array(array(Mustache_Tokenizer::TYPE => 'invalid')), 'SomeClass'); } diff --git a/test/Mustache/Test/ContextTest.php b/test/Mustache/Test/ContextTest.php index 1e74754..70fecc7 100644 --- a/test/Mustache/Test/ContextTest.php +++ b/test/Mustache/Test/ContextTest.php @@ -16,7 +16,7 @@ class Mustache_Test_ContextTest extends PHPUnit_Framework_TestCase { public function testConstructor() { - $one = new Mustache_Context; + $one = new Mustache_Context(); $this->assertSame('', $one->find('foo')); $this->assertSame('', $one->find('bar')); @@ -27,7 +27,7 @@ class Mustache_Test_ContextTest extends PHPUnit_Framework_TestCase $this->assertEquals('FOO', $two->find('foo')); $this->assertEquals('', $two->find('bar')); - $obj = new StdClass; + $obj = new StdClass(); $obj->name = 'NAME'; $three = new Mustache_Context($obj); $this->assertSame($obj, $three->last()); @@ -36,16 +36,16 @@ class Mustache_Test_ContextTest extends PHPUnit_Framework_TestCase public function testPushPopAndLast() { - $context = new Mustache_Context; + $context = new Mustache_Context(); $this->assertFalse($context->last()); - $dummy = new Mustache_Test_TestDummy; + $dummy = new Mustache_Test_TestDummy(); $context->push($dummy); $this->assertSame($dummy, $context->last()); $this->assertSame($dummy, $context->pop()); $this->assertFalse($context->last()); - $obj = new StdClass; + $obj = new StdClass(); $context->push($dummy); $this->assertSame($dummy, $context->last()); $context->push($obj); @@ -57,11 +57,11 @@ class Mustache_Test_ContextTest extends PHPUnit_Framework_TestCase public function testFind() { - $context = new Mustache_Context; + $context = new Mustache_Context(); - $dummy = new Mustache_Test_TestDummy; + $dummy = new Mustache_Test_TestDummy(); - $obj = new StdClass; + $obj = new StdClass(); $obj->name = 'obj'; $arr = array( @@ -112,7 +112,7 @@ class Mustache_Test_ContextTest extends PHPUnit_Framework_TestCase public function testAccessorPriority() { - $context = new Mustache_Context(new Mustache_Test_AllTheThings); + $context = new Mustache_Context(new Mustache_Test_AllTheThings()); $this->assertEquals('win', $context->find('foo'), 'method beats property'); $this->assertEquals('win', $context->find('bar'), 'property beats ArrayAccess'); diff --git a/test/Mustache/Test/EngineTest.php b/test/Mustache/Test/EngineTest.php index 6aca877..c437a04 100644 --- a/test/Mustache/Test/EngineTest.php +++ b/test/Mustache/Test/EngineTest.php @@ -17,8 +17,8 @@ class Mustache_Test_EngineTest extends Mustache_Test_FunctionalTestCase public function testConstructor() { $logger = new Mustache_Logger_StreamLogger(tmpfile()); - $loader = new Mustache_Loader_StringLoader; - $partialsLoader = new Mustache_Loader_ArrayLoader; + $loader = new Mustache_Loader_StringLoader(); + $partialsLoader = new Mustache_Loader_ArrayLoader(); $mustache = new Mustache_Engine(array( 'template_class_prefix' => '__whot__', 'cache' => self::$tempDir, @@ -69,7 +69,7 @@ class Mustache_Test_EngineTest extends Mustache_Test_FunctionalTestCase ->disableOriginalConstructor() ->getMock(); - $mustache = new MustacheStub; + $mustache = new MustacheStub(); $mustache->template = $template; $template->expects($this->once()) @@ -84,11 +84,11 @@ class Mustache_Test_EngineTest extends Mustache_Test_FunctionalTestCase public function testSettingServices() { $logger = new Mustache_Logger_StreamLogger(tmpfile()); - $loader = new Mustache_Loader_StringLoader; - $tokenizer = new Mustache_Tokenizer; - $parser = new Mustache_Parser; - $compiler = new Mustache_Compiler; - $mustache = new Mustache_Engine; + $loader = new Mustache_Loader_StringLoader(); + $tokenizer = new Mustache_Tokenizer(); + $parser = new Mustache_Parser(); + $compiler = new Mustache_Compiler(); + $mustache = new Mustache_Engine(); $cache = new Mustache_Cache_FilesystemCache(self::$tempDir); $this->assertNotSame($logger, $mustache->getLogger()); @@ -181,7 +181,7 @@ class Mustache_Test_EngineTest extends Mustache_Test_FunctionalTestCase public function testImmutablePartialsLoadersThrowException() { $mustache = new Mustache_Engine(array( - 'partials_loader' => new Mustache_Loader_StringLoader, + 'partials_loader' => new Mustache_Loader_StringLoader(), )); $mustache->setPartials(array('foo' => '{{ foo }}')); @@ -245,7 +245,7 @@ class Mustache_Test_EngineTest extends Mustache_Test_FunctionalTestCase */ public function testSetHelpersThrowsExceptions() { - $mustache = new Mustache_Engine; + $mustache = new Mustache_Engine(); $mustache->setHelpers('monkeymonkeymonkey'); } @@ -254,8 +254,8 @@ class Mustache_Test_EngineTest extends Mustache_Test_FunctionalTestCase */ public function testSetLoggerThrowsExceptions() { - $mustache = new Mustache_Engine; - $mustache->setLogger(new StdClass); + $mustache = new Mustache_Engine(); + $mustache->setLogger(new StdClass()); } public function testLoadPartialCascading() diff --git a/test/Mustache/Test/FiveThree/Functional/ClosureQuirksTest.php b/test/Mustache/Test/FiveThree/Functional/ClosureQuirksTest.php index 23ac43b..b048c7e 100644 --- a/test/Mustache/Test/FiveThree/Functional/ClosureQuirksTest.php +++ b/test/Mustache/Test/FiveThree/Functional/ClosureQuirksTest.php @@ -19,7 +19,7 @@ class Mustache_Test_FiveThree_Functional_ClosureQuirksTest extends PHPUnit_Frame public function setUp() { - $this->mustache = new Mustache_Engine; + $this->mustache = new Mustache_Engine(); } public function testClosuresDontLikeItWhenYouTouchTheirProperties() diff --git a/test/Mustache/Test/FiveThree/Functional/FiltersTest.php b/test/Mustache/Test/FiveThree/Functional/FiltersTest.php index cd5deb7..4cbe049 100644 --- a/test/Mustache/Test/FiveThree/Functional/FiltersTest.php +++ b/test/Mustache/Test/FiveThree/Functional/FiltersTest.php @@ -19,7 +19,7 @@ class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_T public function setUp() { - $this->mustache = new Mustache_Engine; + $this->mustache = new Mustache_Engine(); } /** @@ -71,7 +71,7 @@ class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_T return sprintf('[[%s]]', $value); }); - $foo = new \StdClass; + $foo = new \StdClass(); $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/HigherOrderSectionsTest.php b/test/Mustache/Test/FiveThree/Functional/HigherOrderSectionsTest.php index 597c93a..b19b2a7 100644 --- a/test/Mustache/Test/FiveThree/Functional/HigherOrderSectionsTest.php +++ b/test/Mustache/Test/FiveThree/Functional/HigherOrderSectionsTest.php @@ -19,14 +19,14 @@ class Mustache_Test_FiveThree_Functional_HigherOrderSectionsTest extends PHPUnit public function setUp() { - $this->mustache = new Mustache_Engine; + $this->mustache = new Mustache_Engine(); } public function testAnonymousFunctionSectionCallback() { $tpl = $this->mustache->loadTemplate('{{#wrapper}}{{name}}{{/wrapper}}'); - $foo = new Mustache_Test_FiveThree_Functional_Foo; + $foo = new Mustache_Test_FiveThree_Functional_Foo(); $foo->name = 'Mario'; $foo->wrapper = function ($text) { return sprintf('
%s
', $text); @@ -40,7 +40,7 @@ class Mustache_Test_FiveThree_Functional_HigherOrderSectionsTest extends PHPUnit $one = $this->mustache->loadTemplate('{{name}}'); $two = $this->mustache->loadTemplate('{{#wrap}}{{name}}{{/wrap}}'); - $foo = new Mustache_Test_FiveThree_Functional_Foo; + $foo = new Mustache_Test_FiveThree_Functional_Foo(); $foo->name = 'Luigi'; $this->assertEquals($foo->name, $one->render($foo)); diff --git a/test/Mustache/Test/FiveThree/Functional/LambdaHelperTest.php b/test/Mustache/Test/FiveThree/Functional/LambdaHelperTest.php index cf24804..54ad518 100644 --- a/test/Mustache/Test/FiveThree/Functional/LambdaHelperTest.php +++ b/test/Mustache/Test/FiveThree/Functional/LambdaHelperTest.php @@ -19,7 +19,7 @@ class Mustache_Test_FiveThree_Functional_LambdaHelperTest extends PHPUnit_Framew public function setUp() { - $this->mustache = new Mustache_Engine; + $this->mustache = new Mustache_Engine(); } public function testSectionLambdaHelper() @@ -27,7 +27,7 @@ class Mustache_Test_FiveThree_Functional_LambdaHelperTest extends PHPUnit_Framew $one = $this->mustache->loadTemplate('{{name}}'); $two = $this->mustache->loadTemplate('{{#lambda}}{{name}}{{/lambda}}'); - $foo = new StdClass; + $foo = new StdClass(); $foo->name = 'Mario'; $foo->lambda = function ($text, $mustache) { return strtoupper($mustache->render($text)); diff --git a/test/Mustache/Test/FiveThree/Functional/StrictCallablesTest.php b/test/Mustache/Test/FiveThree/Functional/StrictCallablesTest.php index 479e038..d4e1652 100644 --- a/test/Mustache/Test/FiveThree/Functional/StrictCallablesTest.php +++ b/test/Mustache/Test/FiveThree/Functional/StrictCallablesTest.php @@ -23,7 +23,7 @@ class Mustache_Test_FiveThree_Functional_StrictCallablesTest extends PHPUnit_Fra $mustache = new Mustache_Engine(array('strict_callables' => $strict)); $tpl = $mustache->loadTemplate('{{# section }}{{ name }}{{/ section }}'); - $data = new StdClass; + $data = new StdClass(); $data->name = $name; $data->section = $section; diff --git a/test/Mustache/Test/Functional/CallTest.php b/test/Mustache/Test/Functional/CallTest.php index 8fafd9a..e66cc52 100644 --- a/test/Mustache/Test/Functional/CallTest.php +++ b/test/Mustache/Test/Functional/CallTest.php @@ -18,7 +18,7 @@ class Mustache_Test_Functional_CallTest extends PHPUnit_Framework_TestCase public function testCallEatsContext() { - $m = new Mustache_Engine; + $m = new Mustache_Engine(); $tpl = $m->loadTemplate('{{# foo }}{{ label }}: {{ name }}{{/ foo }}'); $foo = new Mustache_Test_Functional_ClassWithCall(); diff --git a/test/Mustache/Test/Functional/ExamplesTest.php b/test/Mustache/Test/Functional/ExamplesTest.php index cc5b09d..1f6fb1b 100644 --- a/test/Mustache/Test/Functional/ExamplesTest.php +++ b/test/Mustache/Test/Functional/ExamplesTest.php @@ -93,7 +93,7 @@ class Mustache_Test_Functional_ExamplesTest extends PHPUnit_Framework_TestCase switch ($info['extension']) { case 'php': require_once $fullpath; - $context = new $info['filename']; + $context = new $info['filename'](); break; case 'mustache': diff --git a/test/Mustache/Test/Functional/HigherOrderSectionsTest.php b/test/Mustache/Test/Functional/HigherOrderSectionsTest.php index b042455..c193636 100644 --- a/test/Mustache/Test/Functional/HigherOrderSectionsTest.php +++ b/test/Mustache/Test/Functional/HigherOrderSectionsTest.php @@ -19,7 +19,7 @@ class Mustache_Test_Functional_HigherOrderSectionsTest extends Mustache_Test_Fun public function setUp() { - $this->mustache = new Mustache_Engine; + $this->mustache = new Mustache_Engine(); } /** @@ -32,10 +32,10 @@ class Mustache_Test_Functional_HigherOrderSectionsTest extends Mustache_Test_Fun public function sectionCallbackData() { - $foo = new Mustache_Test_Functional_Foo; + $foo = new Mustache_Test_Functional_Foo(); $foo->doublewrap = array($foo, 'wrapWithBoth'); - $bar = new Mustache_Test_Functional_Foo; + $bar = new Mustache_Test_Functional_Foo(); $bar->trimmer = array(get_class($bar), 'staticTrim'); return array( @@ -48,7 +48,7 @@ class Mustache_Test_Functional_HigherOrderSectionsTest extends Mustache_Test_Fun { $tpl = $this->mustache->loadTemplate('{{#trim}} {{name}} {{/trim}}'); - $foo = new Mustache_Test_Functional_Foo; + $foo = new Mustache_Test_Functional_Foo(); $data = array( 'name' => 'Bob', @@ -81,7 +81,7 @@ class Mustache_Test_Functional_HigherOrderSectionsTest extends Mustache_Test_Fun $tpl = $mustache->loadTemplate('{{#wrap}}NAME{{/wrap}}'); - $foo = new Mustache_Test_Functional_Foo; + $foo = new Mustache_Test_Functional_Foo(); $foo->wrap = array($foo, 'wrapWithEm'); $this->assertEquals('NAME', $tpl->render($foo)); @@ -96,7 +96,7 @@ class Mustache_Test_Functional_HigherOrderSectionsTest extends Mustache_Test_Fun $tpl = $mustache->loadTemplate('{{#wrap}}{{name}}{{/wrap}}'); - $foo = new Mustache_Test_Functional_Foo; + $foo = new Mustache_Test_Functional_Foo(); $foo->wrap = array($foo, 'wrapWithEm'); $this->assertEquals('' . $foo->name . '', $tpl->render($foo)); @@ -115,7 +115,7 @@ class Mustache_Test_Functional_HigherOrderSectionsTest extends Mustache_Test_Fun )); $tpl = $mustache->loadTemplate('{{#wrap}}{{name}}{{/wrap}}'); - $foo = new Mustache_Test_Functional_Foo; + $foo = new Mustache_Test_Functional_Foo(); $foo->wrap = array($foo, 'wrapWithEm'); $this->assertEquals('' . $foo->name . '', $tpl->render($foo)); $this->assertCount($expect, glob($cacheDir . '/*.php')); diff --git a/test/Mustache/Test/Functional/MustacheInjectionTest.php b/test/Mustache/Test/Functional/MustacheInjectionTest.php index a36c469..54de502 100644 --- a/test/Mustache/Test/Functional/MustacheInjectionTest.php +++ b/test/Mustache/Test/Functional/MustacheInjectionTest.php @@ -20,7 +20,7 @@ class Mustache_Test_Functional_MustacheInjectionTest extends PHPUnit_Framework_T public function setUp() { - $this->mustache = new Mustache_Engine; + $this->mustache = new Mustache_Engine(); } /** diff --git a/test/Mustache/Test/Functional/ObjectSectionTest.php b/test/Mustache/Test/Functional/ObjectSectionTest.php index c611ab2..77f0e7d 100644 --- a/test/Mustache/Test/Functional/ObjectSectionTest.php +++ b/test/Mustache/Test/Functional/ObjectSectionTest.php @@ -19,13 +19,13 @@ class Mustache_Test_Functional_ObjectSectionTest extends PHPUnit_Framework_TestC public function setUp() { - $this->mustache = new Mustache_Engine; + $this->mustache = new Mustache_Engine(); } public function testBasicObject() { $tpl = $this->mustache->loadTemplate('{{#foo}}{{name}}{{/foo}}'); - $this->assertEquals('Foo', $tpl->render(new Mustache_Test_Functional_Alpha)); + $this->assertEquals('Foo', $tpl->render(new Mustache_Test_Functional_Alpha())); } /** @@ -34,7 +34,7 @@ class Mustache_Test_Functional_ObjectSectionTest extends PHPUnit_Framework_TestC public function testObjectWithGet() { $tpl = $this->mustache->loadTemplate('{{#foo}}{{name}}{{/foo}}'); - $this->assertEquals('Foo', $tpl->render(new Mustache_Test_Functional_Beta)); + $this->assertEquals('Foo', $tpl->render(new Mustache_Test_Functional_Beta())); } /** @@ -43,14 +43,14 @@ class Mustache_Test_Functional_ObjectSectionTest extends PHPUnit_Framework_TestC public function testSectionObjectWithGet() { $tpl = $this->mustache->loadTemplate('{{#bar}}{{#foo}}{{name}}{{/foo}}{{/bar}}'); - $this->assertEquals('Foo', $tpl->render(new Mustache_Test_Functional_Gamma)); + $this->assertEquals('Foo', $tpl->render(new Mustache_Test_Functional_Gamma())); } public function testSectionObjectWithFunction() { $tpl = $this->mustache->loadTemplate('{{#foo}}{{name}}{{/foo}}'); - $alpha = new Mustache_Test_Functional_Alpha; - $alpha->foo = new Mustache_Test_Functional_Delta; + $alpha = new Mustache_Test_Functional_Alpha(); + $alpha->foo = new Mustache_Test_Functional_Delta(); $this->assertEquals('Foo', $tpl->render($alpha)); } } @@ -95,7 +95,7 @@ class Mustache_Test_Functional_Gamma public function __construct() { - $this->bar = new Mustache_Test_Functional_Beta; + $this->bar = new Mustache_Test_Functional_Beta(); } } diff --git a/test/Mustache/Test/HelperCollectionTest.php b/test/Mustache/Test/HelperCollectionTest.php index 419bb82..212cfe9 100644 --- a/test/Mustache/Test/HelperCollectionTest.php +++ b/test/Mustache/Test/HelperCollectionTest.php @@ -35,7 +35,7 @@ class Mustache_Test_HelperCollectionTest extends PHPUnit_Framework_TestCase $foo = array($this, 'getFoo'); $bar = 'BAR'; - $helpers = new Mustache_HelperCollection; + $helpers = new Mustache_HelperCollection(); $this->assertTrue($helpers->isEmpty()); $this->assertFalse($helpers->has('foo')); $this->assertFalse($helpers->has('bar')); @@ -61,7 +61,7 @@ class Mustache_Test_HelperCollectionTest extends PHPUnit_Framework_TestCase $foo = array($this, 'getFoo'); $bar = 'BAR'; - $helpers = new Mustache_HelperCollection; + $helpers = new Mustache_HelperCollection(); $this->assertTrue($helpers->isEmpty()); $this->assertFalse($helpers->has('foo')); $this->assertFalse($helpers->has('bar')); diff --git a/test/Mustache/Test/Loader/ArrayLoaderTest.php b/test/Mustache/Test/Loader/ArrayLoaderTest.php index baa932b..db164c3 100644 --- a/test/Mustache/Test/Loader/ArrayLoaderTest.php +++ b/test/Mustache/Test/Loader/ArrayLoaderTest.php @@ -46,7 +46,7 @@ class Mustache_Test_Loader_ArrayLoaderTest extends PHPUnit_Framework_TestCase */ public function testMissingTemplatesThrowExceptions() { - $loader = new Mustache_Loader_ArrayLoader; + $loader = new Mustache_Loader_ArrayLoader(); $loader->load('not_a_real_template'); } } diff --git a/test/Mustache/Test/Loader/StringLoaderTest.php b/test/Mustache/Test/Loader/StringLoaderTest.php index 1dd66d0..347be64 100644 --- a/test/Mustache/Test/Loader/StringLoaderTest.php +++ b/test/Mustache/Test/Loader/StringLoaderTest.php @@ -16,7 +16,7 @@ class Mustache_Test_Loader_StringLoaderTest extends PHPUnit_Framework_TestCase { public function testLoadTemplates() { - $loader = new Mustache_Loader_StringLoader; + $loader = new Mustache_Loader_StringLoader(); $this->assertEquals('foo', $loader->load('foo')); $this->assertEquals('{{ bar }}', $loader->load('{{ bar }}')); diff --git a/test/Mustache/Test/Logger/AbstractLoggerTest.php b/test/Mustache/Test/Logger/AbstractLoggerTest.php index 9e6dda5..63d210d 100644 --- a/test/Mustache/Test/Logger/AbstractLoggerTest.php +++ b/test/Mustache/Test/Logger/AbstractLoggerTest.php @@ -16,7 +16,7 @@ class Mustache_Test_Logger_AbstractLoggerTest extends PHPUnit_Framework_TestCase { public function testEverything() { - $logger = new Mustache_Test_Logger_TestLogger; + $logger = new Mustache_Test_Logger_TestLogger(); $logger->emergency('emergency message'); $logger->alert('alert message'); diff --git a/test/Mustache/Test/ParserTest.php b/test/Mustache/Test/ParserTest.php index a739930..08aed5b 100644 --- a/test/Mustache/Test/ParserTest.php +++ b/test/Mustache/Test/ParserTest.php @@ -20,7 +20,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase */ public function testParse($tokens, $expected) { - $parser = new Mustache_Parser; + $parser = new Mustache_Parser(); $this->assertEquals($expected, $parser->parse($tokens)); } @@ -157,7 +157,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase */ public function testParseWithInheritance($tokens, $expected) { - $parser = new Mustache_Parser; + $parser = new Mustache_Parser(); $parser->setPragmas(array(Mustache_Engine::PRAGMA_BLOCKS)); $this->assertEquals($expected, $parser->parse($tokens)); } @@ -286,7 +286,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase */ public function testParserThrowsExceptions($tokens) { - $parser = new Mustache_Parser; + $parser = new Mustache_Parser(); $parser->parse($tokens); } diff --git a/test/Mustache/Test/SpecTestCase.php b/test/Mustache/Test/SpecTestCase.php index d4dc2c9..2a961e4 100644 --- a/test/Mustache/Test/SpecTestCase.php +++ b/test/Mustache/Test/SpecTestCase.php @@ -15,7 +15,7 @@ abstract class Mustache_Test_SpecTestCase extends PHPUnit_Framework_TestCase public static function setUpBeforeClass() { - self::$mustache = new Mustache_Engine; + self::$mustache = new Mustache_Engine(); } protected static function loadTemplate($source, $partials) @@ -42,7 +42,7 @@ abstract class Mustache_Test_SpecTestCase extends PHPUnit_Framework_TestCase } $data = array(); - $yaml = new sfYamlParser; + $yaml = new sfYamlParser(); $file = file_get_contents($filename); // @hack: pre-process the 'lambdas' spec so the Symfony YAML parser doesn't complain. diff --git a/test/Mustache/Test/TemplateTest.php b/test/Mustache/Test/TemplateTest.php index 6033e53..3255b64 100644 --- a/test/Mustache/Test/TemplateTest.php +++ b/test/Mustache/Test/TemplateTest.php @@ -16,7 +16,7 @@ class Mustache_Test_TemplateTest extends PHPUnit_Framework_TestCase { public function testConstructor() { - $mustache = new Mustache_Engine; + $mustache = new Mustache_Engine(); $template = new Mustache_Test_TemplateStub($mustache); $this->assertSame($mustache, $template->getMustache()); } @@ -24,10 +24,10 @@ class Mustache_Test_TemplateTest extends PHPUnit_Framework_TestCase public function testRendering() { $rendered = '<< wheee >>'; - $mustache = new Mustache_Engine; + $mustache = new Mustache_Engine(); $template = new Mustache_Test_TemplateStub($mustache); $template->rendered = $rendered; - $context = new Mustache_Context; + $context = new Mustache_Context(); if (version_compare(PHP_VERSION, '5.3.0', '>=')) { $this->assertEquals($rendered, $template()); diff --git a/test/Mustache/Test/TokenizerTest.php b/test/Mustache/Test/TokenizerTest.php index 9180f91..e5eeb72 100644 --- a/test/Mustache/Test/TokenizerTest.php +++ b/test/Mustache/Test/TokenizerTest.php @@ -20,7 +20,7 @@ class Mustache_Test_TokenizerTest extends PHPUnit_Framework_TestCase */ public function testScan($text, $delimiters, $expected) { - $tokenizer = new Mustache_Tokenizer; + $tokenizer = new Mustache_Tokenizer(); $this->assertSame($expected, $tokenizer->scan($text, $delimiters)); } @@ -29,7 +29,7 @@ class Mustache_Test_TokenizerTest extends PHPUnit_Framework_TestCase */ public function testUnevenBracesThrowExceptions() { - $tokenizer = new Mustache_Tokenizer; + $tokenizer = new Mustache_Tokenizer(); $text = "{{{ name }}"; $tokenizer->scan($text, null); @@ -40,7 +40,7 @@ class Mustache_Test_TokenizerTest extends PHPUnit_Framework_TestCase */ public function testUnevenBracesWithCustomDelimiterThrowExceptions() { - $tokenizer = new Mustache_Tokenizer; + $tokenizer = new Mustache_Tokenizer(); $text = "<%{ name %>"; $tokenizer->scan($text, "<% %>"); diff --git a/test/fixtures/examples/section_objects/SectionObjects.php b/test/fixtures/examples/section_objects/SectionObjects.php index e6a6b16..4860834 100644 --- a/test/fixtures/examples/section_objects/SectionObjects.php +++ b/test/fixtures/examples/section_objects/SectionObjects.php @@ -6,7 +6,7 @@ class SectionObjects public function middle() { - return new SectionObject; + return new SectionObject(); } public $final = "Then, surprisingly, it worked the final time."; From 7800aa8136851551285a6846203eb32d17b1b8f1 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Sat, 16 Aug 2014 15:38:26 -0700 Subject: [PATCH 26/43] Add submodule instructions to CONTRIBUTING. See #220 --- CONTRIBUTING.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fcbf6e7..c0b323d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,13 +5,15 @@ 1. [Fork the repo on GitHub](https://github.com/bobthecow/mustache.php). - 2. Run the test suite. We only take pull requests with passing tests, and it's great to know that you have a clean slate. Make sure you have PHPUnit 3.5+, then run `phpunit` from the project directory. + 2. Update submodules: `git submodule update --init` - 3. Add tests for your change. Only refactoring and documentation changes require no new tests. If you are adding functionality or fixing a bug, add a test! + 3. Run the test suite. We only take pull requests with passing tests, and it's great to know that you have a clean slate. Make sure you have PHPUnit 3.5+, then run `phpunit` from the project directory. - 4. Make the tests pass. + 4. Add tests for your change. Only refactoring and documentation changes require no new tests. If you are adding functionality or fixing a bug, add a test! - 5. Push your fork to GitHub and submit a pull request against the `dev` branch. + 5. Make the tests pass. + + 6. Push your fork to GitHub and submit a pull request against the `dev` branch. ### You can do some things to increase the chance that your pull request is accepted the first time: From 761538e06523b665c984bbb9a6afe919d07ff322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Sun, 17 Aug 2014 00:48:05 +0200 Subject: [PATCH 27/43] add php-cs-fixer into travis tasks --- .php_cs | 2 +- .travis.yml | 8 ++++++++ src/Mustache/Context.php | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.php_cs b/.php_cs index f34252f..0965a1b 100644 --- a/.php_cs +++ b/.php_cs @@ -1,6 +1,6 @@ getFinder()->exclude('bin'); +$config->getFinder()->in(__DIR__)->exclude('bin'); return $config; diff --git a/.travis.yml b/.travis.yml index e73c8b0..7342d2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,12 @@ language: php + +before_script: + - curl http://cs.sensiolabs.org/get/php-cs-fixer.phar -o php-cs-fixer.phar + +script: + - phpunit + - php php-cs-fixer.phar --dry-run -v fix . + php: - 5.2 - 5.3 diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index 7d1dc90..0115ff0 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -42,7 +42,7 @@ class Mustache_Context /** * Push a new Context frame onto the block context stack. * - * @param mixed $value Object or array to use for block context + * @param mixed $value Object or array to use for block context */ public function pushBlockContext($value) { From 320bab9e7c1d4b1bc7f8ccea8e06d8475742eb6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Sun, 17 Aug 2014 01:54:21 +0200 Subject: [PATCH 28/43] ignore php-cs-fixer on php5.2 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7342d2e..e8b83bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ before_script: script: - phpunit - - php php-cs-fixer.phar --dry-run -v fix . + - if [[ `php -r "echo version_compare(PHP_VERSION, '5.3.6', '>=');"` ]]; then php php-cs-fixer.phar --dry-run -v fix .; fi php: - 5.2 From 3d37a88f0b5e0696df8b410f4ab2a86cae0df177 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 05:00:16 -0700 Subject: [PATCH 29/43] Replace is_object/is_array with switch on gettype When checking multiple types, this is ~10% slower in the worst case, and ~30-33% faster for the average and best case. See #218 --- src/Mustache/Context.php | 31 +++++++++++++++++++------------ src/Mustache/Template.php | 27 +++++++++++++++------------ 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index 0115ff0..b87c99c 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -172,19 +172,26 @@ class Mustache_Context private function findVariableInStack($id, array $stack) { for ($i = count($stack) - 1; $i >= 0; $i--) { - if (is_object($stack[$i]) && !($stack[$i] instanceof Closure)) { + switch (gettype($stack[$i])) { + case 'object': + if (!($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)) { + return $stack[$i]->$id; + } elseif ($stack[$i] instanceof ArrayAccess && isset($stack[$i][$id])) { + return $stack[$i][$id]; + } + } + break; - // 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)) { - return $stack[$i]->$id; - } elseif ($stack[$i] instanceof ArrayAccess && isset($stack[$i][$id])) { - return $stack[$i][$id]; - } - } elseif (is_array($stack[$i]) && array_key_exists($id, $stack[$i])) { - return $stack[$i][$id]; + case 'array': + if (array_key_exists($id, $stack[$i])) { + return $stack[$i][$id]; + } + break; } } diff --git a/src/Mustache/Template.php b/src/Mustache/Template.php index 4d1273d..f6e5f2e 100644 --- a/src/Mustache/Template.php +++ b/src/Mustache/Template.php @@ -113,19 +113,22 @@ abstract class Mustache_Template */ protected function isIterable($value) { - if (is_object($value)) { - return $value instanceof Traversable; - } elseif (is_array($value)) { - $i = 0; - foreach ($value as $k => $v) { - if ($k !== $i++) { - return false; - } - } + switch (gettype($value)) { + case 'object': + return $value instanceof Traversable; - return true; - } else { - return false; + case 'array': + $i = 0; + foreach ($value as $k => $v) { + if ($k !== $i++) { + return false; + } + } + + return true; + + default: + return false; } } From df67052626c0768875c3272c94a6d28458720847 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 09:01:57 -0700 Subject: [PATCH 30/43] Use reference to current stack frame (saves quite a few array lookups) See #218 --- src/Mustache/Context.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index b87c99c..b16b400 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -172,24 +172,26 @@ class Mustache_Context private function findVariableInStack($id, array $stack) { for ($i = count($stack) - 1; $i >= 0; $i--) { - switch (gettype($stack[$i])) { + $frame = &$stack[$i]; + + switch (gettype($frame)) { case 'object': - if (!($stack[$i] instanceof Closure)) { + if (!($frame 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)) { - return $stack[$i]->$id; - } elseif ($stack[$i] instanceof ArrayAccess && isset($stack[$i][$id])) { - return $stack[$i][$id]; + if (method_exists($frame, $id)) { + return $frame->$id(); + } elseif (isset($frame->$id)) { + return $frame->$id; + } elseif ($frame instanceof ArrayAccess && isset($frame[$id])) { + return $frame[$id]; } } break; case 'array': - if (array_key_exists($id, $stack[$i])) { - return $stack[$i][$id]; + if (array_key_exists($id, $frame)) { + return $frame[$id]; } break; } From c0bd7efe3930aa3da0b10de20a454aedc2da0541 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 16:56:32 -0700 Subject: [PATCH 31/43] No need for elseif with returns :) /cc @keradus --- src/Mustache/Compiler.php | 10 ++++++---- src/Mustache/Context.php | 8 ++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index 9161c64..285287c 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -631,11 +631,13 @@ class Mustache_Compiler { if ($id === '.') { return 'last'; - } elseif (strpos($id, '.') === false) { - return 'find'; - } else { - return 'findDot'; } + + if (strpos($id, '.') === false) { + return 'find'; + } + + return 'findDot'; } const IS_CALLABLE = '!is_string(%s) && is_callable(%s)'; diff --git a/src/Mustache/Context.php b/src/Mustache/Context.php index b16b400..db03acc 100644 --- a/src/Mustache/Context.php +++ b/src/Mustache/Context.php @@ -181,9 +181,13 @@ class Mustache_Context // See https://github.com/bobthecow/mustache.php/wiki/Magic-Methods if (method_exists($frame, $id)) { return $frame->$id(); - } elseif (isset($frame->$id)) { + } + + if (isset($frame->$id)) { return $frame->$id; - } elseif ($frame instanceof ArrayAccess && isset($frame[$id])) { + } + + if ($frame instanceof ArrayAccess && isset($frame[$id])) { return $frame[$id]; } } From 8449681a37f1729be96cd57e2ac298ffde138832 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 16:58:19 -0700 Subject: [PATCH 32/43] One more unnecessary else/return combo. --- src/Mustache/Compiler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index 285287c..8d079fb 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -609,9 +609,9 @@ class Mustache_Compiler { if ($this->customEscape) { return sprintf(self::CUSTOM_ESCAPE, $value); - } else { - return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->entityFlags, true), var_export($this->charset, true)); } + + return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->entityFlags, true), var_export($this->charset, true)); } /** From d225afe032f43fcfccb62070152cbe4fcc17ccc2 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 18:28:11 -0700 Subject: [PATCH 33/43] Remove unused block arg code. --- src/Mustache/Compiler.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index 8d079fb..e4179d9 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -253,9 +253,9 @@ class Mustache_Compiler */ private function blockVar($nodes, $id, $start, $end, $otag, $ctag, $level) { - $id_str = var_export($id, true); + $id = var_export($id, true); - return sprintf($this->prepare(self::BLOCK_VAR, $level), $id_str, $this->walk($nodes, 2)); + return sprintf($this->prepare(self::BLOCK_VAR, $level), $id, $this->walk($nodes, 2)); } const BLOCK_ARG = ' @@ -280,13 +280,6 @@ class Mustache_Compiler private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level) { $key = $this->section($nodes, $id, $start, $end, $otag, $ctag, $level, true); - $filters = ''; - - if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { - list($id, $filters) = $this->getFilters($id, $level); - } - - $method = $this->getFindMethod($id); $id = var_export($id, true); return sprintf($this->prepare(self::BLOCK_ARG, $level), $id, $key, $id, $this->flushIndent()); From 58a77265158011f679dd5209bb08c527e70cc37f Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 18:28:50 -0700 Subject: [PATCH 34/43] Move filter logic from the compiler to the parser. --- src/Mustache/Compiler.php | 120 ++++++++++++++++--------------------- src/Mustache/Parser.php | 62 +++++++++++++++++-- src/Mustache/Tokenizer.php | 21 +++---- 3 files changed, 120 insertions(+), 83 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index e4179d9..e77b3df 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -60,7 +60,7 @@ class Mustache_Compiler * * @internal Users should set global pragmas in Mustache_Engine, not here :) * - * @param array $pragmas + * @param string[] $pragmas */ public function setPragmas(array $pragmas) { @@ -95,6 +95,7 @@ class Mustache_Compiler $code .= $this->section( $node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], + isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), $node[Mustache_Tokenizer::INDEX], $node[Mustache_Tokenizer::END], $node[Mustache_Tokenizer::OTAG], @@ -107,6 +108,7 @@ class Mustache_Compiler $code .= $this->invertedSection( $node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], + isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), $level ); break; @@ -154,14 +156,24 @@ class Mustache_Compiler case Mustache_Tokenizer::T_UNESCAPED: case Mustache_Tokenizer::T_UNESCAPED_2: - $code .= $this->variable($node[Mustache_Tokenizer::NAME], false, $level); + $code .= $this->variable( + $node[Mustache_Tokenizer::NAME], + isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), + false, + $level + ); break; case Mustache_Tokenizer::T_COMMENT: break; case Mustache_Tokenizer::T_ESCAPED: - $code .= $this->variable($node[Mustache_Tokenizer::NAME], true, $level); + $code .= $this->variable( + $node[Mustache_Tokenizer::NAME], + isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), + true, + $level + ); break; case Mustache_Tokenizer::T_TEXT: @@ -279,8 +291,8 @@ class Mustache_Compiler */ private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level) { - $key = $this->section($nodes, $id, $start, $end, $otag, $ctag, $level, true); - $id = var_export($id, true); + $key = $this->section($nodes, $id, array(), $start, $end, $otag, $ctag, $level, true); + $id = var_export($id, true); return sprintf($this->prepare(self::BLOCK_ARG, $level), $id, $key, $id, $this->flushIndent()); } @@ -320,25 +332,20 @@ class Mustache_Compiler /** * Generate Mustache Template section PHP source. * - * @param array $nodes Array of child tokens - * @param string $id Section name - * @param int $start Section start offset - * @param int $end Section end offset - * @param string $otag Current Mustache opening tag - * @param string $ctag Current Mustache closing tag - * @param int $level - * @param bool $arg (default: false) + * @param array $nodes Array of child tokens + * @param string $id Section name + * @param string[] $filters Array of filters + * @param int $start Section start offset + * @param int $end Section end offset + * @param string $otag Current Mustache opening tag + * @param string $ctag Current Mustache closing tag + * @param int $level + * @param bool $arg (default: false) * * @return string Generated section PHP source code */ - private function section($nodes, $id, $start, $end, $otag, $ctag, $level, $arg = false) + private function section($nodes, $id, $filters, $start, $end, $otag, $ctag, $level, $arg = false) { - $filters = ''; - - if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { - list($id, $filters) = $this->getFilters($id, $level); - } - $source = var_export(substr($this->source, $start, $end - $start), true); $callable = $this->getCallable(); @@ -357,8 +364,9 @@ class Mustache_Compiler if ($arg === true) { return $key; } else { - $method = $this->getFindMethod($id); - $id = var_export($id, true); + $method = $this->getFindMethod($id); + $id = var_export($id, true); + $filters = $this->getFilters($filters, $level); return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $method, $id, $filters, $key); } @@ -374,22 +382,18 @@ class Mustache_Compiler /** * Generate Mustache Template inverted section PHP source. * - * @param array $nodes Array of child tokens - * @param string $id Section name - * @param int $level + * @param array $nodes Array of child tokens + * @param string $id Section name + * @param string[] $filters Array of filters + * @param int $level * * @return string Generated inverted section PHP source code */ - private function invertedSection($nodes, $id, $level) + private function invertedSection($nodes, $id, $filters, $level) { - $filters = ''; - - if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { - list($id, $filters) = $this->getFilters($id, $level); - } - - $method = $this->getFindMethod($id); - $id = var_export($id, true); + $method = $this->getFindMethod($id); + $id = var_export($id, true); + $filters = $this->getFilters($filters, $level); return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $filters, $this->walk($nodes, $level)); } @@ -477,43 +481,23 @@ class Mustache_Compiler /** * Generate Mustache Template variable interpolation PHP source. * - * @param string $id Variable name - * @param boolean $escape Escape the variable value for output? - * @param int $level + * @param string $id Variable name + * @param string[] $filters Array of filters + * @param boolean $escape Escape the variable value for output? + * @param int $level * * @return string Generated variable interpolation PHP source */ - private function variable($id, $escape, $level) + private function variable($id, $filters, $escape, $level) { - $filters = ''; - - if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { - list($id, $filters) = $this->getFilters($id, $level); - } - - $method = $this->getFindMethod($id); - $id = ($method !== 'last') ? var_export($id, true) : ''; - $value = $escape ? $this->getEscape() : '$value'; + $method = $this->getFindMethod($id); + $id = ($method !== 'last') ? var_export($id, true) : ''; + $filters = $this->getFilters($filters, $level); + $value = $escape ? $this->getEscape() : '$value'; return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value); } - /** - * Generate Mustache Template variable filtering PHP source. - * - * @param string $id Variable name - * @param int $level - * - * @return string Generated variable filtering PHP source - */ - private function getFilters($id, $level) - { - $filters = array_map('trim', explode('|', $id)); - $id = array_shift($filters); - - return array($id, $this->getFilter($filters, $level)); - } - const FILTER = ' $filter = $context->%s(%s); if (!(%s)) { @@ -523,14 +507,14 @@ class Mustache_Compiler '; /** - * Generate PHP source for a single filter. + * Generate Mustache Template variable filtering PHP source. * - * @param array $filters - * @param int $level + * @param string[] $filters Array of filters + * @param int $level * * @return string Generated filter PHP source */ - private function getFilter(array $filters, $level) + private function getFilters(array $filters, $level) { if (empty($filters)) { return ''; @@ -542,7 +526,7 @@ class Mustache_Compiler $callable = $this->getCallable('$filter'); $msg = var_export($name, true); - return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilter($filters, $level)); + return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilters($filters, $level)); } const LINE = '$buffer .= "\n";'; diff --git a/src/Mustache/Parser.php b/src/Mustache/Parser.php index ce3a880..5074183 100644 --- a/src/Mustache/Parser.php +++ b/src/Mustache/Parser.php @@ -21,6 +21,9 @@ class Mustache_Parser private $pragmas; private $defaultPragmas = array(); + private $pragmaFilters; + private $pragmaBlocks; + /** * Process an array of Mustache tokens and convert them into a parse tree. * @@ -34,6 +37,9 @@ class Mustache_Parser $this->lineTokens = 0; $this->pragmas = $this->defaultPragmas; + $this->pragmaFilters = isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS]); + $this->pragmaBlocks = isset($this->pragmas[Mustache_Engine::PRAGMA_BLOCKS]); + return $this->buildTree($tokens); } @@ -43,13 +49,13 @@ class Mustache_Parser * * @internal Users should set global pragmas in Mustache_Engine, not here :) * - * @param array $pragmas + * @param string[] $pragmas */ public function setPragmas(array $pragmas) { $this->pragmas = array(); foreach ($pragmas as $pragma) { - $this->pragmas[$pragma] = true; + $this->enablePragma($pragma); } $this->defaultPragmas = $this->pragmas; } @@ -78,6 +84,14 @@ class Mustache_Parser $this->lineTokens = 0; } + if ($this->pragmaFilters && isset($token[Mustache_Tokenizer::NAME])) { + list($name, $filters) = $this->getNameAndFilters($token[Mustache_Tokenizer::NAME]); + if (!empty($filters)) { + $token[Mustache_Tokenizer::NAME] = $name; + $token[Mustache_Tokenizer::FILTERS] = $filters; + } + } + switch ($token[Mustache_Tokenizer::TYPE]) { case Mustache_Tokenizer::T_DELIM_CHANGE: $this->checkIfTokenIsAllowedInParent($parent, $token); @@ -133,7 +147,7 @@ class Mustache_Parser break; case Mustache_Tokenizer::T_BLOCK_VAR: - if (isset($this->pragmas[Mustache_Engine::PRAGMA_BLOCKS])) { + if ($this->pragmaBlocks) { // BLOCKS pragma is enabled, let's do this! if ($parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG; @@ -150,7 +164,7 @@ class Mustache_Parser break; case Mustache_Tokenizer::T_PRAGMA: - $this->pragmas[$token[Mustache_Tokenizer::NAME]] = true; + $this->enablePragma($token[Mustache_Tokenizer::NAME]); // no break case Mustache_Tokenizer::T_COMMENT: @@ -184,7 +198,7 @@ class Mustache_Parser * @param array $nodes Parsed nodes. * @param array $tokens Tokens to be parsed. * - * @return array Resulting indent token, if any. + * @return array|null Resulting indent token, if any. */ private function clearStandaloneLines(array &$nodes, array &$tokens) { @@ -265,4 +279,42 @@ class Mustache_Parser throw new Mustache_Exception_SyntaxException('Illegal content in < parent tag', $token); } } + + /** + * Split a tag name into name and filters. + * + * @param string $name + * + * @return array { + * @type string Tag name + * @type string[] Array of filters + * } + */ + private function getNameAndFilters($name) + { + $filters = array_map('trim', explode('|', $name)); + $name = array_shift($filters); + + return array($name, $filters); + } + + /** + * Enable a pragma. + * + * @param string $name + */ + private function enablePragma($name) + { + $this->pragmas[$name] = true; + + switch ($name) { + case Mustache_Engine::PRAGMA_BLOCKS: + $this->pragmaBlocks = true; + break; + + case Mustache_Engine::PRAGMA_FILTERS: + $this->pragmaFilters = true; + break; + } + } } diff --git a/src/Mustache/Tokenizer.php b/src/Mustache/Tokenizer.php index eef1580..3175a03 100644 --- a/src/Mustache/Tokenizer.php +++ b/src/Mustache/Tokenizer.php @@ -61,16 +61,17 @@ class Mustache_Tokenizer ); // Token properties - const TYPE = 'type'; - const NAME = 'name'; - const OTAG = 'otag'; - const CTAG = 'ctag'; - const LINE = 'line'; - const INDEX = 'index'; - const END = 'end'; - const INDENT = 'indent'; - const NODES = 'nodes'; - const VALUE = 'value'; + const TYPE = 'type'; + const NAME = 'name'; + const OTAG = 'otag'; + const CTAG = 'ctag'; + const LINE = 'line'; + const INDEX = 'index'; + const END = 'end'; + const INDENT = 'indent'; + const NODES = 'nodes'; + const VALUE = 'value'; + const FILTERS = 'filters'; private $state; private $tagType; From 99caf588ab09b08ed934a6f5f91acd19d3d82634 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 18:32:27 -0700 Subject: [PATCH 35/43] Dry up T_ESCAPED/T_UNESCAPED compiling. --- src/Mustache/Compiler.php | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index e77b3df..72d3414 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -154,24 +154,16 @@ class Mustache_Compiler ); break; + case Mustache_Tokenizer::T_COMMENT: + break; + + case Mustache_Tokenizer::T_ESCAPED: case Mustache_Tokenizer::T_UNESCAPED: case Mustache_Tokenizer::T_UNESCAPED_2: $code .= $this->variable( $node[Mustache_Tokenizer::NAME], isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), - false, - $level - ); - break; - - case Mustache_Tokenizer::T_COMMENT: - break; - - case Mustache_Tokenizer::T_ESCAPED: - $code .= $this->variable( - $node[Mustache_Tokenizer::NAME], - isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), - true, + $node[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_ESCAPED, $level ); break; From 4decec3c0e0b11ff968acfd24a78dd0e2146f962 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 18:33:30 -0700 Subject: [PATCH 36/43] =?UTF-8?q?Reformat=20code=20so=20php-cs-fixer=20doe?= =?UTF-8?q?sn=E2=80=99t=20keep=20breaking=20it.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/Mustache/Test/Functional/ExamplesTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Mustache/Test/Functional/ExamplesTest.php b/test/Mustache/Test/Functional/ExamplesTest.php index 1f6fb1b..ffb8361 100644 --- a/test/Mustache/Test/Functional/ExamplesTest.php +++ b/test/Mustache/Test/Functional/ExamplesTest.php @@ -93,7 +93,8 @@ class Mustache_Test_Functional_ExamplesTest extends PHPUnit_Framework_TestCase switch ($info['extension']) { case 'php': require_once $fullpath; - $context = new $info['filename'](); + $className = $info['filename']; + $context = new $className(); break; case 'mustache': From 21570bfd4df64bff853decdd98c829133fd93bb6 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 18:36:07 -0700 Subject: [PATCH 37/43] =?UTF-8?q?php-cs-fixer=20isn=E2=80=99t=20up=20to=20?= =?UTF-8?q?date=20on=20PSR-5=20draft=20:)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Mustache/Parser.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mustache/Parser.php b/src/Mustache/Parser.php index 5074183..44eb9a4 100644 --- a/src/Mustache/Parser.php +++ b/src/Mustache/Parser.php @@ -286,9 +286,9 @@ class Mustache_Parser * @param string $name * * @return array { - * @type string Tag name - * @type string[] Array of filters - * } + * @type string Tag name + * @type string[] Array of filters + * } */ private function getNameAndFilters($name) { From 4f73c826ff33258912b08d8b1f78bd064f5b9da0 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 20:00:34 -0700 Subject: [PATCH 38/43] Ugh. Fix for php-cs-fixer --- src/Mustache/Parser.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Mustache/Parser.php b/src/Mustache/Parser.php index 44eb9a4..0c134ec 100644 --- a/src/Mustache/Parser.php +++ b/src/Mustache/Parser.php @@ -285,10 +285,7 @@ class Mustache_Parser * * @param string $name * - * @return array { - * @type string Tag name - * @type string[] Array of filters - * } + * @return array [Tag name, Array of filters] */ private function getNameAndFilters($name) { From a8d174a85ff558359135e6ef33ba2cd334ab1a10 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 18 Aug 2014 20:59:16 -0700 Subject: [PATCH 39/43] DRY. --- src/Mustache/Engine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mustache/Engine.php b/src/Mustache/Engine.php index 1564ae1..ef08ed2 100644 --- a/src/Mustache/Engine.php +++ b/src/Mustache/Engine.php @@ -600,7 +600,7 @@ class Mustache_Engine $this->entityFlags, $this->charset, $this->strictCallables ? 'true' : 'false', - implode(' ', array_keys($this->pragmas)), + implode(' ', $this->getPragmas()), $source )); } From 3f492a1bf54dc8b62dc42300d28b6ee06c47529a Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Fri, 22 Aug 2014 22:06:02 -0700 Subject: [PATCH 40/43] Add a simple FILTERS pragma example. This includes examples of both section and interpolation filters. --- test/fixtures/examples/filters/Filters.php | 88 +++++++++++++++++++ .../examples/filters/filters.mustache | 4 + test/fixtures/examples/filters/filters.txt | 50 +++++++++++ 3 files changed, 142 insertions(+) create mode 100644 test/fixtures/examples/filters/Filters.php create mode 100644 test/fixtures/examples/filters/filters.mustache create mode 100644 test/fixtures/examples/filters/filters.txt diff --git a/test/fixtures/examples/filters/Filters.php b/test/fixtures/examples/filters/Filters.php new file mode 100644 index 0000000..1899ab4 --- /dev/null +++ b/test/fixtures/examples/filters/Filters.php @@ -0,0 +1,88 @@ + 'Alabama', + 'ak' => 'Alaska', + 'az' => 'Arizona', + 'ar' => 'Arkansas', + 'ca' => 'California', + 'co' => 'Colorado', + 'ct' => 'Connecticut', + 'de' => 'Delaware', + 'fl' => 'Florida', + 'ga' => 'Georgia', + 'hi' => 'Hawaii', + 'id' => 'Idaho', + 'il' => 'Illinois', + 'in' => 'Indiana', + 'ia' => 'Iowa', + 'ks' => 'Kansas', + 'ky' => 'Kentucky', + 'la' => 'Louisiana', + 'me' => 'Maine', + 'md' => 'Maryland', + 'ma' => 'Massachusetts', + 'mi' => 'Michigan', + 'mn' => 'Minnesota', + 'ms' => 'Mississippi', + 'mo' => 'Missouri', + 'mt' => 'Montana', + 'ne' => 'Nebraska', + 'nv' => 'Nevada', + 'nh' => 'New Hampshire', + 'nj' => 'New Jersey', + 'nm' => 'New Mexico', + 'ny' => 'New York', + 'nc' => 'North Carolina', + 'nd' => 'North Dakota', + 'oh' => 'Ohio', + 'ok' => 'Oklahoma', + 'or' => 'Oregon', + 'pa' => 'Pennsylvania', + 'ri' => 'Rhode Island', + 'sc' => 'South Carolina', + 'sd' => 'South Dakota', + 'tn' => 'Tennessee', + 'tx' => 'Texas', + 'ut' => 'Utah', + 'vt' => 'Vermont', + 'va' => 'Virginia', + 'wa' => 'Washington', + 'wv' => 'West Virginia', + 'wi' => 'Wisconsin', + 'wy' => 'Wyoming', + ); + + // The next few functions are ugly, because they have to work in PHP 5.2... + // for everyone who doesn't have to support 5.2, please, for the love, make + // your ViewModel return closures rather than `array($this, '...')` + // + // :) + + public function upcase() + { + return array($this, '_upcase'); + } + + public function _upcase($val) + { + return strtoupper($val); + } + + public function eachPair() + { + return array($this, '_eachPair'); + } + + public function _eachPair($val) + { + $ret = array(); + foreach ($val as $key => $value) { + array_push($ret, compact('key', 'value')); + } + + return $ret; + } +} diff --git a/test/fixtures/examples/filters/filters.mustache b/test/fixtures/examples/filters/filters.mustache new file mode 100644 index 0000000..50f1465 --- /dev/null +++ b/test/fixtures/examples/filters/filters.mustache @@ -0,0 +1,4 @@ +{{%FILTERS}} +{{# states | eachPair }} +{{ key | upcase }}: {{ value }} +{{/ states }} \ No newline at end of file diff --git a/test/fixtures/examples/filters/filters.txt b/test/fixtures/examples/filters/filters.txt new file mode 100644 index 0000000..67466fd --- /dev/null +++ b/test/fixtures/examples/filters/filters.txt @@ -0,0 +1,50 @@ +AL: Alabama +AK: Alaska +AZ: Arizona +AR: Arkansas +CA: California +CO: Colorado +CT: Connecticut +DE: Delaware +FL: Florida +GA: Georgia +HI: Hawaii +ID: Idaho +IL: Illinois +IN: Indiana +IA: Iowa +KS: Kansas +KY: Kentucky +LA: Louisiana +ME: Maine +MD: Maryland +MA: Massachusetts +MI: Michigan +MN: Minnesota +MS: Mississippi +MO: Missouri +MT: Montana +NE: Nebraska +NV: Nevada +NH: New Hampshire +NJ: New Jersey +NM: New Mexico +NY: New York +NC: North Carolina +ND: North Dakota +OH: Ohio +OK: Oklahoma +OR: Oregon +PA: Pennsylvania +RI: Rhode Island +SC: South Carolina +SD: South Dakota +TN: Tennessee +TX: Texas +UT: Utah +VT: Vermont +VA: Virginia +WA: Washington +WV: West Virginia +WI: Wisconsin +WY: Wyoming From a257fb8acdb9c13d0dab526ac96522cb3f764e1a Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Fri, 22 Aug 2014 22:31:36 -0700 Subject: [PATCH 41/43] Require HHVM to pass now. https://github.com/facebook/hhvm/pull/1860 --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index e8b83bc..fa13c65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,3 @@ php: - 5.5 - 5.6 - hhvm - -matrix: - allow_failures: - - php: hhvm # See https://github.com/facebook/hhvm/pull/1860 \ No newline at end of file From f34f6699c21a6f9c0947514d07c810255d28dde4 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 25 Aug 2014 11:18:09 -0700 Subject: [PATCH 42/43] Increase test coverage. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … fix a bug exposed by increased test coverage :-/ --- src/Mustache/Loader/FilesystemLoader.php | 2 +- test/Mustache/Test/EngineTest.php | 10 +++++++ test/Mustache/Test/ParserTest.php | 36 ++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/Mustache/Loader/FilesystemLoader.php b/src/Mustache/Loader/FilesystemLoader.php index 8a27b01..7cbf9cd 100644 --- a/src/Mustache/Loader/FilesystemLoader.php +++ b/src/Mustache/Loader/FilesystemLoader.php @@ -49,7 +49,7 @@ class Mustache_Loader_FilesystemLoader implements Mustache_Loader { $this->baseDir = $baseDir; - if (strpos($this->baseDir, '://') === -1) { + if (strpos($this->baseDir, '://') === false) { $this->baseDir = realpath($this->baseDir); } diff --git a/test/Mustache/Test/EngineTest.php b/test/Mustache/Test/EngineTest.php index c437a04..f61f876 100644 --- a/test/Mustache/Test/EngineTest.php +++ b/test/Mustache/Test/EngineTest.php @@ -321,6 +321,16 @@ class Mustache_Test_EngineTest extends Mustache_Test_FunctionalTestCase $this->assertContains("WARNING: Partial not found: \"bar\"", $log); } + /** + * @expectedException Mustache_Exception_InvalidArgumentException + */ + public function testUnknownPragmaThrowsException() + { + new Mustache_Engine(array( + 'pragmas' => array('UNKNOWN') + )); + } + private function getLoggedMustache($level = Mustache_Logger::ERROR) { $name = tempnam(sys_get_temp_dir(), 'mustache-test'); diff --git a/test/Mustache/Test/ParserTest.php b/test/Mustache/Test/ParserTest.php index 08aed5b..eccfc3d 100644 --- a/test/Mustache/Test/ParserTest.php +++ b/test/Mustache/Test/ParserTest.php @@ -149,6 +149,42 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase ), ), ), + + array( + array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_TEXT, + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::VALUE => " ", + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_DELIM_CHANGE, + Mustache_Tokenizer::LINE => 0, + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_TEXT, + Mustache_Tokenizer::LINE => 0, + Mustache_Tokenizer::VALUE => " \n", + ), + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_ESCAPED, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '[[', + Mustache_Tokenizer::CTAG => ']]', + Mustache_Tokenizer::LINE => 1, + ), + ), + array( + array( + Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_ESCAPED, + Mustache_Tokenizer::NAME => 'foo', + Mustache_Tokenizer::OTAG => '[[', + Mustache_Tokenizer::CTAG => ']]', + Mustache_Tokenizer::LINE => 1, + ), + ), + ), + ); } From c8e78df703977a11f4c43420b66be276be9811d0 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Tue, 26 Aug 2014 12:50:01 -0700 Subject: [PATCH 43/43] Bump to v2.7.0 --- src/Mustache/Engine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mustache/Engine.php b/src/Mustache/Engine.php index ef08ed2..4cac394 100644 --- a/src/Mustache/Engine.php +++ b/src/Mustache/Engine.php @@ -23,7 +23,7 @@ */ class Mustache_Engine { - const VERSION = '2.6.1'; + const VERSION = '2.7.0'; const SPEC_VERSION = '1.1.2'; const PRAGMA_FILTERS = 'FILTERS';