Merge branch 'release/2.3.0'

This commit is contained in:
Justin Hileman
2013-04-01 20:56:30 -07:00
37 changed files with 994 additions and 101 deletions
+1
View File
@@ -1,2 +1,3 @@
composer.lock composer.lock
vendor vendor
mustache.php
+7 -1
View File
@@ -5,6 +5,7 @@ A [Mustache](http://mustache.github.com/) implementation in PHP.
[![Build Status](https://secure.travis-ci.org/bobthecow/mustache.php.png?branch=dev)](http://travis-ci.org/bobthecow/mustache.php) [![Build Status](https://secure.travis-ci.org/bobthecow/mustache.php.png?branch=dev)](http://travis-ci.org/bobthecow/mustache.php)
Usage Usage
----- -----
@@ -55,9 +56,14 @@ echo $m->render($template, $chris);
``` ```
And That's Not All!
-------------------
Read [the Mustache.php documentation](https://github.com/bobthecow/mustache.php/wiki/Home) for more information.
See Also See Also
-------- --------
* [Mustache.php wiki](https://github.com/bobthecow/mustache.php/wiki/Home).
* [Readme for the Ruby Mustache implementation](http://github.com/defunkt/mustache/blob/master/README.md). * [Readme for the Ruby Mustache implementation](http://github.com/defunkt/mustache/blob/master/README.md).
* [mustache(5)](http://mustache.github.com/mustache.5.html) man page. * [mustache(5)](http://mustache.github.com/mustache.5.html) man page.
+175
View File
@@ -0,0 +1,175 @@
#!/usr/bin/env php
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2012 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A shell script to create a single-file class cache of the entire Mustache
* library:
*
* $ bin/build_bootstrap.php
*
* ... will create a `mustache.php` bootstrap file in the project directory,
* containing all Mustache library classes. This file can then be included in
* your project, rather than requiring the Mustache Autoloader.
*/
$baseDir = realpath(dirname(__FILE__).'/..');
require $baseDir.'/src/Mustache/Autoloader.php';
Mustache_Autoloader::register();
// delete the old file
$file = $baseDir.'/mustache.php';
if (file_exists($file)) {
unlink($file);
}
// and load the new one
SymfonyClassCollectionLoader::load(array(
'\Mustache_Engine',
'\Mustache_Compiler',
'\Mustache_Context',
'\Mustache_Exception',
'\Mustache_Exception_InvalidArgumentException',
'\Mustache_Exception_LogicException',
'\Mustache_Exception_RuntimeException',
'\Mustache_Exception_SyntaxException',
'\Mustache_Exception_UnknownFilterException',
'\Mustache_Exception_UnknownHelperException',
'\Mustache_Exception_UnknownTemplateException',
'\Mustache_HelperCollection',
'\Mustache_LambdaHelper',
'\Mustache_Loader',
'\Mustache_Loader_ArrayLoader',
'\Mustache_Loader_CascadingLoader',
'\Mustache_Loader_FilesystemLoader',
'\Mustache_Loader_InlineLoader',
'\Mustache_Loader_MutableLoader',
'\Mustache_Loader_StringLoader',
'\Mustache_Logger',
'\Mustache_Logger_AbstractLogger',
'\Mustache_Logger_StreamLogger',
'\Mustache_Parser',
'\Mustache_Template',
'\Mustache_Tokenizer',
), dirname($file), basename($file, '.php'));
/**
* SymfonyClassCollectionLoader.
*
* Based heavily on the Symfony ClassCollectionLoader component, with all
* the unnecessary bits removed.
*
* @license http://www.opensource.org/licenses/MIT
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SymfonyClassCollectionLoader
{
static private $loaded;
const HEADER = <<<EOS
<?php
/*
* This file is part of Mustache.php.
*
* (c) %d Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
EOS;
/**
* Loads a list of classes and caches them in one big file.
*
* @param array $classes An array of classes to load
* @param string $cacheDir A cache directory
* @param string $name The cache name prefix
* @param string $extension File extension of the resulting file
*
* @throws InvalidArgumentException When class can't be loaded
*/
static public function load(array $classes, $cacheDir, $name, $extension = '.php')
{
// each $name can only be loaded once per PHP process
if (isset(self::$loaded[$name])) {
return;
}
self::$loaded[$name] = true;
$content = '';
foreach ($classes as $class) {
if (!class_exists($class) && !interface_exists($class) && (!function_exists('trait_exists') || !trait_exists($class))) {
throw new InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
}
$r = new ReflectionClass($class);
$content .= preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($r->getFileName()));
}
$cache = $cacheDir.'/'.$name.$extension;
$header = sprintf(self::HEADER, strftime('%Y'));
self::writeCacheFile($cache, $header . substr(self::stripComments('<?php '.$content), 5));
}
/**
* Writes a cache file.
*
* @param string $file Filename
* @param string $content Temporary file content
*
* @throws RuntimeException when a cache file cannot be written
*/
static private function writeCacheFile($file, $content)
{
$tmpFile = tempnam(dirname($file), basename($file));
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
chmod($file, 0666 & ~umask());
return;
}
throw new RuntimeException(sprintf('Failed to write cache file "%s".', $file));
}
/**
* Removes comments from a PHP source string.
*
* We don't use the PHP php_strip_whitespace() function
* as we want the content to be readable and well-formatted.
*
* @param string $source A PHP string
*
* @return string The PHP string with the comments removed
*/
static private function stripComments($source)
{
if (!function_exists('token_get_all')) {
return $source;
}
$output = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
$output .= $token[1];
}
}
// replace multiple new lines with a single newline
$output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output);
return $output;
}
}
+37 -25
View File
@@ -53,7 +53,7 @@ class Mustache_Compiler
/** /**
* Helper function for walking the Mustache token parse tree. * Helper function for walking the Mustache token parse tree.
* *
* @throws InvalidArgumentException upon encountering unknown token types. * @throws Mustache_Exception_SyntaxException upon encountering unknown token types.
* *
* @param array $tree Parse tree of Mustache tokens * @param array $tree Parse tree of Mustache tokens
* @param int $level (default: 0) * @param int $level (default: 0)
@@ -116,7 +116,7 @@ class Mustache_Compiler
break; break;
default: default:
throw new InvalidArgumentException('Unknown node type: '.json_encode($node)); throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node);
} }
} }
@@ -127,23 +127,34 @@ class Mustache_Compiler
class %s extends Mustache_Template class %s extends Mustache_Template
{ {
private $lambdaHelper; private $lambdaHelper;%s
public function renderInternal(Mustache_Context $context, $indent = \'\', $escape = false) public function renderInternal(Mustache_Context $context, $indent = \'\')
{ {
$this->lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context); $this->lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context);
$buffer = \'\'; $buffer = \'\';
%s %s
if ($escape) { return $buffer;
return %s;
} else {
return $buffer;
}
} }
%s %s
}'; }';
const KLASS_NO_LAMBDAS = '<?php
class %s extends Mustache_Template
{%s
public function renderInternal(Mustache_Context $context, $indent = \'\')
{
$buffer = \'\';
%s
return $buffer;
}
}';
const STRICT_CALLABLE = 'protected $strictCallables = true;';
/** /**
* Generate Mustache Template class PHP source. * Generate Mustache Template class PHP source.
* *
@@ -156,8 +167,10 @@ class Mustache_Compiler
{ {
$code = $this->walk($tree); $code = $this->walk($tree);
$sections = implode("\n", $this->sections); $sections = implode("\n", $this->sections);
$klass = empty($this->sections) ? self::KLASS_NO_LAMBDAS : self::KLASS;
$callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : '';
return sprintf($this->prepare(self::KLASS, 0, false), $name, $code, $this->getEscape('$buffer'), $sections); return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections);
} }
const SECTION_CALL = ' const SECTION_CALL = '
@@ -166,7 +179,8 @@ class Mustache_Compiler
'; ';
const SECTION = ' const SECTION = '
private function section%s(Mustache_Context $context, $indent, $value) { private function section%s(Mustache_Context $context, $indent, $value)
{
$buffer = \'\'; $buffer = \'\';
if (%s) { if (%s) {
$source = %s; $source = %s;
@@ -268,12 +282,7 @@ class Mustache_Compiler
} }
const VARIABLE = ' const VARIABLE = '
$value = $context->%s(%s); $value = $this->resolveValue($context->%s(%s), $context, $indent);%s
if (%s) {
$value = $this->mustache
->loadLambda((string) call_user_func($value))
->renderInternal($context, $indent);
}%s
$buffer .= %s%s; $buffer .= %s%s;
'; ';
@@ -294,12 +303,11 @@ class Mustache_Compiler
list($id, $filters) = $this->getFilters($id, $level); list($id, $filters) = $this->getFilters($id, $level);
} }
$method = $this->getFindMethod($id); $method = $this->getFindMethod($id);
$id = ($method !== 'last') ? var_export($id, true) : ''; $id = ($method !== 'last') ? var_export($id, true) : '';
$callable = $this->getCallable(); $value = $escape ? $this->getEscape() : '$value';
$value = $escape ? $this->getEscape() : '$value';
return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $callable, $filters, $this->flushIndent(), $value); return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value);
} }
/** /**
@@ -321,7 +329,7 @@ class Mustache_Compiler
const FILTER = ' const FILTER = '
$filter = $context->%s(%s); $filter = $context->%s(%s);
if (!(%s)) { if (!(%s)) {
throw new UnexpectedValueException(%s); throw new Mustache_Exception_UnknownFilterException(%s);
} }
$value = call_user_func($filter, $value);%s $value = call_user_func($filter, $value);%s
'; ';
@@ -344,7 +352,7 @@ class Mustache_Compiler
$method = $this->getFindMethod($name); $method = $this->getFindMethod($name);
$filter = ($method !== 'last') ? var_export($name, true) : ''; $filter = ($method !== 'last') ? var_export($name, true) : '';
$callable = $this->getCallable('$filter'); $callable = $this->getCallable('$filter');
$msg = var_export(sprintf('Filter not found: %s', $name), true); $msg = var_export($name, true);
return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilter($filters, $level)); return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilter($filters, $level));
} }
@@ -377,15 +385,19 @@ class Mustache_Compiler
* @param string $text * @param string $text
* @param int $bonus Additional indent level (default: 0) * @param int $bonus Additional indent level (default: 0)
* @param boolean $prependNewline Prepend a newline to the snippet? (default: true) * @param boolean $prependNewline Prepend a newline to the snippet? (default: true)
* @param boolean $appendNewline Append a newline to the snippet? (default: false)
* *
* @return string PHP source code snippet * @return string PHP source code snippet
*/ */
private function prepare($text, $bonus = 0, $prependNewline = true) private function prepare($text, $bonus = 0, $prependNewline = true, $appendNewline = false)
{ {
$text = ($prependNewline ? "\n" : '').trim($text); $text = ($prependNewline ? "\n" : '').trim($text);
if ($prependNewline) { if ($prependNewline) {
$bonus++; $bonus++;
} }
if ($appendNewline) {
$text .= "\n";
}
return preg_replace("/\n( {8})?/", "\n".str_repeat(" ", $bonus * 4), $text); return preg_replace("/\n( {8})?/", "\n".str_repeat(" ", $bonus * 4), $text);
} }
+32 -17
View File
@@ -23,7 +23,7 @@
*/ */
class Mustache_Engine class Mustache_Engine
{ {
const VERSION = '2.2.0'; const VERSION = '2.3.0';
const SPEC_VERSION = '1.1.2'; const SPEC_VERSION = '1.1.2';
const PRAGMA_FILTERS = 'FILTERS'; const PRAGMA_FILTERS = 'FILTERS';
@@ -87,7 +87,7 @@ class Mustache_Engine
* // A Mustache Logger instance. No logging will occur unless this is set. Using a PSR-3 compatible * // 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 * // logging library -- such as Monolog -- is highly recommended. A simple stream logger implementation is
* // available as well: * // available as well:
* 'logger' => new Mustache_StreamLogger('php://stderr'), * 'logger' => new Mustache_Logger_StreamLogger('php://stderr'),
* *
* // Only treat Closure instances and invokable classes as callable. If true, values like * // Only treat Closure instances and invokable classes as callable. If true, values like
* // `array('ClassName', 'methodName')` and `array($classInstance, 'methodName')`, which are traditionally * // `array('ClassName', 'methodName')` and `array($classInstance, 'methodName')`, which are traditionally
@@ -97,6 +97,8 @@ class Mustache_Engine
* 'strict_callables' => true, * 'strict_callables' => true,
* ); * );
* *
* @throws Mustache_Exception_InvalidArgumentException If `escape` option is not callable.
*
* @param array $options (default: array()) * @param array $options (default: array())
*/ */
public function __construct(array $options = array()) public function __construct(array $options = array())
@@ -131,7 +133,7 @@ class Mustache_Engine
if (isset($options['escape'])) { if (isset($options['escape'])) {
if (!is_callable($options['escape'])) { if (!is_callable($options['escape'])) {
throw new InvalidArgumentException('Mustache Constructor "escape" option must be callable'); throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "escape" option must be callable');
} }
$this->escape = $options['escape']; $this->escape = $options['escape'];
@@ -245,18 +247,21 @@ class Mustache_Engine
/** /**
* Set partials for the current partials Loader instance. * Set partials for the current partials Loader instance.
* *
* @throws RuntimeException If the current Loader instance is immutable * @throws Mustache_Exception_RuntimeException If the current Loader instance is immutable
* *
* @param array $partials (default: array()) * @param array $partials (default: array())
*/ */
public function setPartials(array $partials = array()) public function setPartials(array $partials = array())
{ {
$loader = $this->getPartialsLoader(); if (!isset($this->partialsLoader)) {
if (!$loader instanceof Mustache_Loader_MutableLoader) { $this->partialsLoader = new Mustache_Loader_ArrayLoader;
throw new RuntimeException('Unable to set partials on an immutable Mustache Loader instance');
} }
$loader->setTemplates($partials); 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);
} }
/** /**
@@ -266,14 +271,14 @@ class Mustache_Engine
* any other valid Mustache context value. They will be prepended to the context stack, so they will be available in * 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. * any template loaded by this Mustache instance.
* *
* @throws InvalidArgumentException if $helpers is not an array or Traversable * @throws Mustache_Exception_InvalidArgumentException if $helpers is not an array or Traversable
* *
* @param array|Traversable $helpers * @param array|Traversable $helpers
*/ */
public function setHelpers($helpers) public function setHelpers($helpers)
{ {
if (!is_array($helpers) && !$helpers instanceof Traversable) { if (!is_array($helpers) && !$helpers instanceof Traversable) {
throw new InvalidArgumentException('setHelpers expects an array of helpers'); throw new Mustache_Exception_InvalidArgumentException('setHelpers expects an array of helpers');
} }
$this->getHelpers()->clear(); $this->getHelpers()->clear();
@@ -355,12 +360,14 @@ class Mustache_Engine
/** /**
* Set the Mustache Logger instance. * 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 * @param Mustache_Logger|Psr\Log\LoggerInterface $logger
*/ */
public function setLogger($logger = null) public function setLogger($logger = null)
{ {
if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) { if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) {
throw new InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.'); throw new Mustache_Exception_InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.');
} }
$this->logger = $logger; $this->logger = $logger;
@@ -498,13 +505,21 @@ class Mustache_Engine
public function loadPartial($name) public function loadPartial($name)
{ {
try { try {
return $this->loadSource($this->getPartialsLoader()->load($name)); if (isset($this->partialsLoader)) {
} catch (InvalidArgumentException $e) { $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. // If the named partial cannot be found, log then return null.
$this->log( $this->log(
Mustache_Logger::WARNING, Mustache_Logger::WARNING,
'Partial not found: "{name}"', 'Partial not found: "{name}"',
array('name' => $name) array('name' => $e->getTemplateName())
); );
} }
} }
@@ -649,7 +664,7 @@ class Mustache_Engine
/** /**
* Helper method to dump a generated Mustache Template subclass to the file cache. * Helper method to dump a generated Mustache Template subclass to the file cache.
* *
* @throws RuntimeException if unable to create the cache directory or write to $fileName. * @throws Mustache_Exception_RuntimeException if unable to create the cache directory or write to $fileName.
* *
* @param string $fileName * @param string $fileName
* @param string $source * @param string $source
@@ -668,7 +683,7 @@ class Mustache_Engine
@mkdir($dirName, 0777, true); @mkdir($dirName, 0777, true);
if (!is_dir($dirName)) { if (!is_dir($dirName)) {
throw new RuntimeException(sprintf('Failed to create cache directory "%s".', $dirName)); throw new Mustache_Exception_RuntimeException(sprintf('Failed to create cache directory "%s".', $dirName));
} }
} }
@@ -695,7 +710,7 @@ class Mustache_Engine
); );
} }
throw new RuntimeException(sprintf('Failed to write cache file "%s".', $fileName)); throw new Mustache_Exception_RuntimeException(sprintf('Failed to write cache file "%s".', $fileName));
} }
/** /**
+18
View File
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A Mustache Exception interface.
*/
interface Mustache_Exception
{
// This space intentionally left blank.
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Invalid argument exception.
*/
class Mustache_Exception_InvalidArgumentException extends InvalidArgumentException implements Mustache_Exception
{
// This space intentionally left blank.
}
+18
View File
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Logic exception.
*/
class Mustache_Exception_LogicException extends LogicException implements Mustache_Exception
{
// This space intentionally left blank.
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Runtime exception.
*/
class Mustache_Exception_RuntimeException extends RuntimeException implements Mustache_Exception
{
// This space intentionally left blank.
}
@@ -0,0 +1,29 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Mustache syntax exception.
*/
class Mustache_Exception_SyntaxException extends LogicException implements Mustache_Exception
{
protected $token;
public function __construct($msg, array $token)
{
$this->token = $token;
parent::__construct($msg);
}
public function getToken()
{
return $this->token;
}
}
@@ -0,0 +1,29 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Unknown filter exception.
*/
class Mustache_Exception_UnknownFilterException extends UnexpectedValueException implements Mustache_Exception
{
protected $filterName;
public function __construct($filterName)
{
$this->filterName = $filterName;
parent::__construct(sprintf('Unknown filter: %s', $filterName));
}
public function getFilterName()
{
return $this->filterName;
}
}
@@ -0,0 +1,29 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Unknown helper exception.
*/
class Mustache_Exception_UnknownHelperException extends InvalidArgumentException implements Mustache_Exception
{
protected $helperName;
public function __construct($helperName)
{
$this->helperName = $helperName;
parent::__construct(sprintf('Unknown helper: %s', $helperName));
}
public function getHelperName()
{
return $this->helperName;
}
}
@@ -0,0 +1,29 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Unknown template exception.
*/
class Mustache_Exception_UnknownTemplateException extends InvalidArgumentException implements Mustache_Exception
{
protected $templateName;
public function __construct($templateName)
{
$this->templateName = $templateName;
parent::__construct(sprintf('Unknown template: %s', $templateName));
}
public function getTemplateName()
{
return $this->templateName;
}
}
+7 -5
View File
@@ -21,7 +21,7 @@ class Mustache_HelperCollection
* *
* Optionally accepts an array (or Traversable) of `$name => $helper` pairs. * Optionally accepts an array (or Traversable) of `$name => $helper` pairs.
* *
* @throws InvalidArgumentException if the $helpers argument isn't an array or Traversable * @throws Mustache_Exception_InvalidArgumentException if the $helpers argument isn't an array or Traversable
* *
* @param array|Traversable $helpers (default: null) * @param array|Traversable $helpers (default: null)
*/ */
@@ -29,7 +29,7 @@ class Mustache_HelperCollection
{ {
if ($helpers !== null) { if ($helpers !== null) {
if (!is_array($helpers) && !$helpers instanceof Traversable) { if (!is_array($helpers) && !$helpers instanceof Traversable) {
throw new InvalidArgumentException('HelperCollection constructor expects an array of helpers'); throw new Mustache_Exception_InvalidArgumentException('HelperCollection constructor expects an array of helpers');
} }
foreach ($helpers as $name => $helper) { foreach ($helpers as $name => $helper) {
@@ -79,6 +79,8 @@ class Mustache_HelperCollection
/** /**
* Get a helper by name. * Get a helper by name.
* *
* @throws Mustache_Exception_UnknownHelperException If helper does not exist.
*
* @param string $name * @param string $name
* *
* @return mixed Helper * @return mixed Helper
@@ -86,7 +88,7 @@ class Mustache_HelperCollection
public function get($name) public function get($name)
{ {
if (!$this->has($name)) { if (!$this->has($name)) {
throw new InvalidArgumentException('Unknown helper: '.$name); throw new Mustache_Exception_UnknownHelperException($name);
} }
return $this->helpers[$name]; return $this->helpers[$name];
@@ -133,14 +135,14 @@ class Mustache_HelperCollection
/** /**
* Check whether a given helper is present in the collection. * Check whether a given helper is present in the collection.
* *
* @throws InvalidArgumentException if the requested helper is not present. * @throws Mustache_Exception_UnknownHelperException if the requested helper is not present.
* *
* @param string $name * @param string $name
*/ */
public function remove($name) public function remove($name)
{ {
if (!$this->has($name)) { if (!$this->has($name)) {
throw new InvalidArgumentException('Unknown helper: '.$name); throw new Mustache_Exception_UnknownHelperException($name);
} }
unset($this->helpers[$name]); unset($this->helpers[$name]);
+3 -2
View File
@@ -12,8 +12,9 @@
/** /**
* Mustache Lambda Helper. * Mustache Lambda Helper.
* *
* Passed to section and interpolation lambdas, giving them access to a `render` * Passed as the second argument to section lambdas (higher order sections),
* method for rendering a string with the current context. * giving them access to a `render` method for rendering a string with the
* current context.
*/ */
class Mustache_LambdaHelper class Mustache_LambdaHelper
{ {
+2
View File
@@ -18,6 +18,8 @@ interface Mustache_Loader
/** /**
* Load a Template by name. * Load a Template by name.
* *
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
*
* @param string $name * @param string $name
* *
* @return string Mustache Template source * @return string Mustache Template source
+3 -4
View File
@@ -23,9 +23,6 @@
* *
* The ArrayLoader is used internally as a partials loader by Mustache_Engine instance when an array of partials * The ArrayLoader is used internally as a partials loader by Mustache_Engine instance when an array of partials
* is set. It can also be used as a quick-and-dirty Template loader. * is set. It can also be used as a quick-and-dirty Template loader.
*
* @implements Loader
* @implements MutableLoader
*/ */
class Mustache_Loader_ArrayLoader implements Mustache_Loader, Mustache_Loader_MutableLoader class Mustache_Loader_ArrayLoader implements Mustache_Loader, Mustache_Loader_MutableLoader
{ {
@@ -43,6 +40,8 @@ class Mustache_Loader_ArrayLoader implements Mustache_Loader, Mustache_Loader_Mu
/** /**
* Load a Template. * Load a Template.
* *
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
*
* @param string $name * @param string $name
* *
* @return string Mustache Template source * @return string Mustache Template source
@@ -50,7 +49,7 @@ class Mustache_Loader_ArrayLoader implements Mustache_Loader, Mustache_Loader_Mu
public function load($name) public function load($name)
{ {
if (!isset($this->templates[$name])) { if (!isset($this->templates[$name])) {
throw new InvalidArgumentException('Template '.$name.' not found.'); throw new Mustache_Exception_UnknownTemplateException($name);
} }
return $this->templates[$name]; return $this->templates[$name];
+69
View File
@@ -0,0 +1,69 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A Mustache Template cascading loader implementation, which delegates to other
* Loader instances.
*/
class Mustache_Loader_CascadingLoader implements Mustache_Loader
{
private $loaders;
/**
* Construct a CascadingLoader with an array of loaders:
*
* $loader = new Mustache_Loader_CascadingLoader(array(
* new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__),
* new Mustache_Loader_FilesystemLoader(__DIR__.'/templates')
* ));
*
* @param array $loaders An array of Mustache Loader instances
*/
public function __construct(array $loaders = array())
{
$this->loaders = array();
foreach ($loaders as $loader) {
$this->addLoader($loader);
}
}
/**
* Add a Loader instance.
*
* @param Mustache_Loader $loader A Mustache Loader instance
*/
public function addLoader(Mustache_Loader $loader)
{
$this->loaders[] = $loader;
}
/**
* Load a Template by name.
*
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
*
* @param string $name
*
* @return string Mustache Template source
*/
public function load($name)
{
foreach ($this->loaders as $loader) {
try {
return $loader->load($name);
} catch (Mustache_Exception_UnknownTemplateException $e) {
// do nothing, check the next loader.
}
}
throw new Mustache_Exception_UnknownTemplateException($name);
}
}
+4 -6
View File
@@ -23,8 +23,6 @@
* 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'), * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'),
* 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'), * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'),
* )); * ));
*
* @implements Mustache_Loader
*/ */
class Mustache_Loader_FilesystemLoader implements Mustache_Loader class Mustache_Loader_FilesystemLoader implements Mustache_Loader
{ {
@@ -42,7 +40,7 @@ class Mustache_Loader_FilesystemLoader implements Mustache_Loader
* 'extension' => '.ms', * 'extension' => '.ms',
* ); * );
* *
* @throws RuntimeException if $baseDir does not exist. * @throws Mustache_Exception_RuntimeException if $baseDir does not exist.
* *
* @param string $baseDir Base directory containing Mustache template files. * @param string $baseDir Base directory containing Mustache template files.
* @param array $options Array of Loader options (default: array()) * @param array $options Array of Loader options (default: array())
@@ -52,7 +50,7 @@ class Mustache_Loader_FilesystemLoader implements Mustache_Loader
$this->baseDir = rtrim(realpath($baseDir), '/'); $this->baseDir = rtrim(realpath($baseDir), '/');
if (!is_dir($this->baseDir)) { if (!is_dir($this->baseDir)) {
throw new RuntimeException('FilesystemLoader baseDir must be a directory: '.$baseDir); throw new Mustache_Exception_RuntimeException(sprintf('FilesystemLoader baseDir must be a directory: %s', $baseDir));
} }
if (array_key_exists('extension', $options)) { if (array_key_exists('extension', $options)) {
@@ -86,7 +84,7 @@ class Mustache_Loader_FilesystemLoader implements Mustache_Loader
/** /**
* Helper function for loading a Mustache file by name. * Helper function for loading a Mustache file by name.
* *
* @throws InvalidArgumentException if a template file is not found. * @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
* *
* @param string $name * @param string $name
* *
@@ -97,7 +95,7 @@ class Mustache_Loader_FilesystemLoader implements Mustache_Loader
$fileName = $this->getFileName($name); $fileName = $this->getFileName($name);
if (!file_exists($fileName)) { if (!file_exists($fileName)) {
throw new InvalidArgumentException('Template '.$name.' not found.'); throw new Mustache_Exception_UnknownTemplateException($name);
} }
return file_get_contents($fileName); return file_get_contents($fileName);
+121
View File
@@ -0,0 +1,121 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A Mustache Template loader for inline templates.
*
* With the InlineLoader, templates can be defined at the end of any PHP source
* file:
*
* $loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__);
* $hello = $loader->load('hello');
* $goodbye = $loader->load('goodbye');
*
* __halt_compiler();
*
* @@ hello
* Hello, {{ planet }}!
*
* @@ goodbye
* Goodbye, cruel {{ planet }}
*
* Templates are deliniated by lines containing only `@@ name`.
*
* The InlineLoader is well-suited to micro-frameworks such as Silex:
*
* $app->register(new MustacheServiceProvider, array(
* 'mustache.loader' => new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__)
* ));
*
* $app->get('/{name}', function() use ($app) {
* return $app['mustache']->render('hello', compact('name'));
* })
* ->value('name', 'world');
*
* __halt_compiler();
*
* @@ hello
* Hello, {{ name }}!
*
*/
class Mustache_Loader_InlineLoader implements Mustache_Loader
{
protected $fileName;
protected $offset;
protected $templates;
/**
* The InlineLoader requires a filename and offset to process templates.
* The magic constants `__FILE__` and `__COMPILER_HALT_OFFSET__` are usually
* perfectly suited to the job:
*
* $loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__);
*
* Note that this only works if the loader is instantiated inside the same
* file as the inline templates. If the templates are located in another
* file, it would be necessary to manually specify the filename and offset.
*
* @param string $fileName The file to parse for inline templates
* @param int $offset A string offset for the start of the templates.
* This usually coincides with the `__halt_compiler`
* call, and the `__COMPILER_HALT_OFFSET__`.
*/
public function __construct($fileName, $offset)
{
if (!is_file($fileName)) {
throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid filename.');
}
if (!is_int($offset) || $offset < 0) {
throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid file offset.');
}
$this->fileName = $fileName;
$this->offset = $offset;
}
/**
* Load a Template by name.
*
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
*
* @param string $name
*
* @return string Mustache Template source
*/
public function load($name)
{
$this->loadTemplates();
if (!array_key_exists($name, $this->templates)) {
throw new Mustache_Exception_UnknownTemplateException($name);
}
return $this->templates[$name];
}
/**
* Parse and load templates from the end of a source file.
*/
protected function loadTemplates()
{
if ($this->templates === null) {
$this->templates = array();
$data = file_get_contents($this->fileName, false, null, $this->offset);
foreach (preg_split("/^@@(?= [\w\d\.]+$)/m", $data, -1) as $chunk) {
if (trim($chunk)) {
list($name, $content) = explode("\n", $chunk, 2);
$this->templates[trim($name)] = trim($content);
}
}
}
}
}
-2
View File
@@ -22,8 +22,6 @@
* $m = new Mustache; * $m = new Mustache;
* $tpl = $m->loadTemplate('{{ foo }}'); * $tpl = $m->loadTemplate('{{ foo }}');
* echo $tpl->render(array('foo' => 'bar')); // "bar" * echo $tpl->render(array('foo' => 'bar')); // "bar"
*
* @implements Loader
*/ */
class Mustache_Loader_StringLoader implements Mustache_Loader class Mustache_Loader_StringLoader implements Mustache_Loader
{ {
+12 -7
View File
@@ -64,14 +64,14 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
/** /**
* Set the minimum logging level. * Set the minimum logging level.
* *
* @throws InvalidArgumentException if the logging level is unknown. * @throws Mustache_Exception_InvalidArgumentException if the logging level is unknown.
* *
* @param integer $level The minimum logging level which will be written * @param integer $level The minimum logging level which will be written
*/ */
public function setLevel($level) public function setLevel($level)
{ {
if (!array_key_exists($level, self::$levels)) { if (!array_key_exists($level, self::$levels)) {
throw new InvalidArgumentException('Unexpected logging level: ' . $level); throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected logging level: %s', $level));
} }
$this->level = $level; $this->level = $level;
@@ -90,7 +90,7 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
/** /**
* Logs with an arbitrary level. * Logs with an arbitrary level.
* *
* @throws InvalidArgumentException if the logging level is unknown. * @throws Mustache_Exception_InvalidArgumentException if the logging level is unknown.
* *
* @param mixed $level * @param mixed $level
* @param string $message * @param string $message
@@ -99,7 +99,7 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
public function log($level, $message, array $context = array()) public function log($level, $message, array $context = array())
{ {
if (!array_key_exists($level, self::$levels)) { if (!array_key_exists($level, self::$levels)) {
throw new InvalidArgumentException('Unexpected logging level: ' . $level); throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected logging level: %s', $level));
} }
if (self::$levels[$level] >= self::$levels[$this->level]) { if (self::$levels[$level] >= self::$levels[$this->level]) {
@@ -110,6 +110,9 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
/** /**
* Write a record to the log. * Write a record to the log.
* *
* @throws Mustache_Exception_LogicException If neither a stream resource nor url is present.
* @throws Mustache_Exception_RuntimeException If the stream url cannot be opened.
*
* @param integer $level The logging level * @param integer $level The logging level
* @param string $message The log message * @param string $message The log message
* @param array $context The log context * @param array $context The log context
@@ -118,13 +121,13 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
{ {
if (!is_resource($this->stream)) { if (!is_resource($this->stream)) {
if (!isset($this->url)) { if (!isset($this->url)) {
throw new LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); throw new Mustache_Exception_LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
} }
$this->stream = fopen($this->url, 'a'); $this->stream = fopen($this->url, 'a');
if (!is_resource($this->stream)) { if (!is_resource($this->stream)) {
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
throw new UnexpectedValueException(sprintf('The stream or file "%s" could not be opened.', $this->url)); throw new Mustache_Exception_RuntimeException(sprintf('The stream or file "%s" could not be opened.', $this->url));
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
} }
@@ -174,7 +177,9 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
*/ */
protected static function interpolateMessage($message, array $context = array()) protected static function interpolateMessage($message, array $context = array())
{ {
$message = (string) $message; if (strpos($message, '{') === false) {
return $message;
}
// build a replacement array with braces around the context keys // build a replacement array with braces around the context keys
$replace = array(); $replace = array();
+8 -5
View File
@@ -32,12 +32,12 @@ class Mustache_Parser
/** /**
* Helper method for recursively building a parse tree. * Helper method for recursively building a parse tree.
* *
* @throws Mustache_Exception_SyntaxException when nesting errors or mismatched section tags are encountered.
*
* @param ArrayIterator $tokens Stream of Mustache tokens * @param ArrayIterator $tokens Stream of Mustache tokens
* @param array $parent Parent token (default: null) * @param array $parent Parent token (default: null)
* *
* @return array Mustache Token parse tree * @return array Mustache Token parse tree
*
* @throws LogicException when nesting errors or mismatched section tags are encountered.
*/ */
private function buildTree(ArrayIterator $tokens, array $parent = null) private function buildTree(ArrayIterator $tokens, array $parent = null)
{ {
@@ -58,11 +58,13 @@ class Mustache_Parser
case Mustache_Tokenizer::T_END_SECTION: case Mustache_Tokenizer::T_END_SECTION:
if (!isset($parent)) { if (!isset($parent)) {
throw new LogicException('Unexpected closing tag: /'. $token[Mustache_Tokenizer::NAME]); $msg = sprintf('Unexpected closing tag: /%s', $token[Mustache_Tokenizer::NAME]);
throw new Mustache_Exception_SyntaxException($msg, $token);
} }
if ($token[Mustache_Tokenizer::NAME] !== $parent[Mustache_Tokenizer::NAME]) { if ($token[Mustache_Tokenizer::NAME] !== $parent[Mustache_Tokenizer::NAME]) {
throw new LogicException('Nesting error: ' . $parent[Mustache_Tokenizer::NAME] . ' vs. ' . $token[Mustache_Tokenizer::NAME]); $msg = sprintf('Nesting error: %s vs. %s', $parent[Mustache_Tokenizer::NAME], $token[Mustache_Tokenizer::NAME]);
throw new Mustache_Exception_SyntaxException($msg, $token);
} }
$parent[Mustache_Tokenizer::END] = $token[Mustache_Tokenizer::INDEX]; $parent[Mustache_Tokenizer::END] = $token[Mustache_Tokenizer::INDEX];
@@ -80,7 +82,8 @@ class Mustache_Parser
} while ($tokens->valid()); } while ($tokens->valid());
if (isset($parent)) { if (isset($parent)) {
throw new LogicException('Missing closing tag: ' . $parent[Mustache_Tokenizer::NAME]); $msg = sprintf('Missing closing tag: %s', $parent[Mustache_Tokenizer::NAME]);
throw new Mustache_Exception_SyntaxException($msg, $parent);
} }
return $nodes; return $nodes;
+30 -2
View File
@@ -22,6 +22,11 @@ abstract class Mustache_Template
*/ */
protected $mustache; protected $mustache;
/**
* @var boolean
*/
protected $strictCallables = false;
/** /**
* Mustache Template constructor. * Mustache Template constructor.
* *
@@ -67,13 +72,14 @@ abstract class Mustache_Template
* *
* This is where the magic happens :) * This is where the magic happens :)
* *
* NOTE: This method is not part of the Mustache.php public API.
*
* @param Mustache_Context $context * @param Mustache_Context $context
* @param string $indent (default: '') * @param string $indent (default: '')
* @param bool $escape (default: false)
* *
* @return string Rendered template * @return string Rendered template
*/ */
abstract public function renderInternal(Mustache_Context $context, $indent = '', $escape = false); abstract public function renderInternal(Mustache_Context $context, $indent = '');
/** /**
* Tests whether a value should be iterated over (e.g. in a section context). * Tests whether a value should be iterated over (e.g. in a section context).
@@ -146,4 +152,26 @@ abstract class Mustache_Template
return $stack; return $stack;
} }
/**
* Resolve a context value.
*
* Invoke the value if it is callable, otherwise return the value.
*
* @param mixed $value
* @param Mustache_Context $context
* @param string $indent
*
* @return string
*/
protected function resolveValue($value, Mustache_Context $context, $indent = '')
{
if (($this->strictCallables ? is_object($value) : !is_string($value)) && is_callable($value)) {
return $this->mustache
->loadLambda((string) call_user_func($value))
->renderInternal($context, $indent);
}
return $value;
}
} }
+41 -13
View File
@@ -33,23 +33,52 @@ class Mustache_Test_CompilerTest extends PHPUnit_Framework_TestCase
return array( return array(
array('', array(), 'Banana', false, 'ISO-8859-1', array( array('', array(), 'Banana', false, 'ISO-8859-1', array(
"\nclass Banana extends Mustache_Template", "\nclass Banana extends Mustache_Template",
'return htmlspecialchars($buffer, ENT_COMPAT, \'ISO-8859-1\');',
'return $buffer;', 'return $buffer;',
)), )),
array('', array($this->createTextToken('TEXT')), 'Monkey', false, 'UTF-8', array( array('', array($this->createTextToken('TEXT')), 'Monkey', false, 'UTF-8', array(
"\nclass Monkey extends Mustache_Template", "\nclass Monkey extends Mustache_Template",
'return htmlspecialchars($buffer, ENT_COMPAT, \'UTF-8\');',
'$buffer .= $indent . \'TEXT\';', '$buffer .= $indent . \'TEXT\';',
'return $buffer;', 'return $buffer;',
)), )),
array('', array($this->createTextToken('TEXT')), 'Monkey', true, 'ISO-8859-1', array( array(
"\nclass Monkey extends Mustache_Template", '',
'$buffer .= $indent . \'TEXT\';', array(
'return call_user_func($this->mustache->getEscape(), $buffer);', array(
'return $buffer;', Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_ESCAPED,
)), Mustache_Tokenizer::NAME => 'name',
)
),
'Monkey',
true,
'ISO-8859-1',
array(
"\nclass Monkey extends Mustache_Template",
'$value = $this->resolveValue($context->find(\'name\'), $context, $indent);',
'$buffer .= $indent . call_user_func($this->mustache->getEscape(), $value);',
'return $buffer;',
)
),
array(
'',
array(
array(
Mustache_Tokenizer::TYPE => Mustache_Tokenizer::T_ESCAPED,
Mustache_Tokenizer::NAME => 'name',
)
),
'Monkey',
false,
'ISO-8859-1',
array(
"\nclass Monkey extends Mustache_Template",
'$value = $this->resolveValue($context->find(\'name\'), $context, $indent);',
'$buffer .= $indent . htmlspecialchars($value, ENT_COMPAT, \'ISO-8859-1\');',
'return $buffer;',
)
),
array( array(
'', '',
@@ -73,11 +102,10 @@ class Mustache_Test_CompilerTest extends PHPUnit_Framework_TestCase
"\nclass Monkey extends Mustache_Template", "\nclass Monkey extends Mustache_Template",
'$buffer .= $indent . \'foo\'', '$buffer .= $indent . \'foo\'',
'$buffer .= "\n"', '$buffer .= "\n"',
'$value = $context->find(\'name\');', '$value = $this->resolveValue($context->find(\'name\'), $context, $indent);',
'$buffer .= htmlspecialchars($value, ENT_COMPAT, \'UTF-8\');', '$buffer .= htmlspecialchars($value, ENT_COMPAT, \'UTF-8\');',
'$value = $context->last();', '$value = $this->resolveValue($context->last(), $context, $indent);',
'$buffer .= \'\\\'bar\\\'\';', '$buffer .= \'\\\'bar\\\'\';',
'return htmlspecialchars($buffer, ENT_COMPAT, \'UTF-8\');',
'return $buffer;', 'return $buffer;',
) )
), ),
@@ -85,9 +113,9 @@ class Mustache_Test_CompilerTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException InvalidArgumentException * @expectedException Mustache_Exception_SyntaxException
*/ */
public function testCompilerThrowsUnknownNodeTypeException() public function testCompilerThrowsSyntaxException()
{ {
$compiler = new Mustache_Compiler; $compiler = new Mustache_Compiler;
$compiler->compile('', array(array(Mustache_Tokenizer::TYPE => 'invalid')), 'SomeClass'); $compiler->compile('', array(array(Mustache_Tokenizer::TYPE => 'invalid')), 'SomeClass');
+27 -4
View File
@@ -141,7 +141,7 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException InvalidArgumentException * @expectedException Mustache_Exception_InvalidArgumentException
* @dataProvider getBadEscapers * @dataProvider getBadEscapers
*/ */
public function testNonCallableEscapeThrowsException($escape) public function testNonCallableEscapeThrowsException($escape)
@@ -158,7 +158,7 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException RuntimeException * @expectedException Mustache_Exception_RuntimeException
*/ */
public function testImmutablePartialsLoadersThrowException() public function testImmutablePartialsLoadersThrowException()
{ {
@@ -223,7 +223,7 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException InvalidArgumentException * @expectedException Mustache_Exception_InvalidArgumentException
*/ */
public function testSetHelpersThrowsExceptions() public function testSetHelpersThrowsExceptions()
{ {
@@ -232,7 +232,7 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException InvalidArgumentException * @expectedException Mustache_Exception_InvalidArgumentException
*/ */
public function testSetLoggerThrowsExceptions() public function testSetLoggerThrowsExceptions()
{ {
@@ -240,6 +240,29 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
$mustache->setLogger(new StdClass); $mustache->setLogger(new StdClass);
} }
public function testLoadPartialCascading()
{
$loader = new Mustache_Loader_ArrayLoader(array(
'foo' => 'FOO',
));
$mustache = new Mustache_Engine(array('loader' => $loader));
$tpl = $mustache->loadTemplate('foo');
$this->assertSame($tpl, $mustache->loadPartial('foo'));
$mustache->setPartials(array(
'foo' => 'f00',
));
// setting partials overrides the default template loading fallback.
$this->assertNotSame($tpl, $mustache->loadPartial('foo'));
// but it didn't overwrite the original template loader templates.
$this->assertSame($tpl, $mustache->loadTemplate('foo'));
}
public function testPartialLoadFailLogging() public function testPartialLoadFailLogging()
{ {
$name = tempnam(sys_get_temp_dir(), 'mustache-test'); $name = tempnam(sys_get_temp_dir(), 'mustache-test');
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Mustache_Test_Exception_SyntaxExceptionTest extends PHPUnit_Framework_TestCase
{
public function testInstance()
{
$e = new Mustache_Exception_SyntaxException('whot', array('is' => 'this'));
$this->assertTrue($e instanceof LogicException);
$this->assertTrue($e instanceof Mustache_Exception);
}
public function testGetToken()
{
$token = array(Mustache_Tokenizer::TYPE => 'whatever');
$e = new Mustache_Exception_SyntaxException('ignore this', $token);
$this->assertEquals($token, $e->getToken());
}
}
@@ -0,0 +1,32 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Mustache_Test_Exception_UnknownFilterExceptionTest extends PHPUnit_Framework_TestCase
{
public function testInstance()
{
$e = new Mustache_Exception_UnknownFilterException('bacon');
$this->assertTrue($e instanceof UnexpectedValueException);
$this->assertTrue($e instanceof Mustache_Exception);
}
public function testMessage()
{
$e = new Mustache_Exception_UnknownFilterException('sausage');
$this->assertEquals('Unknown filter: sausage', $e->getMessage());
}
public function testGetFilterName()
{
$e = new Mustache_Exception_UnknownFilterException('eggs');
$this->assertEquals('eggs', $e->getFilterName());
}
}
@@ -0,0 +1,32 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Mustache_Test_Exception_UnknownHelperExceptionTest extends PHPUnit_Framework_TestCase
{
public function testInstance()
{
$e = new Mustache_Exception_UnknownHelperException('alpha');
$this->assertTrue($e instanceof InvalidArgumentException);
$this->assertTrue($e instanceof Mustache_Exception);
}
public function testMessage()
{
$e = new Mustache_Exception_UnknownHelperException('beta');
$this->assertEquals('Unknown helper: beta', $e->getMessage());
}
public function testGetHelperName()
{
$e = new Mustache_Exception_UnknownHelperException('gamma');
$this->assertEquals('gamma', $e->getHelperName());
}
}
@@ -0,0 +1,32 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2013 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Mustache_Test_Exception_UnknownTemplateExceptionTest extends PHPUnit_Framework_TestCase
{
public function testInstance()
{
$e = new Mustache_Exception_UnknownTemplateException('mario');
$this->assertTrue($e instanceof InvalidArgumentException);
$this->assertTrue($e instanceof Mustache_Exception);
}
public function testMessage()
{
$e = new Mustache_Exception_UnknownTemplateException('luigi');
$this->assertEquals('Unknown template: luigi', $e->getMessage());
}
public function testGetTemplateName()
{
$e = new Mustache_Exception_UnknownTemplateException('yoshi');
$this->assertEquals('yoshi', $e->getTemplateName());
}
}
@@ -67,7 +67,7 @@ class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_T
} }
/** /**
* @expectedException UnexpectedValueException * @expectedException Mustache_Exception_UnknownFilterException
* @dataProvider getBrokenPipes * @dataProvider getBrokenPipes
*/ */
public function testThrowsExceptionForBrokenPipes($tpl, $data) public function testThrowsExceptionForBrokenPipes($tpl, $data)
@@ -42,7 +42,7 @@ class Mustache_Test_Loader_ArrayLoaderTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException InvalidArgumentException * @expectedException Mustache_Exception_UnknownTemplateException
*/ */
public function testMissingTemplatesThrowExceptions() public function testMissingTemplatesThrowExceptions()
{ {
@@ -0,0 +1,40 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2012 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* @group unit
*/
class Mustache_Test_Loader_CascadingLoaderTest extends PHPUnit_Framework_TestCase
{
public function testLoadTemplates()
{
$loader = new Mustache_Loader_CascadingLoader(array(
new Mustache_Loader_ArrayLoader(array('foo' => '{{ foo }}')),
new Mustache_Loader_ArrayLoader(array('bar' => '{{#bar}}BAR{{/bar}}')),
));
$this->assertEquals('{{ foo }}', $loader->load('foo'));
$this->assertEquals('{{#bar}}BAR{{/bar}}', $loader->load('bar'));
}
/**
* @expectedException Mustache_Exception_UnknownTemplateException
*/
public function testMissingTemplatesThrowExceptions()
{
$loader = new Mustache_Loader_CascadingLoader(array(
new Mustache_Loader_ArrayLoader(array('foo' => '{{ foo }}')),
new Mustache_Loader_ArrayLoader(array('bar' => '{{#bar}}BAR{{/bar}}')),
));
$loader->load('not_a_real_template');
}
}
@@ -44,7 +44,7 @@ class Mustache_Test_Loader_FilesystemLoaderTest extends PHPUnit_Framework_TestCa
} }
/** /**
* @expectedException RuntimeException * @expectedException Mustache_Exception_RuntimeException
*/ */
public function testMissingBaseDirThrowsException() public function testMissingBaseDirThrowsException()
{ {
@@ -52,7 +52,7 @@ class Mustache_Test_Loader_FilesystemLoaderTest extends PHPUnit_Framework_TestCa
} }
/** /**
* @expectedException InvalidArgumentException * @expectedException Mustache_Exception_UnknownTemplateException
*/ */
public function testMissingTemplateThrowsException() public function testMissingTemplateThrowsException()
{ {
@@ -0,0 +1,56 @@
<?php
/*
* This file is part of Mustache.php.
*
* (c) 2012 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* @group unit
*/
class Mustache_Test_Loader_InlineLoaderTest extends PHPUnit_Framework_TestCase
{
public function testLoadTemplates()
{
$loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__);
$this->assertEquals('{{ foo }}', $loader->load('foo'));
$this->assertEquals('{{#bar}}BAR{{/bar}}', $loader->load('bar'));
}
/**
* @expectedException Mustache_Exception_UnknownTemplateException
*/
public function testMissingTemplatesThrowExceptions()
{
$loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__);
$loader->load('not_a_real_template');
}
/**
* @expectedException Mustache_Exception_InvalidArgumentException
*/
public function testInvalidOffsetThrowsException()
{
$loader = new Mustache_Loader_InlineLoader(__FILE__, 'notanumber');
}
/**
* @expectedException Mustache_Exception_InvalidArgumentException
*/
public function testInvalidFileThrowsException()
{
$loader = new Mustache_Loader_InlineLoader('notarealfile', __COMPILER_HALT_OFFSET__);
}
}
__halt_compiler();
@@ foo
{{ foo }}
@@ bar
{{#bar}}BAR{{/bar}}
@@ -34,7 +34,7 @@ class Mustache_Test_Logger_StreamLoggerTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException LogicException * @expectedException Mustache_Exception_LogicException
*/ */
public function testPrematurelyClosedStreamThrowsException() public function testPrematurelyClosedStreamThrowsException()
{ {
@@ -187,7 +187,7 @@ class Mustache_Test_Logger_StreamLoggerTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException InvalidArgumentException * @expectedException Mustache_Exception_InvalidArgumentException
*/ */
public function testThrowsInvalidArgumentExceptionWhenSettingUnknownLevels() public function testThrowsInvalidArgumentExceptionWhenSettingUnknownLevels()
{ {
@@ -196,7 +196,7 @@ class Mustache_Test_Logger_StreamLoggerTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException InvalidArgumentException * @expectedException Mustache_Exception_InvalidArgumentException
*/ */
public function testThrowsInvalidArgumentExceptionWhenLoggingUnknownLevels() public function testThrowsInvalidArgumentExceptionWhenLoggingUnknownLevels()
{ {
+1 -1
View File
@@ -108,7 +108,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase
/** /**
* @dataProvider getBadParseTrees * @dataProvider getBadParseTrees
* @expectedException LogicException * @expectedException Mustache_Exception_SyntaxException
*/ */
public function testParserThrowsExceptions($tokens) public function testParserThrowsExceptions($tokens)
{ {