| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- <?php
- /*
- * This file is part of Mustache.php.
- *
- * (c) 2010-2015 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.9.0';
- const SPEC_VERSION = '1.1.2';
- const PRAGMA_FILTERS = 'FILTERS';
- const PRAGMA_BLOCKS = 'BLOCKS';
- const PRAGMA_ANCHORED_DOT = 'ANCHORED-DOT';
- // Known pragmas
- private static $knownPragmas = array(
- self::PRAGMA_FILTERS => true,
- self::PRAGMA_BLOCKS => true,
- self::PRAGMA_ANCHORED_DOT => true,
- );
- // Template cache
- private $templates = array();
- // Environment
- private $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 $pragmas = array();
- // Services
- private $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,
- *
- * // 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,
- *
- * // 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'])) {
- $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['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.
- *
- * @param string $source
- *
- * @return string Mustache Template class name
- */
- public function getTemplateClassName($source)
- {
- return $this->templateClassPrefix . md5(sprintf(
- 'version:%s,escape:%s,entity_flags:%i,charset:%s,strict_callables:%s,pragmas:%s,source:%s',
- self::VERSION,
- isset($this->escape) ? 'custom' : 'default',
- $this->entityFlags,
- $this->charset,
- $this->strictCallables ? 'true' : 'false',
- implode(' ', $this->getPragmas()),
- $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 {
- 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 $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);
- }
- /**
- * 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 $source
- *
- * @return string generated Mustache template class code
- */
- private function compile($source)
- {
- $tree = $this->parse($source);
- $name = $this->getTemplateClassName($source);
- $this->log(
- Mustache_Logger::INFO,
- 'Compiling template to "{className}" class',
- array('className' => $name)
- );
- $compiler = $this->getCompiler();
- $compiler->setPragmas($this->getPragmas());
- return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags);
- }
- /**
- * 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);
- }
- }
- }
|