Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   1  <?php
   2  
   3  /*
   4   * This file is part of Mustache.php.
   5   *
   6   * (c) 2010-2017 Justin Hileman
   7   *
   8   * For the full copyright and license information, please view the LICENSE
   9   * file that was distributed with this source code.
  10   */
  11  
  12  /**
  13   * A Mustache implementation in PHP.
  14   *
  15   * {@link http://defunkt.github.com/mustache}
  16   *
  17   * Mustache is a framework-agnostic logic-less templating language. It enforces separation of view
  18   * logic from template files. In fact, it is not even possible to embed logic in the template.
  19   *
  20   * This is very, very rad.
  21   *
  22   * @author Justin Hileman {@link http://justinhileman.com}
  23   */
  24  class Mustache_Engine
  25  {
  26      const VERSION        = '2.13.0';
  27      const SPEC_VERSION   = '1.1.2';
  28  
  29      const PRAGMA_FILTERS      = 'FILTERS';
  30      const PRAGMA_BLOCKS       = 'BLOCKS';
  31      const PRAGMA_ANCHORED_DOT = 'ANCHORED-DOT';
  32  
  33      // Known pragmas
  34      private static $knownPragmas = array(
  35          self::PRAGMA_FILTERS      => true,
  36          self::PRAGMA_BLOCKS       => true,
  37          self::PRAGMA_ANCHORED_DOT => true,
  38      );
  39  
  40      // Template cache
  41      private $templates = array();
  42  
  43      // Environment
  44      private $templateClassPrefix = '__Mustache_';
  45      private $cache;
  46      private $lambdaCache;
  47      private $cacheLambdaTemplates = false;
  48      private $loader;
  49      private $partialsLoader;
  50      private $helpers;
  51      private $escape;
  52      private $entityFlags = ENT_COMPAT;
  53      private $charset = 'UTF-8';
  54      private $logger;
  55      private $strictCallables = false;
  56      private $pragmas = array();
  57      private $delimiters;
  58  
  59      // Services
  60      private $tokenizer;
  61      private $parser;
  62      private $compiler;
  63  
  64      /**
  65       * Mustache class constructor.
  66       *
  67       * Passing an $options array allows overriding certain Mustache options during instantiation:
  68       *
  69       *     $options = array(
  70       *         // The class prefix for compiled templates. Defaults to '__Mustache_'.
  71       *         'template_class_prefix' => '__MyTemplates_',
  72       *
  73       *         // A Mustache cache instance or a cache directory string for compiled templates.
  74       *         // Mustache will not cache templates unless this is set.
  75       *         'cache' => dirname(__FILE__).'/tmp/cache/mustache',
  76       *
  77       *         // Override default permissions for cache files. Defaults to using the system-defined umask. It is
  78       *         // *strongly* recommended that you configure your umask properly rather than overriding permissions here.
  79       *         'cache_file_mode' => 0666,
  80       *
  81       *         // Optionally, enable caching for lambda section templates. This is generally not recommended, as lambda
  82       *         // sections are often too dynamic to benefit from caching.
  83       *         'cache_lambda_templates' => true,
  84       *
  85       *         // Customize the tag delimiters used by this engine instance. Note that overriding here changes the
  86       *         // delimiters used to parse all templates and partials loaded by this instance. To override just for a
  87       *         // single template, use an inline "change delimiters" tag at the start of the template file:
  88       *         //
  89       *         //     {{=<% %>=}}
  90       *         //
  91       *         'delimiters' => '<% %>',
  92       *
  93       *         // A Mustache template loader instance. Uses a StringLoader if not specified.
  94       *         'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'),
  95       *
  96       *         // A Mustache loader instance for partials.
  97       *         'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'),
  98       *
  99       *         // An array of Mustache partials. Useful for quick-and-dirty string template loading, but not as
 100       *         // efficient or lazy as a Filesystem (or database) loader.
 101       *         'partials' => array('foo' => file_get_contents(dirname(__FILE__).'/views/partials/foo.mustache')),
 102       *
 103       *         // An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order
 104       *         // sections), or any other valid Mustache context value. They will be prepended to the context stack,
 105       *         // so they will be available in any template loaded by this Mustache instance.
 106       *         'helpers' => array('i18n' => function ($text) {
 107       *             // do something translatey here...
 108       *         }),
 109       *
 110       *         // An 'escape' callback, responsible for escaping double-mustache variables.
 111       *         'escape' => function ($value) {
 112       *             return htmlspecialchars($buffer, ENT_COMPAT, 'UTF-8');
 113       *         },
 114       *
 115       *         // Type argument for `htmlspecialchars`.  Defaults to ENT_COMPAT.  You may prefer ENT_QUOTES.
 116       *         'entity_flags' => ENT_QUOTES,
 117       *
 118       *         // Character set for `htmlspecialchars`. Defaults to 'UTF-8'. Use 'UTF-8'.
 119       *         'charset' => 'ISO-8859-1',
 120       *
 121       *         // A Mustache Logger instance. No logging will occur unless this is set. Using a PSR-3 compatible
 122       *         // logging library -- such as Monolog -- is highly recommended. A simple stream logger implementation is
 123       *         // available as well:
 124       *         'logger' => new Mustache_Logger_StreamLogger('php://stderr'),
 125       *
 126       *         // Only treat Closure instances and invokable classes as callable. If true, values like
 127       *         // `array('ClassName', 'methodName')` and `array($classInstance, 'methodName')`, which are traditionally
 128       *         // "callable" in PHP, are not called to resolve variables for interpolation or section contexts. This
 129       *         // helps protect against arbitrary code execution when user input is passed directly into the template.
 130       *         // This currently defaults to false, but will default to true in v3.0.
 131       *         'strict_callables' => true,
 132       *
 133       *         // Enable pragmas across all templates, regardless of the presence of pragma tags in the individual
 134       *         // templates.
 135       *         'pragmas' => [Mustache_Engine::PRAGMA_FILTERS],
 136       *     );
 137       *
 138       * @throws Mustache_Exception_InvalidArgumentException If `escape` option is not callable
 139       *
 140       * @param array $options (default: array())
 141       */
 142      public function __construct(array $options = array())
 143      {
 144          if (isset($options['template_class_prefix'])) {
 145              if ((string) $options['template_class_prefix'] === '') {
 146                  throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "template_class_prefix" must not be empty');
 147              }
 148  
 149              $this->templateClassPrefix = $options['template_class_prefix'];
 150          }
 151  
 152          if (isset($options['cache'])) {
 153              $cache = $options['cache'];
 154  
 155              if (is_string($cache)) {
 156                  $mode  = isset($options['cache_file_mode']) ? $options['cache_file_mode'] : null;
 157                  $cache = new Mustache_Cache_FilesystemCache($cache, $mode);
 158              }
 159  
 160              $this->setCache($cache);
 161          }
 162  
 163          if (isset($options['cache_lambda_templates'])) {
 164              $this->cacheLambdaTemplates = (bool) $options['cache_lambda_templates'];
 165          }
 166  
 167          if (isset($options['loader'])) {
 168              $this->setLoader($options['loader']);
 169          }
 170  
 171          if (isset($options['partials_loader'])) {
 172              $this->setPartialsLoader($options['partials_loader']);
 173          }
 174  
 175          if (isset($options['partials'])) {
 176              $this->setPartials($options['partials']);
 177          }
 178  
 179          if (isset($options['helpers'])) {
 180              $this->setHelpers($options['helpers']);
 181          }
 182  
 183          if (isset($options['escape'])) {
 184              if (!is_callable($options['escape'])) {
 185                  throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "escape" option must be callable');
 186              }
 187  
 188              $this->escape = $options['escape'];
 189          }
 190  
 191          if (isset($options['entity_flags'])) {
 192              $this->entityFlags = $options['entity_flags'];
 193          }
 194  
 195          if (isset($options['charset'])) {
 196              $this->charset = $options['charset'];
 197          }
 198  
 199          if (isset($options['logger'])) {
 200              $this->setLogger($options['logger']);
 201          }
 202  
 203          if (isset($options['strict_callables'])) {
 204              $this->strictCallables = $options['strict_callables'];
 205          }
 206  
 207          if (isset($options['delimiters'])) {
 208              $this->delimiters = $options['delimiters'];
 209          }
 210  
 211          if (isset($options['pragmas'])) {
 212              foreach ($options['pragmas'] as $pragma) {
 213                  if (!isset(self::$knownPragmas[$pragma])) {
 214                      throw new Mustache_Exception_InvalidArgumentException(sprintf('Unknown pragma: "%s".', $pragma));
 215                  }
 216                  $this->pragmas[$pragma] = true;
 217              }
 218          }
 219      }
 220  
 221      /**
 222       * Shortcut 'render' invocation.
 223       *
 224       * Equivalent to calling `$mustache->loadTemplate($template)->render($context);`
 225       *
 226       * @see Mustache_Engine::loadTemplate
 227       * @see Mustache_Template::render
 228       *
 229       * @param string $template
 230       * @param mixed  $context  (default: array())
 231       *
 232       * @return string Rendered template
 233       */
 234      public function render($template, $context = array())
 235      {
 236          return $this->loadTemplate($template)->render($context);
 237      }
 238  
 239      /**
 240       * Get the current Mustache escape callback.
 241       *
 242       * @return callable|null
 243       */
 244      public function getEscape()
 245      {
 246          return $this->escape;
 247      }
 248  
 249      /**
 250       * Get the current Mustache entitity type to escape.
 251       *
 252       * @return int
 253       */
 254      public function getEntityFlags()
 255      {
 256          return $this->entityFlags;
 257      }
 258  
 259      /**
 260       * Get the current Mustache character set.
 261       *
 262       * @return string
 263       */
 264      public function getCharset()
 265      {
 266          return $this->charset;
 267      }
 268  
 269      /**
 270       * Get the current globally enabled pragmas.
 271       *
 272       * @return array
 273       */
 274      public function getPragmas()
 275      {
 276          return array_keys($this->pragmas);
 277      }
 278  
 279      /**
 280       * Set the Mustache template Loader instance.
 281       *
 282       * @param Mustache_Loader $loader
 283       */
 284      public function setLoader(Mustache_Loader $loader)
 285      {
 286          $this->loader = $loader;
 287      }
 288  
 289      /**
 290       * Get the current Mustache template Loader instance.
 291       *
 292       * If no Loader instance has been explicitly specified, this method will instantiate and return
 293       * a StringLoader instance.
 294       *
 295       * @return Mustache_Loader
 296       */
 297      public function getLoader()
 298      {
 299          if (!isset($this->loader)) {
 300              $this->loader = new Mustache_Loader_StringLoader();
 301          }
 302  
 303          return $this->loader;
 304      }
 305  
 306      /**
 307       * Set the Mustache partials Loader instance.
 308       *
 309       * @param Mustache_Loader $partialsLoader
 310       */
 311      public function setPartialsLoader(Mustache_Loader $partialsLoader)
 312      {
 313          $this->partialsLoader = $partialsLoader;
 314      }
 315  
 316      /**
 317       * Get the current Mustache partials Loader instance.
 318       *
 319       * If no Loader instance has been explicitly specified, this method will instantiate and return
 320       * an ArrayLoader instance.
 321       *
 322       * @return Mustache_Loader
 323       */
 324      public function getPartialsLoader()
 325      {
 326          if (!isset($this->partialsLoader)) {
 327              $this->partialsLoader = new Mustache_Loader_ArrayLoader();
 328          }
 329  
 330          return $this->partialsLoader;
 331      }
 332  
 333      /**
 334       * Set partials for the current partials Loader instance.
 335       *
 336       * @throws Mustache_Exception_RuntimeException If the current Loader instance is immutable
 337       *
 338       * @param array $partials (default: array())
 339       */
 340      public function setPartials(array $partials = array())
 341      {
 342          if (!isset($this->partialsLoader)) {
 343              $this->partialsLoader = new Mustache_Loader_ArrayLoader();
 344          }
 345  
 346          if (!$this->partialsLoader instanceof Mustache_Loader_MutableLoader) {
 347              throw new Mustache_Exception_RuntimeException('Unable to set partials on an immutable Mustache Loader instance');
 348          }
 349  
 350          $this->partialsLoader->setTemplates($partials);
 351      }
 352  
 353      /**
 354       * Set an array of Mustache helpers.
 355       *
 356       * An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order sections), or
 357       * any other valid Mustache context value. They will be prepended to the context stack, so they will be available in
 358       * any template loaded by this Mustache instance.
 359       *
 360       * @throws Mustache_Exception_InvalidArgumentException if $helpers is not an array or Traversable
 361       *
 362       * @param array|Traversable $helpers
 363       */
 364      public function setHelpers($helpers)
 365      {
 366          if (!is_array($helpers) && !$helpers instanceof Traversable) {
 367              throw new Mustache_Exception_InvalidArgumentException('setHelpers expects an array of helpers');
 368          }
 369  
 370          $this->getHelpers()->clear();
 371  
 372          foreach ($helpers as $name => $helper) {
 373              $this->addHelper($name, $helper);
 374          }
 375      }
 376  
 377      /**
 378       * Get the current set of Mustache helpers.
 379       *
 380       * @see Mustache_Engine::setHelpers
 381       *
 382       * @return Mustache_HelperCollection
 383       */
 384      public function getHelpers()
 385      {
 386          if (!isset($this->helpers)) {
 387              $this->helpers = new Mustache_HelperCollection();
 388          }
 389  
 390          return $this->helpers;
 391      }
 392  
 393      /**
 394       * Add a new Mustache helper.
 395       *
 396       * @see Mustache_Engine::setHelpers
 397       *
 398       * @param string $name
 399       * @param mixed  $helper
 400       */
 401      public function addHelper($name, $helper)
 402      {
 403          $this->getHelpers()->add($name, $helper);
 404      }
 405  
 406      /**
 407       * Get a Mustache helper by name.
 408       *
 409       * @see Mustache_Engine::setHelpers
 410       *
 411       * @param string $name
 412       *
 413       * @return mixed Helper
 414       */
 415      public function getHelper($name)
 416      {
 417          return $this->getHelpers()->get($name);
 418      }
 419  
 420      /**
 421       * Check whether this Mustache instance has a helper.
 422       *
 423       * @see Mustache_Engine::setHelpers
 424       *
 425       * @param string $name
 426       *
 427       * @return bool True if the helper is present
 428       */
 429      public function hasHelper($name)
 430      {
 431          return $this->getHelpers()->has($name);
 432      }
 433  
 434      /**
 435       * Remove a helper by name.
 436       *
 437       * @see Mustache_Engine::setHelpers
 438       *
 439       * @param string $name
 440       */
 441      public function removeHelper($name)
 442      {
 443          $this->getHelpers()->remove($name);
 444      }
 445  
 446      /**
 447       * Set the Mustache Logger instance.
 448       *
 449       * @throws Mustache_Exception_InvalidArgumentException If logger is not an instance of Mustache_Logger or Psr\Log\LoggerInterface
 450       *
 451       * @param Mustache_Logger|Psr\Log\LoggerInterface $logger
 452       */
 453      public function setLogger($logger = null)
 454      {
 455          if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) {
 456              throw new Mustache_Exception_InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.');
 457          }
 458  
 459          if ($this->getCache()->getLogger() === null) {
 460              $this->getCache()->setLogger($logger);
 461          }
 462  
 463          $this->logger = $logger;
 464      }
 465  
 466      /**
 467       * Get the current Mustache Logger instance.
 468       *
 469       * @return Mustache_Logger|Psr\Log\LoggerInterface
 470       */
 471      public function getLogger()
 472      {
 473          return $this->logger;
 474      }
 475  
 476      /**
 477       * Set the Mustache Tokenizer instance.
 478       *
 479       * @param Mustache_Tokenizer $tokenizer
 480       */
 481      public function setTokenizer(Mustache_Tokenizer $tokenizer)
 482      {
 483          $this->tokenizer = $tokenizer;
 484      }
 485  
 486      /**
 487       * Get the current Mustache Tokenizer instance.
 488       *
 489       * If no Tokenizer instance has been explicitly specified, this method will instantiate and return a new one.
 490       *
 491       * @return Mustache_Tokenizer
 492       */
 493      public function getTokenizer()
 494      {
 495          if (!isset($this->tokenizer)) {
 496              $this->tokenizer = new Mustache_Tokenizer();
 497          }
 498  
 499          return $this->tokenizer;
 500      }
 501  
 502      /**
 503       * Set the Mustache Parser instance.
 504       *
 505       * @param Mustache_Parser $parser
 506       */
 507      public function setParser(Mustache_Parser $parser)
 508      {
 509          $this->parser = $parser;
 510      }
 511  
 512      /**
 513       * Get the current Mustache Parser instance.
 514       *
 515       * If no Parser instance has been explicitly specified, this method will instantiate and return a new one.
 516       *
 517       * @return Mustache_Parser
 518       */
 519      public function getParser()
 520      {
 521          if (!isset($this->parser)) {
 522              $this->parser = new Mustache_Parser();
 523          }
 524  
 525          return $this->parser;
 526      }
 527  
 528      /**
 529       * Set the Mustache Compiler instance.
 530       *
 531       * @param Mustache_Compiler $compiler
 532       */
 533      public function setCompiler(Mustache_Compiler $compiler)
 534      {
 535          $this->compiler = $compiler;
 536      }
 537  
 538      /**
 539       * Get the current Mustache Compiler instance.
 540       *
 541       * If no Compiler instance has been explicitly specified, this method will instantiate and return a new one.
 542       *
 543       * @return Mustache_Compiler
 544       */
 545      public function getCompiler()
 546      {
 547          if (!isset($this->compiler)) {
 548              $this->compiler = new Mustache_Compiler();
 549          }
 550  
 551          return $this->compiler;
 552      }
 553  
 554      /**
 555       * Set the Mustache Cache instance.
 556       *
 557       * @param Mustache_Cache $cache
 558       */
 559      public function setCache(Mustache_Cache $cache)
 560      {
 561          if (isset($this->logger) && $cache->getLogger() === null) {
 562              $cache->setLogger($this->getLogger());
 563          }
 564  
 565          $this->cache = $cache;
 566      }
 567  
 568      /**
 569       * Get the current Mustache Cache instance.
 570       *
 571       * If no Cache instance has been explicitly specified, this method will instantiate and return a new one.
 572       *
 573       * @return Mustache_Cache
 574       */
 575      public function getCache()
 576      {
 577          if (!isset($this->cache)) {
 578              $this->setCache(new Mustache_Cache_NoopCache());
 579          }
 580  
 581          return $this->cache;
 582      }
 583  
 584      /**
 585       * Get the current Lambda Cache instance.
 586       *
 587       * If 'cache_lambda_templates' is enabled, this is the default cache instance. Otherwise, it is a NoopCache.
 588       *
 589       * @see Mustache_Engine::getCache
 590       *
 591       * @return Mustache_Cache
 592       */
 593      protected function getLambdaCache()
 594      {
 595          if ($this->cacheLambdaTemplates) {
 596              return $this->getCache();
 597          }
 598  
 599          if (!isset($this->lambdaCache)) {
 600              $this->lambdaCache = new Mustache_Cache_NoopCache();
 601          }
 602  
 603          return $this->lambdaCache;
 604      }
 605  
 606      /**
 607       * Helper method to generate a Mustache template class.
 608       *
 609       * This method must be updated any time options are added which make it so
 610       * the same template could be parsed and compiled multiple different ways.
 611       *
 612       * @param string|Mustache_Source $source
 613       *
 614       * @return string Mustache Template class name
 615       */
 616      public function getTemplateClassName($source)
 617      {
 618          // For the most part, adding a new option here should do the trick.
 619          //
 620          // Pick a value here which is unique for each possible way the template
 621          // could be compiled... but not necessarily unique per option value. See
 622          // escape below, which only needs to differentiate between 'custom' and
 623          // 'default' escapes.
 624          //
 625          // Keep this list in alphabetical order :)
 626          $chunks = array(
 627              'charset'         => $this->charset,
 628              'delimiters'      => $this->delimiters ? $this->delimiters : '{{ }}',
 629              'entityFlags'     => $this->entityFlags,
 630              'escape'          => isset($this->escape) ? 'custom' : 'default',
 631              'key'             => ($source instanceof Mustache_Source) ? $source->getKey() : 'source',
 632              'pragmas'         => $this->getPragmas(),
 633              'strictCallables' => $this->strictCallables,
 634              'version'         => self::VERSION,
 635          );
 636  
 637          $key = json_encode($chunks);
 638  
 639          // Template Source instances have already provided their own source key. For strings, just include the whole
 640          // source string in the md5 hash.
 641          if (!$source instanceof Mustache_Source) {
 642              $key .= "\n" . $source;
 643          }
 644  
 645          return $this->templateClassPrefix . md5($key);
 646      }
 647  
 648      /**
 649       * Load a Mustache Template by name.
 650       *
 651       * @param string $name
 652       *
 653       * @return Mustache_Template
 654       */
 655      public function loadTemplate($name)
 656      {
 657          return $this->loadSource($this->getLoader()->load($name));
 658      }
 659  
 660      /**
 661       * Load a Mustache partial Template by name.
 662       *
 663       * This is a helper method used internally by Template instances for loading partial templates. You can most likely
 664       * ignore it completely.
 665       *
 666       * @param string $name
 667       *
 668       * @return Mustache_Template
 669       */
 670      public function loadPartial($name)
 671      {
 672          try {
 673              if (isset($this->partialsLoader)) {
 674                  $loader = $this->partialsLoader;
 675              } elseif (isset($this->loader) && !$this->loader instanceof Mustache_Loader_StringLoader) {
 676                  $loader = $this->loader;
 677              } else {
 678                  throw new Mustache_Exception_UnknownTemplateException($name);
 679              }
 680  
 681              return $this->loadSource($loader->load($name));
 682          } catch (Mustache_Exception_UnknownTemplateException $e) {
 683              // If the named partial cannot be found, log then return null.
 684              $this->log(
 685                  Mustache_Logger::WARNING,
 686                  'Partial not found: "{name}"',
 687                  array('name' => $e->getTemplateName())
 688              );
 689          }
 690      }
 691  
 692      /**
 693       * Load a Mustache lambda Template by source.
 694       *
 695       * This is a helper method used by Template instances to generate subtemplates for Lambda sections. You can most
 696       * likely ignore it completely.
 697       *
 698       * @param string $source
 699       * @param string $delims (default: null)
 700       *
 701       * @return Mustache_Template
 702       */
 703      public function loadLambda($source, $delims = null)
 704      {
 705          if ($delims !== null) {
 706              $source = $delims . "\n" . $source;
 707          }
 708  
 709          return $this->loadSource($source, $this->getLambdaCache());
 710      }
 711  
 712      /**
 713       * Instantiate and return a Mustache Template instance by source.
 714       *
 715       * Optionally provide a Mustache_Cache instance. This is used internally by Mustache_Engine::loadLambda to respect
 716       * the 'cache_lambda_templates' configuration option.
 717       *
 718       * @see Mustache_Engine::loadTemplate
 719       * @see Mustache_Engine::loadPartial
 720       * @see Mustache_Engine::loadLambda
 721       *
 722       * @param string|Mustache_Source $source
 723       * @param Mustache_Cache         $cache  (default: null)
 724       *
 725       * @return Mustache_Template
 726       */
 727      private function loadSource($source, Mustache_Cache $cache = null)
 728      {
 729          $className = $this->getTemplateClassName($source);
 730  
 731          if (!isset($this->templates[$className])) {
 732              if ($cache === null) {
 733                  $cache = $this->getCache();
 734              }
 735  
 736              if (!class_exists($className, false)) {
 737                  if (!$cache->load($className)) {
 738                      $compiled = $this->compile($source);
 739                      $cache->cache($className, $compiled);
 740                  }
 741              }
 742  
 743              $this->log(
 744                  Mustache_Logger::DEBUG,
 745                  'Instantiating template: "{className}"',
 746                  array('className' => $className)
 747              );
 748  
 749              $this->templates[$className] = new $className($this);
 750          }
 751  
 752          return $this->templates[$className];
 753      }
 754  
 755      /**
 756       * Helper method to tokenize a Mustache template.
 757       *
 758       * @see Mustache_Tokenizer::scan
 759       *
 760       * @param string $source
 761       *
 762       * @return array Tokens
 763       */
 764      private function tokenize($source)
 765      {
 766          return $this->getTokenizer()->scan($source, $this->delimiters);
 767      }
 768  
 769      /**
 770       * Helper method to parse a Mustache template.
 771       *
 772       * @see Mustache_Parser::parse
 773       *
 774       * @param string $source
 775       *
 776       * @return array Token tree
 777       */
 778      private function parse($source)
 779      {
 780          $parser = $this->getParser();
 781          $parser->setPragmas($this->getPragmas());
 782  
 783          return $parser->parse($this->tokenize($source));
 784      }
 785  
 786      /**
 787       * Helper method to compile a Mustache template.
 788       *
 789       * @see Mustache_Compiler::compile
 790       *
 791       * @param string|Mustache_Source $source
 792       *
 793       * @return string generated Mustache template class code
 794       */
 795      private function compile($source)
 796      {
 797          $name = $this->getTemplateClassName($source);
 798  
 799          $this->log(
 800              Mustache_Logger::INFO,
 801              'Compiling template to "{className}" class',
 802              array('className' => $name)
 803          );
 804  
 805          if ($source instanceof Mustache_Source) {
 806              $source = $source->getSource();
 807          }
 808          $tree = $this->parse($source);
 809  
 810          $compiler = $this->getCompiler();
 811          $compiler->setPragmas($this->getPragmas());
 812  
 813          return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags);
 814      }
 815  
 816      /**
 817       * Add a log record if logging is enabled.
 818       *
 819       * @param int    $level   The logging level
 820       * @param string $message The log message
 821       * @param array  $context The log context
 822       */
 823      private function log($level, $message, array $context = array())
 824      {
 825          if (isset($this->logger)) {
 826              $this->logger->log($level, $message, $context);
 827          }
 828      }
 829  }