From fc453b5d0100003a734d47fd3584c7fe6f43a40f Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Wed, 1 Aug 2012 23:00:42 -0700 Subject: [PATCH] Better filters implementation: * Throw UnexpectedValueException when unknown filter is found. * More optimized compiler code. * Falsey initial values will still be fed through the pipe. --- src/Mustache/Compiler.php | 47 +++++++++----- .../Test/FiveThree/Functional/FiltersTest.php | 62 +++++++++++-------- 2 files changed, 67 insertions(+), 42 deletions(-) diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index 293f9e0..1c3bcc4 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -297,13 +297,6 @@ class Mustache_Compiler return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value); } - const FILTER = ' - if (!empty($value)) { - $filter = $context->%s(%s); - $value = (is_string($filter) || !is_callable($filter)) ? "" : call_user_func($filter, $value); - } - '; - /** * Generate Mustache Template variable filtering PHP source. * @@ -314,17 +307,41 @@ class Mustache_Compiler */ private function getFilters($id, $level) { - $chunks = array_map('trim', explode('|', $id)); - $id = array_shift($chunks); - $filters = ''; + $filters = array_map('trim', explode('|', $id)); + $id = array_shift($filters); - foreach ($chunks as $filter) { - $method = $this->getFindMethod($filter); - $filter = ($method !== 'last') ? var_export($filter, true) : ''; - $filters .= sprintf($this->prepare(self::FILTER, $level), $method, $filter); + return array($id, $this->getFilter($filters, $level)); + } + + const FILTER = ' + $filter = $context->%s(%s); + if (!is_string($filter) && is_callable($filter)) { + $value = call_user_func($filter, $value);%s + } else { + throw new UnexpectedValueException(%s); + } + '; + + /** + * Generate PHP source for a single filter. + * + * @param array $filters + * @param int $level + * + * @return string Generated filter PHP source + */ + private function getFilter(array $filters, $level) + { + if (empty($filters)) { + return ''; } - return array($id, $filters); + $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); + + return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $this->getFilter($filters, $level + 1), $msg); } const LINE = '$buffer .= "\n";'; diff --git a/test/Mustache/Test/FiveThree/Functional/FiltersTest.php b/test/Mustache/Test/FiveThree/Functional/FiltersTest.php index 1d1af85..b7d54d3 100644 --- a/test/Mustache/Test/FiveThree/Functional/FiltersTest.php +++ b/test/Mustache/Test/FiveThree/Functional/FiltersTest.php @@ -13,15 +13,18 @@ * @group filters * @group functional */ -class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_TestCase { +class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_TestCase +{ private $mustache; - public function setUp() { + public function setUp() + { $this->mustache = new Mustache_Engine; } - public function testSingleFilter() { + public function testSingleFilter() + { $tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{ date | longdate }}'); $this->mustache->addHelper('longdate', function(\DateTime $value) { @@ -34,7 +37,8 @@ class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_T $this->assertEquals('2000-01-01 12:01:00', $tpl->render($foo)); } - public function testChainedFilters() { + public function testChainedFilters() + { $tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{ date | longdate | withbrackets }}'); $this->mustache->addHelper('longdate', function(\DateTime $value) { @@ -51,29 +55,8 @@ class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_T $this->assertEquals('[[2000-01-01 12:01:00]]', $tpl->render($foo)); } - public function testBrokenPipe() { - $tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{ foo | bar | baz }}'); - $this->assertEquals('', $tpl->render(array( - 'foo' => 'FOO', - ))); - - $this->assertEquals('', $tpl->render(array( - 'foo' => 'FOO', - 'bar' => function($value) { return 'BAR'; }, - ))); - - $this->assertEquals('', $tpl->render(array( - 'foo' => 'FOO', - 'baz' => function($value) { return 'BAZ'; }, - ))); - - $this->assertEquals('', $tpl->render(array( - 'bar' => function($value) { return 'BAR'; }, - 'baz' => function($value) { return 'BAZ'; }, - ))); - } - - public function testInterpolateFirst() { + public function testInterpolateFirst() + { $tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{ foo | bar }}'); $this->assertEquals('win!', $tpl->render(array( 'foo' => 'FOO', @@ -82,4 +65,29 @@ class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_T }, ))); } + + /** + * @expectedException UnexpectedValueException + * @dataProvider getBrokenPipes + */ + public function testThrowsExceptionForBrokenPipes($tpl, $data) + { + $this->mustache + ->loadTemplate(sprintf('{{%% FILTERS }}{{ %s }}', $tpl)) + ->render($data); + } + + public function getBrokenPipes() + { + return array( + array('foo | bar', array()), + array('foo | bar', array('foo' => 'FOO')), + array('foo | bar', array('foo' => 'FOO', 'bar' => 'BAR')), + array('foo | bar | baz', array('foo' => 'FOO', 'bar' => function() { return 'BAR'; })), + array('foo | bar | baz', array('foo' => 'FOO', 'baz' => function() { return 'BAZ'; })), + array('foo | bar | baz', array('bar' => function() { return 'BAR'; })), + array('foo | bar | baz', array('baz' => function() { return 'BAZ'; })), + array('foo | bar.baz', array('foo' => 'FOO', 'bar' => function() { return 'BAR'; }, 'baz' => function() { return 'BAZ'; })), + ); + } }