Better filters implementation:

* Throw UnexpectedValueException when unknown
   filter is found.
 * More optimized compiler code.
 * Falsey initial values will still be fed through
   the pipe.
This commit is contained in:
Justin Hileman
2012-10-01 14:51:15 -07:00
parent 1cf44d5de9
commit fc453b5d01
2 changed files with 67 additions and 42 deletions
+32 -15
View File
@@ -297,13 +297,6 @@ class Mustache_Compiler
return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value); 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. * Generate Mustache Template variable filtering PHP source.
* *
@@ -314,17 +307,41 @@ class Mustache_Compiler
*/ */
private function getFilters($id, $level) private function getFilters($id, $level)
{ {
$chunks = array_map('trim', explode('|', $id)); $filters = array_map('trim', explode('|', $id));
$id = array_shift($chunks); $id = array_shift($filters);
$filters = '';
foreach ($chunks as $filter) { return array($id, $this->getFilter($filters, $level));
$method = $this->getFindMethod($filter);
$filter = ($method !== 'last') ? var_export($filter, true) : '';
$filters .= sprintf($this->prepare(self::FILTER, $level), $method, $filter);
} }
return array($id, $filters); 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 '';
}
$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";'; const LINE = '$buffer .= "\n";';
@@ -13,15 +13,18 @@
* @group filters * @group filters
* @group functional * @group functional
*/ */
class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_TestCase { class Mustache_Test_FiveThree_Functional_FiltersTest extends PHPUnit_Framework_TestCase
{
private $mustache; private $mustache;
public function setUp() { public function setUp()
{
$this->mustache = new Mustache_Engine; $this->mustache = new Mustache_Engine;
} }
public function testSingleFilter() { public function testSingleFilter()
{
$tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{ date | longdate }}'); $tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{ date | longdate }}');
$this->mustache->addHelper('longdate', function(\DateTime $value) { $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)); $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 }}'); $tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{ date | longdate | withbrackets }}');
$this->mustache->addHelper('longdate', function(\DateTime $value) { $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)); $this->assertEquals('[[2000-01-01 12:01:00]]', $tpl->render($foo));
} }
public function testBrokenPipe() { public function testInterpolateFirst()
$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() {
$tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{ foo | bar }}'); $tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{ foo | bar }}');
$this->assertEquals('win!', $tpl->render(array( $this->assertEquals('win!', $tpl->render(array(
'foo' => 'FOO', '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'; })),
);
}
} }