瀏覽代碼

Extract cache interface

Refactoring to allow for alternative cache implementations. Includes
implementations for the default noop (i.e. un-cached) behavior and a
filesystem-based cache whose configuration is consistent with the
existing Mustache_Engine constructor.

Includes updated and new tests.
Amit Snyderman 12 年之前
父節點
當前提交
60338e4956

+ 7 - 0
src/Mustache/Cache.php

@@ -0,0 +1,7 @@
+<?php
+
+interface Mustache_Cache
+{
+    public function get($key);
+    public function put($key, $value);
+}

+ 51 - 0
src/Mustache/Cache/FilesystemCache.php

@@ -0,0 +1,51 @@
+<?php
+
+class Mustache_Cache_FilesystemCache implements Mustache_Cache
+{
+    private $directory;
+    private $fileMode;
+
+    public function __construct($directory, $fileMode = null)
+    {
+        $this->directory = $directory;
+        $this->fileMode = $fileMode;
+    }
+
+    public function get($key)
+    {
+        $fileName = $this->getCacheFilename($key);
+        return (is_file($fileName))
+            ? file_get_contents($fileName)
+            : null;
+    }
+
+    public function put($key, $value)
+    {
+        $fileName = $this->getCacheFilename($key);
+        $dirName = dirname($fileName);
+        if (!is_dir($dirName)) {
+            @mkdir($dirName, 0777, true);
+            if (!is_dir($dirName)) {
+                throw new Mustache_Exception_RuntimeException(sprintf('Failed to create cache directory "%s".', $dirName));
+            }
+
+        }
+
+        $tempFile = tempnam($dirName, basename($fileName));
+        if (false !== @file_put_contents($tempFile, $value)) {
+            if (@rename($tempFile, $fileName)) {
+                $mode = isset($this->fileMode) ? $this->fileMode : (0666 & ~umask());
+                @chmod($fileName, $mode);
+
+                return;
+            }
+        }
+
+        throw new Mustache_Exception_RuntimeException(sprintf('Failed to write cache file "%s".', $fileName));
+    }
+
+    protected function getCacheFilename($name)
+    {
+        return sprintf('%s/%s.php', $this->directory, md5($name));
+    }
+}

+ 7 - 0
src/Mustache/Cache/NoopCache.php

@@ -0,0 +1,7 @@
+<?php
+
+class Mustache_Cache_NoopCache implements Mustache_Cache
+{
+    public function get($key) { return null; }
+    public function put($key, $value) {}
+}

+ 50 - 91
src/Mustache/Engine.php

@@ -33,8 +33,7 @@ class Mustache_Engine
 
     // Environment
     private $templateClassPrefix = '__Mustache_';
-    private $cache = null;
-    private $cacheFileMode = null;
+    private $cache;
     private $loader;
     private $partialsLoader;
     private $helpers;
@@ -53,6 +52,9 @@ class Mustache_Engine
      *         // The class prefix for compiled templates. Defaults to '__Mustache_'.
      *         'template_class_prefix' => '__MyTemplates_',
      *
+     *         // A Mustache cache instance. Uses a NoopCache if not specified.
+     *         'cacher' => new Mustache_Cache_FilesystemCache(dirname(__FILE__).'/tmp/cache/mustache'),
+     *
      *         // A cache directory for compiled templates. Mustache will not cache templates unless this is set
      *         'cache' => dirname(__FILE__).'/tmp/cache/mustache',
      *
@@ -111,12 +113,13 @@ class Mustache_Engine
             $this->templateClassPrefix = $options['template_class_prefix'];
         }
 
-        if (isset($options['cache'])) {
-            $this->cache = $options['cache'];
-        }
-
-        if (isset($options['cache_file_mode'])) {
-            $this->cacheFileMode = $options['cache_file_mode'];
+        if (isset($options['cacher'])) {
+            $this->cache = $options['cacher'];
+        } else if (isset($options['cache'])) {
+            $this->cache = new Mustache_Cache_FilesystemCache(
+                $options['cache'],
+                $options['cache_file_mode']
+            );
         }
 
         if (isset($options['loader'])) {
@@ -479,6 +482,38 @@ class Mustache_Engine
         return $this->compiler;
     }
 
+    /**
+     * Set the Mustache Tokenizer instance.
+     *
+     * @param Mustache_Cache $cache
+     */
+    public function setCache(Mustache_Cache $cache)
+    {
+        $this->cache = $cache;
+    }
+
+    /**
+     * Get the current Mustache Cache instance.
+     *
+     * If no Cache instance has been explicitly specified, this method will instantiate and return a new one.
+     *
+     * @return Mustache_Cache
+     */
+    public function getCache()
+    {
+        if (!isset($this->cache)) {
+            $this->cache = new Mustache_Cache_NoopCache();
+
+            $this->log(
+                Mustache_Logger::WARNING,
+                'Template cache disabled',
+                array()
+            );
+        }
+
+        return $this->cache;
+    }
+
     /**
      * Helper method to generate a Mustache template class.
      *
@@ -580,27 +615,17 @@ class Mustache_Engine
 
         if (!isset($this->templates[$className])) {
             if (!class_exists($className, false)) {
-                if ($fileName = $this->getCacheFilename($source)) {
-                    if (!is_file($fileName)) {
-                        $this->log(
-                            Mustache_Logger::DEBUG,
-                            'Writing "{className}" class to template cache: "{fileName}"',
-                            array('className' => $className, 'fileName' => $fileName)
-                        );
-
-                        $this->writeCacheFile($fileName, $this->compile($source));
-                    }
-
-                    require_once $fileName;
-                } else {
+                $cached = $this->getCache()->get($source);
+                if (!$cached) {
                     $this->log(
-                        Mustache_Logger::WARNING,
-                        'Template cache disabled, evaluating "{className}" class at runtime',
+                        Mustache_Logger::DEBUG,
+                        'Writing "{className}" class to template cache',
                         array('className' => $className)
                     );
-
-                    eval('?>'.$this->compile($source));
+                    $cached = $this->compile($source);
+                    $this->getCache()->put($source, $cached);
                 }
+                eval('?>'.$cached);
             }
 
             $this->log(
@@ -666,72 +691,6 @@ class Mustache_Engine
         return $this->getCompiler()->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags);
     }
 
-    /**
-     * Helper method to generate a Mustache Template class cache filename.
-     *
-     * @param string $source
-     *
-     * @return string Mustache Template class cache filename
-     */
-    private function getCacheFilename($source)
-    {
-        if ($this->cache) {
-            return sprintf('%s/%s.php', $this->cache, $this->getTemplateClassName($source));
-        }
-    }
-
-    /**
-     * Helper method to dump a generated Mustache Template subclass to the file cache.
-     *
-     * @throws Mustache_Exception_RuntimeException if unable to create the cache directory or write to $fileName.
-     *
-     * @param string $fileName
-     * @param string $source
-     *
-     * @codeCoverageIgnore
-     */
-    private function writeCacheFile($fileName, $source)
-    {
-        $dirName = dirname($fileName);
-        if (!is_dir($dirName)) {
-            $this->log(
-                Mustache_Logger::INFO,
-                'Creating Mustache template cache directory: "{dirName}"',
-                array('dirName' => $dirName)
-            );
-
-            @mkdir($dirName, 0777, true);
-            if (!is_dir($dirName)) {
-                throw new Mustache_Exception_RuntimeException(sprintf('Failed to create cache directory "%s".', $dirName));
-            }
-
-        }
-
-        $this->log(
-            Mustache_Logger::DEBUG,
-            'Caching compiled template to "{fileName}"',
-            array('fileName' => $fileName)
-        );
-
-        $tempFile = tempnam($dirName, basename($fileName));
-        if (false !== @file_put_contents($tempFile, $source)) {
-            if (@rename($tempFile, $fileName)) {
-                $mode = isset($this->cacheFileMode) ? $this->cacheFileMode : (0666 & ~umask());
-                @chmod($fileName, $mode);
-
-                return;
-            }
-
-            $this->log(
-                Mustache_Logger::ERROR,
-                'Unable to rename Mustache temp cache file: "{tempName}" -> "{fileName}"',
-                array('tempName' => $tempFile, 'fileName' => $fileName)
-            );
-        }
-
-        throw new Mustache_Exception_RuntimeException(sprintf('Failed to write cache file "%s".', $fileName));
-    }
-
     /**
      * Add a log record if logging is enabled.
      *

+ 58 - 0
test/Mustache/Test/Cache/FilesystemCacheTest.php

@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @group functional
+ */
+class Mustache_Test_Cache_FilesystemCacheTest extends PHPUnit_Framework_TestCase
+{
+    private static $tempDir;
+
+    public static function setUpBeforeClass()
+    {
+        self::$tempDir = sys_get_temp_dir() . '/mustache_test';
+        if (file_exists(self::$tempDir)) {
+            self::rmdir(self::$tempDir);
+        }
+    }
+
+    public function testCacheGetNone()
+    {
+        $key = 'some key';
+        $cache = new Mustache_Cache_FilesystemCache(self::$tempDir);;
+        $cached = $cache->get($key);
+
+        $this->assertNull($cached);
+    }
+
+    public function testCachePut()
+    {
+        $key = 'some key';
+        $value = 'some value';
+        $cache = new Mustache_Cache_FilesystemCache(self::$tempDir);;
+        $cache->put($key, $value);
+        $cached = $cache->get($key);
+
+        $this->assertEquals($cached, $value);
+    }
+
+    private static function rmdir($path)
+    {
+        $path = rtrim($path, '/').'/';
+        $handle = opendir($path);
+        while (($file = readdir($handle)) !== false) {
+            if ($file == '.' || $file == '..') {
+                continue;
+            }
+
+            $fullpath = $path.$file;
+            if (is_dir($fullpath)) {
+                self::rmdir($fullpath);
+            } else {
+                unlink($fullpath);
+            }
+        }
+
+        closedir($handle);
+        rmdir($path);
+    }
+}

+ 8 - 4
test/Mustache/Test/EngineTest.php

@@ -58,6 +58,7 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
         $this->assertTrue($mustache->hasHelper('foo'));
         $this->assertTrue($mustache->hasHelper('bar'));
         $this->assertFalse($mustache->hasHelper('baz'));
+        $this->assertInstanceOf('Mustache_Cache_FilesystemCache', $mustache->getCache());
     }
 
     public static function getFoo()
@@ -95,6 +96,7 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
         $parser    = new Mustache_Parser;
         $compiler  = new Mustache_Compiler;
         $mustache  = new Mustache_Engine;
+        $cache     = new Mustache_Cache_FilesystemCache(sys_get_temp_dir());
 
         $this->assertNotSame($logger, $mustache->getLogger());
         $mustache->setLogger($logger);
@@ -119,6 +121,10 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
         $this->assertNotSame($compiler, $mustache->getCompiler());
         $mustache->setCompiler($compiler);
         $this->assertSame($compiler, $mustache->getCompiler());
+
+        $this->assertNotSame($cache, $mustache->getCache());
+        $mustache->setCache($cache);
+        $this->assertSame($cache, $mustache->getCache());
     }
 
     /**
@@ -134,10 +140,8 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
         $source    = '{{ foo }}';
         $template  = $mustache->loadTemplate($source);
         $className = $mustache->getTemplateClassName($source);
-        $fileName  = self::$tempDir . '/' . $className . '.php';
+
         $this->assertInstanceOf($className, $template);
-        $this->assertFileExists($fileName);
-        $this->assertContains("\nclass $className extends Mustache_Template", file_get_contents($fileName));
     }
 
     /**
@@ -290,7 +294,7 @@ class Mustache_Test_EngineTest extends PHPUnit_Framework_TestCase
         $result = $mustache->render('{{ foo }}', array('foo' => 'FOO'));
         $this->assertEquals('FOO', $result);
 
-        $this->assertContains('WARNING: Template cache disabled, evaluating', file_get_contents($name));
+        $this->assertContains('WARNING: Template cache disabled', file_get_contents($name));
     }
 
     public function testLoggingIsNotTooAnnoying()