Improve Mustache exception types, catchability.

* Subclass common exception types (runtime, logic, invalid argument).
 * Add new "unknown x" exceptions for filters, helpers and templates.
 * Make all Mustache exceptions implement Mustache_Exception interface… Gotta catch 'em all!
This commit is contained in:
Justin Hileman
2013-01-20 10:35:55 -08:00
parent 5e69e63ffb
commit 0436c59477
22 changed files with 250 additions and 48 deletions
+4 -4
View File
@@ -50,7 +50,7 @@ class Mustache_Compiler
/**
* 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 int $level (default: 0)
@@ -113,7 +113,7 @@ class Mustache_Compiler
break;
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);
}
}
@@ -316,7 +316,7 @@ class Mustache_Compiler
const FILTER = '
$filter = $context->%s(%s);
if (is_string($filter) || !is_callable($filter)) {
throw new UnexpectedValueException(%s);
throw new Mustache_Exception_UnknownFilterException(%s);
}
$value = call_user_func($filter, $value);%s
';
@@ -338,7 +338,7 @@ class Mustache_Compiler
$name = array_shift($filters);
$method = $this->getFindMethod($name);
$filter = ($method !== 'last') ? var_export($name, true) : '';
$msg = var_export(sprintf('Filter not found: %s', $name), true);
$msg = var_export($name, true);
return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $msg, $this->getFilter($filters, $level));
}
+13 -9
View File
@@ -89,6 +89,8 @@ class Mustache_Engine
* 'logger' => new Mustache_StreamLogger('php://stderr'),
* );
*
* @throws Mustache_Exception_InvalidArgumentException If `escape` option is not callable.
*
* @param array $options (default: array())
*/
public function __construct(array $options = array())
@@ -123,7 +125,7 @@ class Mustache_Engine
if (isset($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'];
@@ -233,7 +235,7 @@ class Mustache_Engine
/**
* 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())
*/
@@ -241,7 +243,7 @@ class Mustache_Engine
{
$loader = $this->getPartialsLoader();
if (!$loader instanceof Mustache_Loader_MutableLoader) {
throw new RuntimeException('Unable to set partials on an immutable Mustache Loader instance');
throw new Mustache_Exception_RuntimeException('Unable to set partials on an immutable Mustache Loader instance');
}
$loader->setTemplates($partials);
@@ -254,14 +256,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 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
*/
public function setHelpers($helpers)
{
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();
@@ -343,12 +345,14 @@ class Mustache_Engine
/**
* 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 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;
@@ -636,7 +640,7 @@ class Mustache_Engine
/**
* 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 $source
@@ -655,7 +659,7 @@ class Mustache_Engine
@mkdir($dirName, 0777, true);
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));
}
}
@@ -682,7 +686,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.
*
* @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)
*/
@@ -29,7 +29,7 @@ class Mustache_HelperCollection
{
if ($helpers !== null) {
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) {
@@ -79,6 +79,8 @@ class Mustache_HelperCollection
/**
* Get a helper by name.
*
* @throws Mustache_Exception_UnknownHelperException If helper does not exist.
*
* @param string $name
*
* @return mixed Helper
@@ -86,7 +88,7 @@ class Mustache_HelperCollection
public function get($name)
{
if (!$this->has($name)) {
throw new InvalidArgumentException('Unknown helper: '.$name);
throw new Mustache_Exception_UnknownHelperException($name);
}
return $this->helpers[$name];
@@ -133,14 +135,14 @@ class Mustache_HelperCollection
/**
* 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
*/
public function remove($name)
{
if (!$this->has($name)) {
throw new InvalidArgumentException('Unknown helper: '.$name);
throw new Mustache_Exception_UnknownHelperException($name);
}
unset($this->helpers[$name]);
+3 -1
View File
@@ -43,6 +43,8 @@ class Mustache_Loader_ArrayLoader implements Mustache_Loader, Mustache_Loader_Mu
/**
* Load a Template.
*
* @throws Mustache_Exception_UnknownTemplateException If a template file is not found.
*
* @param string $name
*
* @return string Mustache Template source
@@ -50,7 +52,7 @@ class Mustache_Loader_ArrayLoader implements Mustache_Loader, Mustache_Loader_Mu
public function load($name)
{
if (!isset($this->templates[$name])) {
throw new InvalidArgumentException('Template '.$name.' not found.');
throw new Mustache_Exception_UnknownTemplateException($name);
}
return $this->templates[$name];
+4 -4
View File
@@ -42,7 +42,7 @@ class Mustache_Loader_FilesystemLoader implements Mustache_Loader
* '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 array $options Array of Loader options (default: array())
@@ -52,7 +52,7 @@ class Mustache_Loader_FilesystemLoader implements Mustache_Loader
$this->baseDir = rtrim(realpath($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)) {
@@ -86,7 +86,7 @@ class Mustache_Loader_FilesystemLoader implements Mustache_Loader
/**
* 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
*
@@ -97,7 +97,7 @@ class Mustache_Loader_FilesystemLoader implements Mustache_Loader
$fileName = $this->getFileName($name);
if (!file_exists($fileName)) {
throw new InvalidArgumentException('Template '.$name.' not found.');
throw new Mustache_Exception_UnknownTemplateException($name);
}
return file_get_contents($fileName);
+9 -6
View File
@@ -64,14 +64,14 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
/**
* 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
*/
public function setLevel($level)
{
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;
@@ -90,7 +90,7 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
/**
* 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 string $message
@@ -99,7 +99,7 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
public function log($level, $message, array $context = array())
{
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]) {
@@ -110,6 +110,9 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
/**
* 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 string $message The log message
* @param array $context The log context
@@ -118,13 +121,13 @@ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger
{
if (!is_resource($this->stream)) {
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');
if (!is_resource($this->stream)) {
// @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
}
}
+8 -5
View File
@@ -32,12 +32,12 @@ class Mustache_Parser
/**
* 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 array $parent Parent token (default: null)
*
* @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)
{
@@ -58,11 +58,13 @@ class Mustache_Parser
case Mustache_Tokenizer::T_END_SECTION:
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]) {
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];
@@ -80,7 +82,8 @@ class Mustache_Parser
} while ($tokens->valid());
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;
+2 -2
View File
@@ -85,9 +85,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->compile('', array(array(Mustache_Tokenizer::TYPE => 'invalid')), 'SomeClass');
+4 -4
View File
@@ -141,7 +141,7 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
}
/**
* @expectedException InvalidArgumentException
* @expectedException Mustache_Exception_InvalidArgumentException
* @dataProvider getBadEscapers
*/
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()
{
@@ -223,7 +223,7 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
}
/**
* @expectedException InvalidArgumentException
* @expectedException Mustache_Exception_InvalidArgumentException
*/
public function testSetHelpersThrowsExceptions()
{
@@ -232,7 +232,7 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
}
/**
* @expectedException InvalidArgumentException
* @expectedException Mustache_Exception_InvalidArgumentException
*/
public function testSetLoggerThrowsExceptions()
{
@@ -67,7 +67,7 @@ class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_T
}
/**
* @expectedException UnexpectedValueException
* @expectedException Mustache_Exception_UnknownFilterException
* @dataProvider getBrokenPipes
*/
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()
{
@@ -44,7 +44,7 @@ class Mustache_Test_Loader_FilesystemLoaderTest extends PHPUnit_Framework_TestCa
}
/**
* @expectedException RuntimeException
* @expectedException Mustache_Exception_RuntimeException
*/
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()
{
@@ -34,7 +34,7 @@ class Mustache_Test_Logger_StreamLoggerTest extends PHPUnit_Framework_TestCase
}
/**
* @expectedException LogicException
* @expectedException Mustache_Exception_LogicException
*/
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()
{
@@ -196,7 +196,7 @@ class Mustache_Test_Logger_StreamLoggerTest extends PHPUnit_Framework_TestCase
}
/**
* @expectedException InvalidArgumentException
* @expectedException Mustache_Exception_InvalidArgumentException
*/
public function testThrowsInvalidArgumentExceptionWhenLoggingUnknownLevels()
{
+1 -1
View File
@@ -108,7 +108,7 @@ class Mustache_Test_ParserTest extends PHPUnit_Framework_TestCase
/**
* @dataProvider getBadParseTrees
* @expectedException LogicException
* @expectedException Mustache_Exception_SyntaxException
*/
public function testParserThrowsExceptions($tokens)
{