Преглед на файлове

Better filters implementation:

 * Throw UnexpectedValueException when unknown
   filter is found.
 * More optimized compiler code.
 * Falsey initial values will still be fed through
   the pipe.
Justin Hileman преди 13 години
родител
ревизия
fc453b5d01
променени са 2 файла, в които са добавени 67 реда и са изтрити 42 реда
  1. 32 15
      src/Mustache/Compiler.php
  2. 35 27
      test/Mustache/Test/FiveThree/Functional/FiltersTest.php

+ 32 - 15
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 '';
+        }
+
+        $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 array($id, $filters);
+        return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $this->getFilter($filters, $level + 1), $msg);
     }
 
     const LINE = '$buffer .= "\n";';

+ 35 - 27
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'; })),
+        );
+    }
 }