Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php/** This file is part of Mustache.php.** (c) 2010-2017 Justin Hileman** For the full copyright and license information, please view the LICENSE* file that was distributed with this source code.*//*** A Mustache implementation in PHP.** {@link http://defunkt.github.com/mustache}** Mustache is a framework-agnostic logic-less templating language. It enforces separation of view* logic from template files. In fact, it is not even possible to embed logic in the template.** This is very, very rad.** @author Justin Hileman {@link http://justinhileman.com}*/class Mustache_Engine{const VERSION = '2.14.2';const SPEC_VERSION = '1.2.2';const PRAGMA_FILTERS = 'FILTERS';const PRAGMA_BLOCKS = 'BLOCKS';const PRAGMA_ANCHORED_DOT = 'ANCHORED-DOT';// Known pragmasprivate static $knownPragmas = array(self::PRAGMA_FILTERS => true,self::PRAGMA_BLOCKS => true,self::PRAGMA_ANCHORED_DOT => true,);// Template cacheprivate $templates = array();// Environmentprivate $templateClassPrefix = '__Mustache_';private $cache;private $lambdaCache;private $cacheLambdaTemplates = false;private $loader;private $partialsLoader;private $helpers;private $escape;private $entityFlags = ENT_COMPAT;private $charset = 'UTF-8';private $logger;private $strictCallables = false;private $disableLambdaRendering = false;private $pragmas = array();private $delimiters;// Servicesprivate $tokenizer;private $parser;private $compiler;/*** 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 Mustache cache instance or a cache directory string for compiled templates.* // Mustache will not cache templates unless this is set.* 'cache' => dirname(__FILE__).'/tmp/cache/mustache',** // Override default permissions for cache files. Defaults to using the system-defined umask. It is* // *strongly* recommended that you configure your umask properly rather than overriding permissions here.* 'cache_file_mode' => 0666,** // Optionally, enable caching for lambda section templates. This is generally not recommended, as lambda* // sections are often too dynamic to benefit from caching.* 'cache_lambda_templates' => true,** // Customize the tag delimiters used by this engine instance. Note that overriding here changes the* // delimiters used to parse all templates and partials loaded by this instance. To override just for a* // single template, use an inline "change delimiters" tag at the start of the template file:* //* // {{=<% %>=}}* //* 'delimiters' => '<% %>',** // 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');* },** // Type argument for `htmlspecialchars`. Defaults to ENT_COMPAT. You may prefer ENT_QUOTES.* 'entity_flags' => ENT_QUOTES,** // Character set for `htmlspecialchars`. Defaults to 'UTF-8'. Use 'UTF-8'.* 'charset' => 'ISO-8859-1',** // A Mustache Logger instance. No logging will occur unless this is set. Using a PSR-3 compatible* // logging library -- such as Monolog -- is highly recommended. A simple stream logger implementation is* // available as well:* 'logger' => new Mustache_Logger_StreamLogger('php://stderr'),** // Only treat Closure instances and invokable classes as callable. If true, values like* // `array('ClassName', 'methodName')` and `array($classInstance, 'methodName')`, which are traditionally* // "callable" in PHP, are not called to resolve variables for interpolation or section contexts. This* // 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,** // Do not render the output of lambdas. Use this to prevent repeated rendering if the lambda already* // takes care of rendering its content. This helps protect against mustache code injection when user* // input is passed directly into the template. Defaults to false.* 'disable_lambda_rendering' => 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** @param array $options (default: array())*/public function __construct(array $options = array()){if (isset($options['template_class_prefix'])) {if ((string) $options['template_class_prefix'] === '') {throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "template_class_prefix" must not be empty');}$this->templateClassPrefix = $options['template_class_prefix'];}if (isset($options['cache'])) {$cache = $options['cache'];if (is_string($cache)) {$mode = isset($options['cache_file_mode']) ? $options['cache_file_mode'] : null;$cache = new Mustache_Cache_FilesystemCache($cache, $mode);}$this->setCache($cache);}if (isset($options['cache_lambda_templates'])) {$this->cacheLambdaTemplates = (bool) $options['cache_lambda_templates'];}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 Mustache_Exception_InvalidArgumentException('Mustache Constructor "escape" option must be callable');}$this->escape = $options['escape'];}if (isset($options['entity_flags'])) {$this->entityFlags = $options['entity_flags'];}if (isset($options['charset'])) {$this->charset = $options['charset'];}if (isset($options['logger'])) {$this->setLogger($options['logger']);}if (isset($options['strict_callables'])) {$this->strictCallables = $options['strict_callables'];}if (isset($options['disable_lambda_rendering'])) {$this->disableLambdaRendering = $options['disable_lambda_rendering'];}if (isset($options['delimiters'])) {$this->delimiters = $options['delimiters'];}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;}}}/*** Shortcut 'render' invocation.** Equivalent to calling `$mustache->loadTemplate($template)->render($context);`** @see Mustache_Engine::loadTemplate* @see Mustache_Template::render** @param string $template* @param mixed $context (default: array())** @return string Rendered template*/public function render($template, $context = array()){return $this->loadTemplate($template)->render($context);}/*** Get the current Mustache escape callback.** @return callable|null*/public function getEscape(){return $this->escape;}/*** Get the current Mustache entitity type to escape.** @return int*/public function getEntityFlags(){return $this->entityFlags;}/*** Get the current Mustache character set.** @return string*/public function getCharset(){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.** @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 Mustache_Exception_RuntimeException If the current Loader instance is immutable** @param array $partials (default: array())*/public function setPartials(array $partials = array()){if (!isset($this->partialsLoader)) {$this->partialsLoader = new Mustache_Loader_ArrayLoader();}if (!$this->partialsLoader instanceof Mustache_Loader_MutableLoader) {throw new Mustache_Exception_RuntimeException('Unable to set partials on an immutable Mustache Loader instance');}$this->partialsLoader->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 Mustache_Exception_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 Mustache_Exception_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 bool 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 Logger instance.** @throws Mustache_Exception_InvalidArgumentException If logger is not an instance of Mustache_Logger or Psr\Log\LoggerInterface** @param Mustache_Logger|Psr\Log\LoggerInterface $logger*/public function setLogger($logger = null){if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) {throw new Mustache_Exception_InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.');}if ($this->getCache()->getLogger() === null) {$this->getCache()->setLogger($logger);}$this->logger = $logger;}/*** Get the current Mustache Logger instance.** @return Mustache_Logger|Psr\Log\LoggerInterface*/public function getLogger(){return $this->logger;}/*** 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;}/*** Set the Mustache Cache instance.** @param Mustache_Cache $cache*/public function setCache(Mustache_Cache $cache){if (isset($this->logger) && $cache->getLogger() === null) {$cache->setLogger($this->getLogger());}$this->cache = $cache;}/*** Get the current Mustache Cache instance.** If no Cache instance has been explicitly specified, this method will instantiate and return a new one.** @return Mustache_Cache*/public function getCache(){if (!isset($this->cache)) {$this->setCache(new Mustache_Cache_NoopCache());}return $this->cache;}/*** Get the current Lambda Cache instance.** If 'cache_lambda_templates' is enabled, this is the default cache instance. Otherwise, it is a NoopCache.** @see Mustache_Engine::getCache** @return Mustache_Cache*/protected function getLambdaCache(){if ($this->cacheLambdaTemplates) {return $this->getCache();}if (!isset($this->lambdaCache)) {$this->lambdaCache = new Mustache_Cache_NoopCache();}return $this->lambdaCache;}/*** Helper method to generate a Mustache template class.** This method must be updated any time options are added which make it so* the same template could be parsed and compiled multiple different ways.** @param string|Mustache_Source $source** @return string Mustache Template class name*/public function getTemplateClassName($source){// For the most part, adding a new option here should do the trick.//// Pick a value here which is unique for each possible way the template// could be compiled... but not necessarily unique per option value. See// escape below, which only needs to differentiate between 'custom' and// 'default' escapes.//// Keep this list in alphabetical order :)$chunks = array('charset' => $this->charset,'delimiters' => $this->delimiters ? $this->delimiters : '{{ }}','entityFlags' => $this->entityFlags,'escape' => isset($this->escape) ? 'custom' : 'default','key' => ($source instanceof Mustache_Source) ? $source->getKey() : 'source','pragmas' => $this->getPragmas(),'strictCallables' => $this->strictCallables,'disableLambdaRendering' => $this->disableLambdaRendering,'version' => self::VERSION,);$key = json_encode($chunks);// Template Source instances have already provided their own source key. For strings, just include the whole// source string in the md5 hash.if (!$source instanceof Mustache_Source) {$key .= "\n" . $source;}return $this->templateClassPrefix . md5($key);}/*** 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 {if (isset($this->partialsLoader)) {$loader = $this->partialsLoader;} elseif (isset($this->loader) && !$this->loader instanceof Mustache_Loader_StringLoader) {$loader = $this->loader;} else {throw new Mustache_Exception_UnknownTemplateException($name);}return $this->loadSource($loader->load($name));} catch (Mustache_Exception_UnknownTemplateException $e) {// If the named partial cannot be found, log then return null.$this->log(Mustache_Logger::WARNING,'Partial not found: "{name}"',array('name' => $e->getTemplateName()));}}/*** 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, $this->getLambdaCache());}/*** Instantiate and return a Mustache Template instance by source.** Optionally provide a Mustache_Cache instance. This is used internally by Mustache_Engine::loadLambda to respect* the 'cache_lambda_templates' configuration option.** @see Mustache_Engine::loadTemplate* @see Mustache_Engine::loadPartial* @see Mustache_Engine::loadLambda** @param string|Mustache_Source $source* @param Mustache_Cache $cache (default: null)** @return Mustache_Template*/private function loadSource($source, Mustache_Cache $cache = null){$className = $this->getTemplateClassName($source);if (!isset($this->templates[$className])) {if ($cache === null) {$cache = $this->getCache();}if (!class_exists($className, false)) {if (!$cache->load($className)) {$compiled = $this->compile($source);$cache->cache($className, $compiled);}}$this->log(Mustache_Logger::DEBUG,'Instantiating template: "{className}"',array('className' => $className));$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, $this->delimiters);}/*** Helper method to parse a Mustache template.** @see Mustache_Parser::parse** @param string $source** @return array Token tree*/private function parse($source){$parser = $this->getParser();$parser->setPragmas($this->getPragmas());return $parser->parse($this->tokenize($source));}/*** Helper method to compile a Mustache template.** @see Mustache_Compiler::compile** @param string|Mustache_Source $source** @return string generated Mustache template class code*/private function compile($source){$name = $this->getTemplateClassName($source);$this->log(Mustache_Logger::INFO,'Compiling template to "{className}" class',array('className' => $name));if ($source instanceof Mustache_Source) {$source = $source->getSource();}$tree = $this->parse($source);$compiler = $this->getCompiler();$compiler->setPragmas($this->getPragmas());return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags, $this->disableLambdaRendering);}/*** Add a log record if logging is enabled.** @param int $level The logging level* @param string $message The log message* @param array $context The log context*/private function log($level, $message, array $context = array()){if (isset($this->logger)) {$this->logger->log($level, $message, $context);}}}