Permalink
Browse files

Update to proposed PSR-1 coding standard.

 * 4 space (no tab) indent
 * K&R-style braces
  • Loading branch information...
1 parent be5cfb3 commit 2448992cc88553c0395c64cc3cded8307f97b384 @bobthecow committed Mar 24, 2012
Showing with 3,508 additions and 3,234 deletions.
  1. +33 −33 bin/create_example.php
  2. +47 −43 src/Mustache/Autoloader.php
  3. +366 −351 src/Mustache/Compiler.php
  4. +123 −115 src/Mustache/Context.php
  5. +563 −531 src/Mustache/Engine.php
  6. +153 −141 src/Mustache/HelperCollection.php
  7. +10 −9 src/Mustache/Loader.php
  8. +44 −39 src/Mustache/Loader/ArrayLoader.php
  9. +79 −74 src/Mustache/Loader/FilesystemLoader.php
  10. +15 −14 src/Mustache/Loader/MutableLoader.php
  11. +13 −11 src/Mustache/Loader/StringLoader.php
  12. +59 −56 src/Mustache/Parser.php
  13. +121 −115 src/Mustache/Template.php
  14. +240 −233 src/Mustache/Tokenizer.php
  15. +17 −14 test/Mustache/Test/AutoloaderTest.php
  16. +80 −75 test/Mustache/Test/CompilerTest.php
  17. +99 −91 test/Mustache/Test/ContextTest.php
  18. +238 −222 test/Mustache/Test/EngineTest.php
  19. +18 −14 test/Mustache/Test/Functional/CallTest.php
  20. +126 −121 test/Mustache/Test/Functional/ExamplesTest.php
  21. +71 −59 test/Mustache/Test/Functional/HigherOrderSectionsTest.php
  22. +101 −89 test/Mustache/Test/Functional/MustacheInjectionTest.php
  23. +157 −140 test/Mustache/Test/Functional/MustacheSpecTest.php
  24. +83 −67 test/Mustache/Test/Functional/ObjectSectionTest.php
  25. +25 −18 test/Mustache/Test/HelperCollectionTest.php
  26. +32 −28 test/Mustache/Test/Loader/ArrayLoaderTest.php
  27. +32 −27 test/Mustache/Test/Loader/FilesystemLoaderTest.php
  28. +9 −7 test/Mustache/Test/Loader/StringLoaderTest.php
  29. +156 −153 test/Mustache/Test/ParserTest.php
  30. +36 −30 test/Mustache/Test/TemplateTest.php
  31. +121 −118 test/Mustache/Test/TokenizerTest.php
  32. +3 −2 test/fixtures/autoloader/Mustache/Bar.php
  33. +3 −2 test/fixtures/autoloader/Mustache/Foo.php
  34. +3 −2 test/fixtures/autoloader/NonMustacheClass.php
  35. +10 −9 test/fixtures/examples/child_context/ChildContext.php
  36. +6 −4 test/fixtures/examples/comments/Comments.php
  37. +16 −13 test/fixtures/examples/complex/complex.php
  38. +11 −9 test/fixtures/examples/delimiters/Delimiters.php
  39. +11 −10 test/fixtures/examples/dot_notation/DotNotation.php
  40. +7 −5 test/fixtures/examples/double_section/DoubleSection.php
  41. +3 −2 test/fixtures/examples/escaped/Escaped.php
  42. +18 −16 test/fixtures/examples/grand_parent_context/GrandParentContext.php
  43. +4 −2 test/fixtures/examples/i18n/I18n.php
  44. +3 −2 test/fixtures/examples/implicit_iterator/ImplicitIterator.php
  45. +4 −3 test/fixtures/examples/inverted_double_section/InvertedDoubleSection.php
  46. +3 −2 test/fixtures/examples/inverted_section/InvertedSection.php
  47. +10 −9 test/fixtures/examples/recursive_partials/RecursivePartials.php
  48. +12 −10 test/fixtures/examples/section_iterator_objects/SectionIteratorObjects.php
  49. +22 −17 test/fixtures/examples/section_magic_objects/SectionMagicObjects.php
  50. +12 −9 test/fixtures/examples/section_objects/SectionObjects.php
  51. +11 −9 test/fixtures/examples/sections/Sections.php
  52. +31 −29 test/fixtures/examples/sections_nested/SectionsNested.php
  53. +9 −7 test/fixtures/examples/simple/Simple.php
  54. +3 −2 test/fixtures/examples/unescaped/Unescaped.php
  55. +3 −2 test/fixtures/examples/utf8/UTF8.php
  56. +3 −2 test/fixtures/examples/utf8_unescaped/UTF8Unescaped.php
  57. +20 −17 test/fixtures/examples/whitespace/Whitespace.php
View
66 bin/create_example.php
@@ -38,10 +38,10 @@
* @return string
*/
function getLowerCaseName($name) {
- return preg_replace_callback("/([A-Z])/", create_function (
- '$match',
- 'return "_" . strtolower($match[1]);'
- ), lcfirst($name));
+ return preg_replace_callback("/([A-Z])/", create_function (
+ '$match',
+ 'return "_" . strtolower($match[1]);'
+ ), lcfirst($name));
}
/**
@@ -57,10 +57,10 @@ function getLowerCaseName($name) {
* @return string
*/
function getUpperCaseName($name) {
- return preg_replace_callback("/_([a-z])/", create_function (
- '$match',
- 'return strtoupper($match{1});'
- ), ucfirst($name));
+ return preg_replace_callback("/_([a-z])/", create_function (
+ '$match',
+ 'return strtoupper($match{1});'
+ ), ucfirst($name));
}
@@ -72,8 +72,8 @@ function getUpperCaseName($name) {
* @return mixed
*/
function out($value) {
- echo $value . "\n";
- return $value;
+ echo $value . "\n";
+ return $value;
}
/**
@@ -89,8 +89,8 @@ function out($value) {
* @return string
*/
function buildPath($directory, $filename = null, $extension = null) {
- return out(EXAMPLE_PATH . '/' . $directory.
- ($extension !== null && $filename !== null ? '/' . $filename. "." . $extension : ""));
+ return out(EXAMPLE_PATH . '/' . $directory.
+ ($extension !== null && $filename !== null ? '/' . $filename. "." . $extension : ""));
}
/**
@@ -102,9 +102,9 @@ function buildPath($directory, $filename = null, $extension = null) {
* @return void
*/
function createDirectory($directory) {
- if(!@mkdir(buildPath($directory))) {
- die("FAILED to create directory\n");
- }
+ if(!@mkdir(buildPath($directory))) {
+ die("FAILED to create directory\n");
+ }
}
/**
@@ -119,13 +119,13 @@ function createDirectory($directory) {
* @return void
*/
function createFile($directory, $filename, $extension, $content = "") {
- $handle = @fopen(buildPath($directory, $filename, $extension), "w");
- if($handle) {
- fwrite($handle, $content);
- fclose($handle);
- } else {
- die("FAILED to create file\n");
- }
+ $handle = @fopen(buildPath($directory, $filename, $extension), "w");
+ if($handle) {
+ fwrite($handle, $content);
+ fclose($handle);
+ } else {
+ die("FAILED to create file\n");
+ }
}
@@ -143,29 +143,29 @@ function createFile($directory, $filename, $extension, $content = "") {
* @return void
*/
function main($example_name) {
- $lowercase = getLowerCaseName($example_name);
- $uppercase = getUpperCaseName($example_name);
- createDirectory($lowercase);
- createFile($lowercase, $lowercase, "mustache");
- createFile($lowercase, $lowercase, "txt");
- createFile($lowercase, $uppercase, "php", <<<CONTENT
+ $lowercase = getLowerCaseName($example_name);
+ $uppercase = getUpperCaseName($example_name);
+ createDirectory($lowercase);
+ createFile($lowercase, $lowercase, "mustache");
+ createFile($lowercase, $lowercase, "txt");
+ createFile($lowercase, $uppercase, "php", <<<CONTENT
<?php
class {$uppercase} {
}
CONTENT
- );
+ );
}
// check if enougth arguments are given
if(count($argv) > 1) {
- // get the name of the example
- $example_name = $argv[1];
+ // get the name of the example
+ $example_name = $argv[1];
- main($example_name);
+ main($example_name);
} else {
- echo USAGE;
+ echo USAGE;
}
View
90 src/Mustache/Autoloader.php
@@ -12,54 +12,58 @@
/**
* Mustache class autoloader.
*/
-class Mustache_Autoloader {
+class Mustache_Autoloader
+{
- private $baseDir;
+ private $baseDir;
- /**
- * Autoloader constructor.
- *
- * @param string $baseDir Mustache library base directory (default: dirname(__FILE__).'/..')
- */
- public function __construct($baseDir = null) {
- if ($baseDir === null) {
- $this->baseDir = dirname(__FILE__).'/..';
- } else {
- $this->baseDir = rtrim($baseDir, '/');
- }
- }
+ /**
+ * Autoloader constructor.
+ *
+ * @param string $baseDir Mustache library base directory (default: dirname(__FILE__).'/..')
+ */
+ public function __construct($baseDir = null)
+ {
+ if ($baseDir === null) {
+ $this->baseDir = dirname(__FILE__).'/..';
+ } else {
+ $this->baseDir = rtrim($baseDir, '/');
+ }
+ }
- /**
- * Register a new instance as an SPL autoloader.
- *
- * @param string $baseDir Mustache library base directory (default: dirname(__FILE__).'/..')
- *
- * @return Mustache_Autoloader Registered Autoloader instance
- */
- static public function register($baseDir = null) {
- $loader = new self($baseDir);
- spl_autoload_register(array($loader, 'autoload'));
+ /**
+ * Register a new instance as an SPL autoloader.
+ *
+ * @param string $baseDir Mustache library base directory (default: dirname(__FILE__).'/..')
+ *
+ * @return Mustache_Autoloader Registered Autoloader instance
+ */
+ static public function register($baseDir = null)
+ {
+ $loader = new self($baseDir);
+ spl_autoload_register(array($loader, 'autoload'));
- return $loader;
- }
+ return $loader;
+ }
- /**
- * Autoload Mustache classes.
- *
- * @param string $class
- */
- public function autoload($class) {
- if ($class[0] === '\\') {
- $class = substr($class, 1);
- }
+ /**
+ * Autoload Mustache classes.
+ *
+ * @param string $class
+ */
+ public function autoload($class)
+ {
+ if ($class[0] === '\\') {
+ $class = substr($class, 1);
+ }
- if (strpos($class, 'Mustache') !== 0) {
- return;
- }
+ if (strpos($class, 'Mustache') !== 0) {
+ return;
+ }
- $file = sprintf('%s/%s.php', $this->baseDir, str_replace('_', '/', $class));
- if (is_file($file)) {
- require $file;
- }
- }
+ $file = sprintf('%s/%s.php', $this->baseDir, str_replace('_', '/', $class));
+ if (is_file($file)) {
+ require $file;
+ }
+ }
}
View
717 src/Mustache/Compiler.php
@@ -14,355 +14,370 @@
*
* This class is responsible for turning a Mustache token parse tree into normal PHP source code.
*/
-class Mustache_Compiler {
-
- private $sections;
- private $source;
- private $indentNextLine;
- private $customEscape;
- private $charset;
-
- /**
- * Compile a Mustache token parse tree into PHP source code.
- *
- * @param string $source Mustache Template source code
- * @param string $tree Parse tree of Mustache tokens
- * @param string $name Mustache Template class name
- *
- * @return string Generated PHP source code
- */
- public function compile($source, array $tree, $name, $customEscape = false, $charset = 'UTF-8') {
- $this->sections = array();
- $this->source = $source;
- $this->indentNextLine = true;
- $this->customEscape = $customEscape;
- $this->charset = $charset;
-
- return $this->writeCode($tree, $name);
- }
-
- /**
- * Helper function for walking the Mustache token parse tree.
- *
- * @throws InvalidArgumentException upon encountering unknown token types.
- *
- * @param array $tree Parse tree of Mustache tokens
- * @param int $level (default: 0)
- *
- * @return string Generated PHP source code;
- */
- private function walk(array $tree, $level = 0) {
- $code = '';
- $level++;
- foreach ($tree as $node) {
- switch ($node[Mustache_Tokenizer::TYPE]) {
- case Mustache_Tokenizer::T_SECTION:
- $code .= $this->section(
- $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_INVERTED:
- $code .= $this->invertedSection(
- $node[Mustache_Tokenizer::NODES],
- $node[Mustache_Tokenizer::NAME],
- $level
- );
- 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] : '',
- $level
- );
- break;
-
- case Mustache_Tokenizer::T_UNESCAPED:
- case Mustache_Tokenizer::T_UNESCAPED_2:
- $code .= $this->variable($node[Mustache_Tokenizer::NAME], false, $level);
- break;
-
- case Mustache_Tokenizer::T_COMMENT:
- break;
-
- case Mustache_Tokenizer::T_ESCAPED:
- $code .= $this->variable($node[Mustache_Tokenizer::NAME], true, $level);
- break;
-
-
- case Mustache_Tokenizer::T_TEXT:
- $code .= $this->text($node[Mustache_Tokenizer::VALUE], $level);
- break;
-
- default:
- throw new InvalidArgumentException('Unknown node type: '.json_encode($node));
- }
- }
-
- return $code;
- }
-
- const KLASS = '<?php
-
- class %s extends Mustache_Template {
- public function renderInternal(Mustache_Context $context, $indent = \'\', $escape = false) {
- $buffer = \'\';
- %s
-
- if ($escape) {
- return %s;
- } else {
- return $buffer;
- }
- }
- %s
- }';
-
- /**
- * Generate Mustache Template class PHP source.
- *
- * @param array $tree Parse tree of Mustache tokens
- * @param string $name Mustache Template class name
- *
- * @return string Generated PHP source code
- */
- private function writeCode($tree, $name) {
- $code = $this->walk($tree);
- $sections = implode("\n", $this->sections);
-
- return sprintf($this->prepare(self::KLASS, 0, false), $name, $code, $this->getEscape('$buffer'), $sections);
- }
-
- const SECTION_CALL = '
- // %s section
- $buffer .= $this->section%s($context, $indent, $context->%s(%s));
- ';
-
- const SECTION = '
- private function section%s(Mustache_Context $context, $indent, $value) {
- $buffer = \'\';
- if (!is_string($value) && is_callable($value)) {
- $source = %s;
- $buffer .= $this->mustache
- ->loadLambda((string) call_user_func($value, $source)%s)
- ->renderInternal($context, $indent);
- } elseif (!empty($value)) {
- $values = $this->isIterable($value) ? $value : array($value);
- foreach ($values as $value) {
- $context->push($value);%s
- $context->pop();
- }
- }
-
- return $buffer;
- }';
-
- /**
- * 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
- *
- * @return string Generated section PHP source code
- */
- private function section($nodes, $id, $start, $end, $otag, $ctag, $level) {
- $method = $this->getFindMethod($id);
- $id = var_export($id, true);
- $source = var_export(substr($this->source, $start, $end - $start), true);
-
- 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, $source, $delims, $this->walk($nodes, 2));
- }
-
- return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $key, $method, $id);
- }
-
- const INVERTED_SECTION = '
- // %s inverted section
- $value = $context->%s(%s);
- if (empty($value)) {
- %s
- }';
-
- /**
- * Generate Mustache Template inverted section PHP source.
- *
- * @param array $nodes Array of child tokens
- * @param string $id Section name
- * @param int $level
- *
- * @return string Generated inverted section PHP source code
- */
- private function invertedSection($nodes, $id, $level) {
- $method = $this->getFindMethod($id);
- $id = var_export($id, true);
-
- return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $this->walk($nodes, $level));
- }
-
- const PARTIAL = '
- if ($partial = $this->mustache->loadPartial(%s)) {
- $buffer .= $partial->renderInternal($context, %s);
- }
- ';
-
- /**
- * Generate Mustache Template partial call PHP source.
- *
- * @param string $id Partial name
- * @param string $indent Whitespace indent to apply to partial
- * @param int $level
- *
- * @return string Generated partial call PHP source code
- */
- private function partial($id, $indent, $level) {
- return sprintf(
- $this->prepare(self::PARTIAL, $level),
- var_export($id, true),
- var_export($indent, true)
- );
- }
-
- const VARIABLE = '
- $value = $context->%s(%s);
- if (!is_string($value) && is_callable($value)) {
- $value = $this->mustache
- ->loadLambda((string) call_user_func($value))
- ->renderInternal($context, $indent);
- }
- $buffer .= %s%s;
- ';
-
- /**
- * Generate Mustache Template variable interpolation PHP source.
- *
- * @param string $id Variable name
- * @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) {
- $method = $this->getFindMethod($id);
- $id = ($method !== 'last') ? var_export($id, true) : '';
- $value = $escape ? $this->getEscape() : '$value';
-
- return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $this->flushIndent(), $value);
- }
-
- const LINE = '$buffer .= "\n";';
- const TEXT = '$buffer .= %s%s;';
-
- /**
- * Generate Mustache Template output Buffer call PHP source.
- *
- * @param string $text
- * @param int $level
- *
- * @return string Generated output Buffer call PHP source
- */
- private function text($text, $level) {
- if ($text === "\n") {
- $this->indentNextLine = true;
-
- return $this->prepare(self::LINE, $level);
- } else {
- return sprintf($this->prepare(self::TEXT, $level), $this->flushIndent(), var_export($text, true));
- }
- }
-
- /**
- * Prepare PHP source code snippet for output.
- *
- * @param string $text
- * @param int $bonus Additional indent level (default: 0)
- * @param boolean $prependNewline Prepend a newline to the snippet? (default: true)
- *
- * @return string PHP source code snippet
- */
- private function prepare($text, $bonus = 0, $prependNewline = true) {
- $text = ($prependNewline ? "\n" : '').trim($text);
- if ($prependNewline) {
- $bonus++;
- }
-
- return preg_replace("/\n(\t\t)?/", "\n".str_repeat("\t", $bonus), $text);
- }
-
- const DEFAULT_ESCAPE = 'htmlspecialchars(%s, ENT_COMPAT, %s)';
- const CUSTOM_ESCAPE = 'call_user_func($this->mustache->getEscape(), %s)';
-
- /**
- * Get the current escaper.
- *
- * @return string Either a custom callback, or an inline call to `htmlspecialchars`
- */
- private function getEscape($value = '$value') {
- if ($this->customEscape) {
- return sprintf(self::CUSTOM_ESCAPE, $value);
- } else {
- return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->charset, true));
- }
- }
-
- /**
- * Select the appropriate Context `find` method for a given $id.
- *
- * The return value will be one of `find`, `findDot` or `last`.
- *
- * @see Mustache_Context::find
- * @see Mustache_Context::findDot
- * @see Mustache_Context::last
- *
- * @param string $id Variable name
- *
- * @return string `find` method name
- */
- private function getFindMethod($id) {
- if ($id === '.') {
- return 'last';
- } elseif (strpos($id, '.') === false) {
- return 'find';
- } else {
- return 'findDot';
- }
- }
-
- const LINE_INDENT = '$indent . ';
-
- /**
- * Get the current $indent prefix to write to the buffer.
- *
- * @return string "$indent . " or ""
- */
- private function flushIndent() {
- if ($this->indentNextLine) {
- $this->indentNextLine = false;
-
- return self::LINE_INDENT;
- } else {
- return '';
- }
- }
+class Mustache_Compiler
+{
+
+ private $sections;
+ private $source;
+ private $indentNextLine;
+ private $customEscape;
+ private $charset;
+
+ /**
+ * Compile a Mustache token parse tree into PHP source code.
+ *
+ * @param string $source Mustache Template source code
+ * @param string $tree Parse tree of Mustache tokens
+ * @param string $name Mustache Template class name
+ *
+ * @return string Generated PHP source code
+ */
+ public function compile($source, array $tree, $name, $customEscape = false, $charset = 'UTF-8')
+ {
+ $this->sections = array();
+ $this->source = $source;
+ $this->indentNextLine = true;
+ $this->customEscape = $customEscape;
+ $this->charset = $charset;
+
+ return $this->writeCode($tree, $name);
+ }
+
+ /**
+ * Helper function for walking the Mustache token parse tree.
+ *
+ * @throws InvalidArgumentException upon encountering unknown token types.
+ *
+ * @param array $tree Parse tree of Mustache tokens
+ * @param int $level (default: 0)
+ *
+ * @return string Generated PHP source code;
+ */
+ private function walk(array $tree, $level = 0)
+ {
+ $code = '';
+ $level++;
+ foreach ($tree as $node) {
+ switch ($node[Mustache_Tokenizer::TYPE]) {
+ case Mustache_Tokenizer::T_SECTION:
+ $code .= $this->section(
+ $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_INVERTED:
+ $code .= $this->invertedSection(
+ $node[Mustache_Tokenizer::NODES],
+ $node[Mustache_Tokenizer::NAME],
+ $level
+ );
+ 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] : '',
+ $level
+ );
+ break;
+
+ case Mustache_Tokenizer::T_UNESCAPED:
+ case Mustache_Tokenizer::T_UNESCAPED_2:
+ $code .= $this->variable($node[Mustache_Tokenizer::NAME], false, $level);
+ break;
+
+ case Mustache_Tokenizer::T_COMMENT:
+ break;
+
+ case Mustache_Tokenizer::T_ESCAPED:
+ $code .= $this->variable($node[Mustache_Tokenizer::NAME], true, $level);
+ break;
+
+
+ case Mustache_Tokenizer::T_TEXT:
+ $code .= $this->text($node[Mustache_Tokenizer::VALUE], $level);
+ break;
+
+ default:
+ throw new InvalidArgumentException('Unknown node type: '.json_encode($node));
+ }
+ }
+
+ return $code;
+ }
+
+ const KLASS = '<?php
+
+ class %s extends Mustache_Template
+ {
+ public function renderInternal(Mustache_Context $context, $indent = \'\', $escape = false)
+ {
+ $buffer = \'\';
+ %s
+
+ if ($escape) {
+ return %s;
+ } else {
+ return $buffer;
+ }
+ }
+ %s
+ }';
+
+ /**
+ * Generate Mustache Template class PHP source.
+ *
+ * @param array $tree Parse tree of Mustache tokens
+ * @param string $name Mustache Template class name
+ *
+ * @return string Generated PHP source code
+ */
+ private function writeCode($tree, $name)
+ {
+ $code = $this->walk($tree);
+ $sections = implode("\n", $this->sections);
+
+ return sprintf($this->prepare(self::KLASS, 0, false), $name, $code, $this->getEscape('$buffer'), $sections);
+ }
+
+ const SECTION_CALL = '
+ // %s section
+ $buffer .= $this->section%s($context, $indent, $context->%s(%s));
+ ';
+
+ const SECTION = '
+ private function section%s(Mustache_Context $context, $indent, $value) {
+ $buffer = \'\';
+ if (!is_string($value) && is_callable($value)) {
+ $source = %s;
+ $buffer .= $this->mustache
+ ->loadLambda((string) call_user_func($value, $source)%s)
+ ->renderInternal($context, $indent);
+ } elseif (!empty($value)) {
+ $values = $this->isIterable($value) ? $value : array($value);
+ foreach ($values as $value) {
+ $context->push($value);%s
+ $context->pop();
+ }
+ }
+
+ return $buffer;
+ }';
+
+ /**
+ * 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
+ *
+ * @return string Generated section PHP source code
+ */
+ private function section($nodes, $id, $start, $end, $otag, $ctag, $level)
+ {
+ $method = $this->getFindMethod($id);
+ $id = var_export($id, true);
+ $source = var_export(substr($this->source, $start, $end - $start), true);
+
+ 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, $source, $delims, $this->walk($nodes, 2));
+ }
+
+ return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $key, $method, $id);
+ }
+
+ const INVERTED_SECTION = '
+ // %s inverted section
+ $value = $context->%s(%s);
+ if (empty($value)) {
+ %s
+ }';
+
+ /**
+ * Generate Mustache Template inverted section PHP source.
+ *
+ * @param array $nodes Array of child tokens
+ * @param string $id Section name
+ * @param int $level
+ *
+ * @return string Generated inverted section PHP source code
+ */
+ private function invertedSection($nodes, $id, $level)
+ {
+ $method = $this->getFindMethod($id);
+ $id = var_export($id, true);
+
+ return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $this->walk($nodes, $level));
+ }
+
+ const PARTIAL = '
+ if ($partial = $this->mustache->loadPartial(%s)) {
+ $buffer .= $partial->renderInternal($context, %s);
+ }
+ ';
+
+ /**
+ * Generate Mustache Template partial call PHP source.
+ *
+ * @param string $id Partial name
+ * @param string $indent Whitespace indent to apply to partial
+ * @param int $level
+ *
+ * @return string Generated partial call PHP source code
+ */
+ private function partial($id, $indent, $level)
+ {
+ return sprintf(
+ $this->prepare(self::PARTIAL, $level),
+ var_export($id, true),
+ var_export($indent, true)
+ );
+ }
+
+ const VARIABLE = '
+ $value = $context->%s(%s);
+ if (!is_string($value) && is_callable($value)) {
+ $value = $this->mustache
+ ->loadLambda((string) call_user_func($value))
+ ->renderInternal($context, $indent);
+ }
+ $buffer .= %s%s;
+ ';
+
+ /**
+ * Generate Mustache Template variable interpolation PHP source.
+ *
+ * @param string $id Variable name
+ * @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)
+ {
+ $method = $this->getFindMethod($id);
+ $id = ($method !== 'last') ? var_export($id, true) : '';
+ $value = $escape ? $this->getEscape() : '$value';
+
+ return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $this->flushIndent(), $value);
+ }
+
+ const LINE = '$buffer .= "\n";';
+ const TEXT = '$buffer .= %s%s;';
+
+ /**
+ * Generate Mustache Template output Buffer call PHP source.
+ *
+ * @param string $text
+ * @param int $level
+ *
+ * @return string Generated output Buffer call PHP source
+ */
+ private function text($text, $level)
+ {
+ if ($text === "\n") {
+ $this->indentNextLine = true;
+
+ return $this->prepare(self::LINE, $level);
+ } else {
+ return sprintf($this->prepare(self::TEXT, $level), $this->flushIndent(), var_export($text, true));
+ }
+ }
+
+ /**
+ * Prepare PHP source code snippet for output.
+ *
+ * @param string $text
+ * @param int $bonus Additional indent level (default: 0)
+ * @param boolean $prependNewline Prepend a newline to the snippet? (default: true)
+ *
+ * @return string PHP source code snippet
+ */
+ private function prepare($text, $bonus = 0, $prependNewline = true)
+ {
+ $text = ($prependNewline ? "\n" : '').trim($text);
+ if ($prependNewline) {
+ $bonus++;
+ }
+
+ return preg_replace("/\n( {8})?/", "\n".str_repeat(" ", $bonus * 4), $text);
+ }
+
+ const DEFAULT_ESCAPE = 'htmlspecialchars(%s, ENT_COMPAT, %s)';
+ const CUSTOM_ESCAPE = 'call_user_func($this->mustache->getEscape(), %s)';
+
+ /**
+ * Get the current escaper.
+ *
+ * @return string Either a custom callback, or an inline call to `htmlspecialchars`
+ */
+ private function getEscape($value = '$value')
+ {
+ if ($this->customEscape) {
+ return sprintf(self::CUSTOM_ESCAPE, $value);
+ } else {
+ return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->charset, true));
+ }
+ }
+
+ /**
+ * Select the appropriate Context `find` method for a given $id.
+ *
+ * The return value will be one of `find`, `findDot` or `last`.
+ *
+ * @see Mustache_Context::find
+ * @see Mustache_Context::findDot
+ * @see Mustache_Context::last
+ *
+ * @param string $id Variable name
+ *
+ * @return string `find` method name
+ */
+ private function getFindMethod($id)
+ {
+ if ($id === '.') {
+ return 'last';
+ } elseif (strpos($id, '.') === false) {
+ return 'find';
+ } else {
+ return 'findDot';
+ }
+ }
+
+ const LINE_INDENT = '$indent . ';
+
+ /**
+ * Get the current $indent prefix to write to the buffer.
+ *
+ * @return string "$indent . " or ""
+ */
+ private function flushIndent()
+ {
+ if ($this->indentNextLine) {
+ $this->indentNextLine = false;
+
+ return self::LINE_INDENT;
+ } else {
+ return '';
+ }
+ }
}
View
238 src/Mustache/Context.php
@@ -12,130 +12,138 @@
/**
* Mustache Template rendering Context.
*/
-class Mustache_Context {
- private $stack = array();
+class Mustache_Context
+{
+ private $stack = array();
- /**
- * Mustache rendering Context constructor.
- *
- * @param mixed $context Default rendering context (default: null)
- */
- public function __construct($context = null) {
- if ($context !== null) {
- $this->stack = array($context);
- }
- }
+ /**
+ * Mustache rendering Context constructor.
+ *
+ * @param mixed $context Default rendering context (default: null)
+ */
+ public function __construct($context = null)
+ {
+ if ($context !== null) {
+ $this->stack = array($context);
+ }
+ }
- /**
- * Push a new Context frame onto the stack.
- *
- * @param mixed $value Object or array to use for context
- */
- public function push($value) {
- array_push($this->stack, $value);
- }
+ /**
+ * Push a new Context frame onto the stack.
+ *
+ * @param mixed $value Object or array to use for context
+ */
+ public function push($value)
+ {
+ array_push($this->stack, $value);
+ }
- /**
- * Pop the last Context frame from the stack.
- *
- * @return mixed Last Context frame (object or array)
- */
- public function pop() {
- return array_pop($this->stack);
- }
+ /**
+ * Pop the last Context frame from the stack.
+ *
+ * @return mixed Last Context frame (object or array)
+ */
+ public function pop()
+ {
+ return array_pop($this->stack);
+ }
- /**
- * Get the last Context frame.
- *
- * @return mixed Last Context frame (object or array)
- */
- public function last() {
- return end($this->stack);
- }
+ /**
+ * Get the last Context frame.
+ *
+ * @return mixed Last Context frame (object or array)
+ */
+ public function last()
+ {
+ return end($this->stack);
+ }
- /**
- * Find a variable in the Context stack.
- *
- * Starting with the last Context frame (the context of the innermost section), and working back to the top-level
- * rendering context, look for a variable with the given name:
- *
- * * If the Context frame is an associative array which contains the key $id, returns the value of that element.
- * * If the Context frame is an object, this will check first for a public method, then a public property named
- * $id. Failing both of these, it will try `__isset` and `__get` magic methods.
- * * If a value named $id is not found in any Context frame, returns an empty string.
- *
- * @param string $id Variable name
- *
- * @return mixed Variable value, or '' if not found
- */
- public function find($id) {
- return $this->findVariableInStack($id, $this->stack);
- }
+ /**
+ * Find a variable in the Context stack.
+ *
+ * Starting with the last Context frame (the context of the innermost section), and working back to the top-level
+ * rendering context, look for a variable with the given name:
+ *
+ * * If the Context frame is an associative array which contains the key $id, returns the value of that element.
+ * * If the Context frame is an object, this will check first for a public method, then a public property named
+ * $id. Failing both of these, it will try `__isset` and `__get` magic methods.
+ * * If a value named $id is not found in any Context frame, returns an empty string.
+ *
+ * @param string $id Variable name
+ *
+ * @return mixed Variable value, or '' if not found
+ */
+ public function find($id)
+ {
+ return $this->findVariableInStack($id, $this->stack);
+ }
- /**
- * Find a 'dot notation' variable in the Context stack.
- *
- * Note that dot notation traversal bubbles through scope differently than the regular find method. After finding
- * the initial chunk of the dotted name, each subsequent chunk is searched for only within the value of the previous
- * result. For example, given the following context stack:
- *
- * $data = array(
- * 'name' => 'Fred',
- * 'child' => array(
- * 'name' => 'Bob'
- * ),
- * );
- *
- * ... and the Mustache following template:
- *
- * {{ child.name }}
- *
- * ... the `name` value is only searched for within the `child` value of the global Context, not within parent
- * Context frames.
- *
- * @param string $id Dotted variable selector
- *
- * @return mixed Variable value, or '' if not found
- */
- public function findDot($id) {
- $chunks = explode('.', $id);
- $first = array_shift($chunks);
- $value = $this->findVariableInStack($first, $this->stack);
+ /**
+ * Find a 'dot notation' variable in the Context stack.
+ *
+ * Note that dot notation traversal bubbles through scope differently than the regular find method. After finding
+ * the initial chunk of the dotted name, each subsequent chunk is searched for only within the value of the previous
+ * result. For example, given the following context stack:
+ *
+ * $data = array(
+ * 'name' => 'Fred',
+ * 'child' => array(
+ * 'name' => 'Bob'
+ * ),
+ * );
+ *
+ * ... and the Mustache following template:
+ *
+ * {{ child.name }}
+ *
+ * ... the `name` value is only searched for within the `child` value of the global Context, not within parent
+ * Context frames.
+ *
+ * @param string $id Dotted variable selector
+ *
+ * @return mixed Variable value, or '' if not found
+ */
+ public function findDot($id)
+ {
+ $chunks = explode('.', $id);
+ $first = array_shift($chunks);
+ $value = $this->findVariableInStack($first, $this->stack);
- foreach ($chunks as $chunk) {
- if ($value === '') {
- return $value;
- }
+ foreach ($chunks as $chunk) {
+ if ($value === '') {
+ return $value;
+ }
- $value = $this->findVariableInStack($chunk, array($value));
- }
+ $value = $this->findVariableInStack($chunk, array($value));
+ }
- return $value;
- }
+ return $value;
+ }
- /**
- * Helper function to find a variable in the Context stack.
- *
- * @see Mustache_Context::find
- *
- * @param string $id Variable name
- * @param array $stack Context stack
- *
- * @return mixed Variable value, or '' if not found
- */
- private function findVariableInStack($id, array $stack) {
- for ($i = count($stack) - 1; $i >= 0; $i--) {
- if (is_object($stack[$i])) {
- if (method_exists($stack[$i], $id)) {
- return $stack[$i]->$id();
- } elseif (isset($stack[$i]->$id)) {
- return $stack[$i]->$id;
- }
- } elseif (is_array($stack[$i]) && array_key_exists($id, $stack[$i])) {
- return $stack[$i][$id];
- }
- }
+ /**
+ * Helper function to find a variable in the Context stack.
+ *
+ * @see Mustache_Context::find
+ *
+ * @param string $id Variable name
+ * @param array $stack Context stack
+ *
+ * @return mixed Variable value, or '' if not found
+ */
+ private function findVariableInStack($id, array $stack)
+ {
+ for ($i = count($stack) - 1; $i >= 0; $i--) {
+ if (is_object($stack[$i])) {
+ if (method_exists($stack[$i], $id)) {
+ return $stack[$i]->$id();
+ } elseif (isset($stack[$i]->$id)) {
+ return $stack[$i]->$id;
+ }
+ } elseif (is_array($stack[$i]) && array_key_exists($id, $stack[$i])) {
+ return $stack[$i][$id];
+ }
+ }
- return '';
- }
+ return '';
+ }
}
View
1,094 src/Mustache/Engine.php
@@ -21,535 +21,567 @@
*
* @author Justin Hileman {@link http://justinhileman.com}
*/
-class Mustache_Engine {
- const VERSION = '2.0.0-dev';
- const SPEC_VERSION = '1.1.2';
-
- // Template cache
- private $templates = array();
-
- // Environment
- private $templateClassPrefix = '__Mustache_';
- private $cache = null;
- private $loader;
- private $partialsLoader;
- private $helpers;
- private $escape;
- private $charset = 'UTF-8';
-
- /**
- * Mustache class constructor.
- *
- * Passing an $options array allows overriding certain Mustache options during instantiation:
- *
- * $options = array(
- * // The class prefix for compiled templates. Defaults to '__Mustache_'
- * 'template_class_prefix' => '__MyTemplates_',
- *
- * // A cache directory for compiled templates. Mustache will not cache templates unless this is set
- * 'cache' => dirname(__FILE__).'/tmp/cache/mustache',
- *
- * // A Mustache template loader instance. Uses a StringLoader if not specified
- * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'),
- *
- * // A Mustache loader instance for partials.
- * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'),
- *
- * // An array of Mustache partials. Useful for quick-and-dirty string template loading, but not as
- * // efficient or lazy as a Filesystem (or database) loader.
- * 'partials' => array('foo' => file_get_contents(dirname(__FILE__).'/views/partials/foo.mustache')),
- *
- * // An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order
- * // sections), or any other valid Mustache context value. They will be prepended to the context stack,
- * // so they will be available in any template loaded by this Mustache instance.
- * 'helpers' => array('i18n' => function($text) {
- * // do something translatey here...
- * }),
- *
- * // An 'escape' callback, responsible for escaping double-mustache variables.
- * 'escape' => function($value) {
- * return htmlspecialchars($buffer, ENT_COMPAT, 'UTF-8');
- * },
- *
- * // character set for `htmlspecialchars`. Defaults to 'UTF-8'
- * 'charset' => 'ISO-8859-1',
- * );
- *
- * @param array $options (default: array())
- */
- public function __construct(array $options = array()) {
- if (isset($options['template_class_prefix'])) {
- $this->templateClassPrefix = $options['template_class_prefix'];
- }
-
- if (isset($options['cache'])) {
- $this->cache = $options['cache'];
- }
-
- if (isset($options['loader'])) {
- $this->setLoader($options['loader']);
- }
-
- if (isset($options['partials_loader'])) {
- $this->setPartialsLoader($options['partials_loader']);
- }
-
- if (isset($options['partials'])) {
- $this->setPartials($options['partials']);
- }
-
- if (isset($options['helpers'])) {
- $this->setHelpers($options['helpers']);
- }
-
- if (isset($options['escape'])) {
- if (!is_callable($options['escape'])) {
- throw new InvalidArgumentException('Mustache Constructor "escape" option must be callable');
- }
-
- $this->escape = $options['escape'];
- }
-
- if (isset($options['charset'])) {
- $this->charset = $options['charset'];
- }
- }
-
- /**
- * Shortcut 'render' invocation.
- *
- * Equivalent to calling `$mustache->loadTemplate($template)->render($data);`
- *
- * @see Mustache_Engine::loadTemplate
- * @see Mustache_Template::render
- *
- * @param string $template
- * @param mixed $data
- *
- * @return string Rendered template
- */
- public function render($template, $data) {
- return $this->loadTemplate($template)->render($data);
- }
-
- /**
- * Get the current Mustache escape callback.
- *
- * @return mixed Callable or null
- */
- public function getEscape() {
- return $this->escape;
- }
-
- /**
- * Get the current Mustache character set.
- *
- * @return string
- */
- public function getCharset() {
- return $this->charset;
- }
-
- /**
- * Set the Mustache template Loader instance.
- *
- * @param Mustache_Loader $loader
- */
- public function setLoader(Mustache_Loader $loader) {
- $this->loader = $loader;
- }
-
- /**
- * Get the current Mustache template Loader instance.
- *
- * If no Loader instance has been explicitly specified, this method will instantiate and return
- * a StringLoader instance.
- *
- * @return Mustache_Loader
- */
- public function getLoader() {
- if (!isset($this->loader)) {
- $this->loader = new Mustache_Loader_StringLoader;
- }
-
- return $this->loader;
- }
-
- /**
- * Set the Mustache partials Loader instance.
- *
- * @param Mustache_Loader $partialsLoader
- */
- public function setPartialsLoader(Mustache_Loader $partialsLoader) {
- $this->partialsLoader = $partialsLoader;
- }
-
- /**
- * Get the current Mustache partials Loader instance.
- *
- * If no Loader instance has been explicitly specified, this method will instantiate and return
- * an ArrayLoader instance.
- *
- * @return Mustache_Loader
- */
- public function getPartialsLoader() {
- if (!isset($this->partialsLoader)) {
- $this->partialsLoader = new Mustache_Loader_ArrayLoader;
- }
-
- return $this->partialsLoader;
- }
-
- /**
- * Set partials for the current partials Loader instance.
- *
- * @throws RuntimeException If the current Loader instance is immutable
- *
- * @param array $partials (default: array())
- */
- public function setPartials(array $partials = array()) {
- $loader = $this->getPartialsLoader();
- if (!$loader instanceof Mustache_Loader_MutableLoader) {
- throw new RuntimeException('Unable to set partials on an immutable Mustache Loader instance');
- }
-
- $loader->setTemplates($partials);
- }
-
- /**
- * Set an array of Mustache helpers.
- *
- * An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order sections), or
- * any other valid Mustache context value. They will be prepended to the context stack, so they will be available in
- * any template loaded by this Mustache instance.
- *
- * @throws InvalidArgumentException if $helpers is not an array or Traversable
- *
- * @param array|Traversable $helpers
- */
- public function setHelpers($helpers) {
- if (!is_array($helpers) && !$helpers instanceof Traversable) {
- throw new InvalidArgumentException('setHelpers expects an array of helpers');
- }
-
- $this->getHelpers()->clear();
-
- foreach ($helpers as $name => $helper) {
- $this->addHelper($name, $helper);
- }
- }
-
- /**
- * Get the current set of Mustache helpers.
- *
- * @see Mustache_Engine::setHelpers
- *
- * @return Mustache_HelperCollection
- */
- public function getHelpers() {
- if (!isset($this->helpers)) {
- $this->helpers = new Mustache_HelperCollection;
- }
-
- return $this->helpers;
- }
-
- /**
- * Add a new Mustache helper.
- *
- * @see Mustache_Engine::setHelpers
- *
- * @param string $name
- * @param mixed $helper
- */
- public function addHelper($name, $helper) {
- $this->getHelpers()->add($name, $helper);
- }
-
- /**
- * Get a Mustache helper by name.
- *
- * @see Mustache_Engine::setHelpers
- *
- * @param string $name
- *
- * @return mixed Helper
- */
- public function getHelper($name) {
- return $this->getHelpers()->get($name);
- }
-
- /**
- * Check whether this Mustache instance has a helper.
- *
- * @see Mustache_Engine::setHelpers
- *
- * @param string $name
- *
- * @return boolean True if the helper is present
- */
- public function hasHelper($name) {
- return $this->getHelpers()->has($name);
- }
-
- /**
- * Remove a helper by name.
- *
- * @see Mustache_Engine::setHelpers
- *
- * @param string $name
- */
- public function removeHelper($name) {
- $this->getHelpers()->remove($name);
- }
-
- /**
- * Set the Mustache Tokenizer instance.
- *
- * @param Mustache_Tokenizer $tokenizer
- */
- public function setTokenizer(Mustache_Tokenizer $tokenizer) {
- $this->tokenizer = $tokenizer;
- }
-
- /**
- * Get the current Mustache Tokenizer instance.
- *
- * If no Tokenizer instance has been explicitly specified, this method will instantiate and return a new one.
- *
- * @return Mustache_Tokenizer
- */
- public function getTokenizer() {
- if (!isset($this->tokenizer)) {
- $this->tokenizer = new Mustache_Tokenizer;
- }
-
- return $this->tokenizer;
- }
-
- /**
- * Set the Mustache Parser instance.
- *
- * @param Mustache_Parser $parser
- */
- public function setParser(Mustache_Parser $parser) {
- $this->parser = $parser;
- }
-
- /**
- * Get the current Mustache Parser instance.
- *
- * If no Parser instance has been explicitly specified, this method will instantiate and return a new one.
- *
- * @return Mustache_Parser
- */
- public function getParser() {
- if (!isset($this->parser)) {
- $this->parser = new Mustache_Parser;
- }
-
- return $this->parser;
- }
-
- /**
- * Set the Mustache Compiler instance.
- *
- * @param Mustache_Compiler $compiler
- */
- public function setCompiler(Mustache_Compiler $compiler) {
- $this->compiler = $compiler;
- }
-
- /**
- * Get the current Mustache Compiler instance.
- *
- * If no Compiler instance has been explicitly specified, this method will instantiate and return a new one.
- *
- * @return Mustache_Compiler
- */
- public function getCompiler() {
- if (!isset($this->compiler)) {
- $this->compiler = new Mustache_Compiler;
- }
-
- return $this->compiler;
- }
-
- /**
- * Helper method to generate a Mustache template class.
- *
- * @param string $source
- *
- * @return string Mustache Template class name
- */
- public function getTemplateClassName($source) {
- return $this->templateClassPrefix . md5(sprintf(
- 'version:%s,escape:%s,charset:%s,source:%s',
- self::VERSION,
- isset($this->escape) ? 'custom' : 'default',
- $this->charset,
- $source
- ));
- }
-
- /**
- * Load a Mustache Template by name.
- *
- * @param string $name
- *
- * @return Mustache_Template
- */
- public function loadTemplate($name) {
- return $this->loadSource($this->getLoader()->load($name));
- }
-
- /**
- * Load a Mustache partial Template by name.
- *
- * This is a helper method used internally by Template instances for loading partial templates. You can most likely
- * ignore it completely.
- *
- * @param string $name
- *
- * @return Mustache_Template
- */
- public function loadPartial($name) {
- try {
- return $this->loadSource($this->getPartialsLoader()->load($name));
- } catch (InvalidArgumentException $e) {
- // If the named partial cannot be found, return null.
- }
- }
-
- /**
- * Load a Mustache lambda Template by source.
- *
- * This is a helper method used by Template instances to generate subtemplates for Lambda sections. You can most
- * likely ignore it completely.
- *
- * @param string $source
- * @param string $delims (default: null)
- *
- * @return Mustache_Template
- */
- public function loadLambda($source, $delims = null) {
- if ($delims !== null) {
- $source = $delims . "\n" . $source;
- }
-
- return $this->loadSource($source);
- }
-
- /**
- * Instantiate and return a Mustache Template instance by source.
- *
- * @see Mustache_Engine::loadTemplate
- * @see Mustache_Engine::loadPartial
- * @see Mustache_Engine::loadLambda
- *
- * @param string $source
- *
- * @return Mustache_Template
- */
- private function loadSource($source) {
- $className = $this->getTemplateClassName($source);
-
- if (!isset($this->templates[$className])) {
- if (!class_exists($className, false)) {
- if ($fileName = $this->getCacheFilename($source)) {
- if (!is_file($fileName)) {
- $this->writeCacheFile($fileName, $this->compile($source));
- }
-
- require_once $fileName;
- } else {
- eval('?>'.$this->compile($source));
- }
- }
-
- $this->templates[$className] = new $className($this);
- }
-
- return $this->templates[$className];
- }
-
- /**
- * Helper method to tokenize a Mustache template.
- *
- * @see Mustache_Tokenizer::scan
- *
- * @param string $source
- *
- * @return array Tokens
- */
- private function tokenize($source) {
- return $this->getTokenizer()->scan($source);
- }
-
- /**
- * Helper method to parse a Mustache template.
- *
- * @see Mustache_Parser::parse
- *
- * @param string $source
- *
- * @return array Token tree
- */
- private function parse($source) {
- return $this->getParser()->parse($this->tokenize($source));
- }
-
- /**
- * Helper method to compile a Mustache template.
- *
- * @see Mustache_Compiler::compile
- *
- * @param string $source
- *
- * @return string generated Mustache template class code
- */
- private function compile($source) {
- $tree = $this->parse($source);
- $name = $this->getTemplateClassName($source);
-
- return $this->getCompiler()->compile($source, $tree, $name, isset($this->escape), $this->charset);
- }
-
- /**
- * Helper method to generate a Mustache Template class cache filename.
- *
- * @param string $source
- *
- * @return string Mustache Template class cache filename
- */
- private function getCacheFilename($source) {
- if ($this->cache) {
- return sprintf('%s/%s.php', $this->cache, $this->getTemplateClassName($source));
- }
- }
-
- /**
- * Helper method to dump a generated Mustache Template subclass to the file cache.
- *
- * @throws RuntimeException if unable to write to $fileName.
- *
- * @param string $fileName
- * @param string $source
- */
- private function writeCacheFile($fileName, $source) {
- if (!is_dir(dirname($fileName))) {
- mkdir(dirname($fileName), 0777, true);
- }
-
- $tempFile = tempnam(dirname($fileName), basename($fileName));
- if (false !== @file_put_contents($tempFile, $source)) {
- if (@rename($tempFile, $fileName)) {
- chmod($fileName, 0644);
-
- return;
- }
- }
-
- throw new RuntimeException(sprintf('Failed to write cache file "%s".', $fileName));
- }
+class Mustache_Engine
+{
+ const VERSION = '2.0.0-dev';
+ const SPEC_VERSION = '1.1.2';
+
+ // Template cache
+ private $templates = array();
+
+ // Environment
+ private $templateClassPrefix = '__Mustache_';
+ private $cache = null;
+ private $loader;
+ private $partialsLoader;
+ private $helpers;
+ private $escape;
+ private $charset = 'UTF-8';
+
+ /**
+ * Mustache class constructor.
+ *
+ * Passing an $options array allows overriding certain Mustache options during instantiation:
+ *
+ * $options = array(
+ * // The class prefix for compiled templates. Defaults to '__Mustache_'
+ * 'template_class_prefix' => '__MyTemplates_',
+ *
+ * // A cache directory for compiled templates. Mustache will not cache templates unless this is set
+ * 'cache' => dirname(__FILE__).'/tmp/cache/mustache',
+ *
+ * // A Mustache template loader instance. Uses a StringLoader if not specified
+ * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'),
+ *
+ * // A Mustache loader instance for partials.
+ * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'),
+ *
+ * // An array of Mustache partials. Useful for quick-and-dirty string template loading, but not as
+ * // efficient or lazy as a Filesystem (or database) loader.
+ * 'partials' => array('foo' => file_get_contents(dirname(__FILE__).'/views/partials/foo.mustache')),
+ *
+ * // An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order
+ * // sections), or any other valid Mustache context value. They will be prepended to the context stack,
+ * // so they will be available in any template loaded by this Mustache instance.
+ * 'helpers' => array('i18n' => function($text) {
+ * // do something translatey here...
+ * }),
+ *
+ * // An 'escape' callback, responsible for escaping double-mustache variables.
+ * 'escape' => function($value) {
+ * return htmlspecialchars($buffer, ENT_COMPAT, 'UTF-8');
+ * },
+ *
+ * // character set for `htmlspecialchars`. Defaults to 'UTF-8'
+ * 'charset' => 'ISO-8859-1',
+ * );
+ *
+ * @param array $options (default: array())
+ */
+ public function __construct(array $options = array())
+ {
+ if (isset($options['template_class_prefix'])) {
+ $this->templateClassPrefix = $options['template_class_prefix'];
+ }
+
+ if (isset($options['cache'])) {
+ $this->cache = $options['cache'];
+ }
+
+ if (isset($options['loader'])) {
+ $this->setLoader($options['loader']);
+ }
+
+ if (isset($options['partials_loader'])) {
+ $this->setPartialsLoader($options['partials_loader']);
+ }
+
+ if (isset($options['partials'])) {
+ $this->setPartials($options['partials']);
+ }
+
+ if (isset($options['helpers'])) {
+ $this->setHelpers($options['helpers']);
+ }
+
+ if (isset($options['escape'])) {
+ if (!is_callable($options['escape'])) {
+ throw new InvalidArgumentException('Mustache Constructor "escape" option must be callable');
+ }
+
+ $this->escape = $options['escape'];
+ }
+
+ if (isset($options['charset'])) {
+ $this->charset = $options['charset'];
+ }
+ }
+
+ /**
+ * Shortcut 'render' invocation.
+ *
+ * Equivalent to calling `$mustache->loadTemplate($template)->render($data);`
+ *
+ * @see Mustache_Engine::loadTemplate
+ * @see Mustache_Template::render
+ *
+ * @param string $template
+ * @param mixed $data
+ *
+ * @return string Rendered template
+ */
+ public function render($template, $data)
+ {
+ return $this->loadTemplate($template)->render($data);
+ }
+
+ /**
+ * Get the current Mustache escape callback.
+ *
+ * @return mixed Callable or null
+ */
+ public function getEscape()
+ {
+ return $this->escape;
+ }
+
+ /**
+ * Get the current Mustache character set.
+ *
+ * @return string
+ */
+ public function getCharset()
+ {
+ return $this->charset;
+ }
+
+ /**
+ * Set the Mustache template Loader instance.
+ *
+ * @param Mustache_Loader $loader
+ */
+ public function setLoader(Mustache_Loader $loader)
+ {
+ $this->loader = $loader;
+ }
+
+ /**
+ * Get the current Mustache template Loader instance.
+ *
+ * If no Loader instance has been explicitly specified, this method will instantiate and return
+ * a StringLoader instance.
+ *
+ * @return Mustache_Loader
+ */
+ public function getLoader()
+ {
+ if (!isset($this->loader)) {
+ $this->loader = new Mustache_Loader_StringLoader;
+ }
+
+ return $this->loader;
+ }
+
+ /**
+ * Set the Mustache partials Loader instance.
+ *
+ * @param Mustache_Loader $partialsLoader
+ */
+ public function setPartialsLoader(Mustache_Loader $partialsLoader)
+ {
+ $this->partialsLoader = $partialsLoader;
+ }
+
+ /**
+ * Get the current Mustache partials Loader instance.
+ *
+ * If no Loader instance has been explicitly specified, this method will instantiate and return
+ * an ArrayLoader instance.
+ *
+ * @return Mustache_Loader
+ */
+ public function getPartialsLoader()
+ {
+ if (!isset($this->partialsLoader)) {
+ $this->partialsLoader = new Mustache_Loader_ArrayLoader;
+ }
+
+ return $this->partialsLoader;
+ }
+
+ /**
+ * Set partials for the current partials Loader instance.
+ *
+ * @throws RuntimeException If the current Loader instance is immutable
+ *
+ * @param array $partials (default: array())
+ */
+ public function setPartials(array $partials = array())
+ {
+ $loader = $this->getPartialsLoader();
+ if (!$loader instanceof Mustache_Loader_MutableLoader) {
+ throw new RuntimeException('Unable to set partials on an immutable Mustache Loader instance');
+ }
+
+ $loader->setTemplates($partials);
+ }
+
+ /**
+ * Set an array of Mustache helpers.
+ *
+ * An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order sections), or
+ * any other valid Mustache context value. They will be prepended to the context stack, so they will be available in
+ * any template loaded by this Mustache instance.
+ *
+ * @throws InvalidArgumentException if $helpers is not an array or Traversable
+ *
+ * @param array|Traversable $helpers
+ */
+ public function setHelpers($helpers)
+ {
+ if (!is_array($helpers) && !$helpers instanceof Traversable) {
+ throw new InvalidArgumentException('setHelpers expects an array of helpers');
+ }
+
+ $this->getHelpers()->clear();
+
+ foreach ($helpers as $name => $helper) {
+ $this->addHelper($name, $helper);
+ }
+ }
+
+ /**
+ * Get the current set of Mustache helpers.
+ *
+ * @see Mustache_Engine::setHelpers
+ *
+ * @return Mustache_HelperCollection
+ */
+ public function getHelpers()
+ {
+ if (!isset($this->helpers)) {
+ $this->helpers = new Mustache_HelperCollection;
+ }
+
+ return $this->helpers;
+ }
+
+ /**
+ * Add a new Mustache helper.
+ *
+ * @see Mustache_Engine::setHelpers
+ *
+ * @param string $name
+ * @param mixed $helper
+ */
+ public function addHelper($name, $helper)
+ {
+ $this->getHelpers()->add($name, $helper);
+ }
+
+ /**
+ * Get a Mustache helper by name.
+ *
+ * @see Mustache_Engine::setHelpers
+ *
+ * @param string $name
+ *
+ * @return mixed Helper
+ */
+ public function getHelper($name)
+ {
+ return $this->getHelpers()->get($name);
+ }
+
+ /**
+ * Check whether this Mustache instance has a helper.
+ *
+ * @see Mustache_Engine::setHelpers
+ *
+ * @param string $name
+ *
+ * @return boolean True if the helper is present
+ */
+ public function hasHelper($name)
+ {
+ return $this->getHelpers()->has($name);
+ }
+
+ /**
+ * Remove a helper by name.
+ *
+ * @see Mustache_Engine::setHelpers
+ *
+ * @param string $name
+ */
+ public function removeHelper($name)
+ {
+ $this->getHelpers()->remove($name);
+ }
+
+ /**
+ * Set the Mustache Tokenizer instance.
+ *
+ * @param Mustache_Tokenizer $tokenizer
+ */
+ public function setTokenizer(Mustache_Tokenizer $tokenizer)
+ {
+ $this->tokenizer = $tokenizer;
+ }
+
+ /**
+ * Get the current Mustache Tokenizer instance.
+ *
+ * If no Tokenizer instance has been explicitly specified, this method will instantiate and return a new one.
+ *
+ * @return Mustache_Tokenizer
+ */
+ public function getTokenizer()
+ {
+ if (!isset($this->tokenizer)) {
+ $this->tokenizer = new Mustache_Tokenizer;
+ }
+
+ return $this->tokenizer;
+ }
+
+ /**
+ * Set the Mustache Parser instance.
+ *
+ * @param Mustache_Parser $parser
+ */
+ public function setParser(Mustache_Parser $parser)
+ {
+ $this->parser = $parser;
+ }
+
+ /**
+ * Get the current Mustache Parser instance.
+ *
+ * If no Parser instance has been explicitly specified, this method will instantiate and return a new one.
+ *
+ * @return Mustache_Parser
+ */
+ public function getParser()
+ {
+ if (!isset($this->parser)) {
+ $this->parser = new Mustache_Parser;
+ }
+
+ return $this->parser;
+ }
+
+ /**
+ * Set the Mustache Compiler instance.
+ *
+ * @param Mustache_Compiler $compiler
+ */
+ public function setCompiler(Mustache_Compiler $compiler)
+ {
+ $this->compiler = $compiler;
+ }
+
+ /**
+ * Get the current Mustache Compiler instance.
+ *
+ * If no Compiler instance has been explicitly specified, this method will instantiate and return a new one.
+ *
+ * @return Mustache_Compiler
+ */
+ public function getCompiler()
+ {
+ if (!isset($this->compiler)) {
+ $this->compiler = new Mustache_Compiler;
+ }
+
+ return $this->compiler;
+ }
+
+ /**
+ * Helper method to generate a Mustache template class.
+ *
+ * @param string $source
+ *
+ * @return string Mustache Template class name
+ */
+ public function getTemplateClassName($source)
+ {
+ return $this->templateClassPrefix . md5(sprintf(
+ 'version:%s,escape:%s,charset:%s,source:%s',
+ self::VERSION,
+ isset($this->escape) ? 'custom' : 'default',
+ $this->charset,
+ $source
+ ));
+ }
+
+ /**
+ * Load a Mustache Template by name.
+ *
+ * @param string $name
+ *