From 60338e495656f3735b82dc8e44c0c2dbe23cd799 Mon Sep 17 00:00:00 2001 From: Amit Snyderman Date: Thu, 5 Sep 2013 17:07:38 -0400 Subject: [PATCH] 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. --- src/Mustache/Cache.php | 7 + src/Mustache/Cache/FilesystemCache.php | 51 +++++++ src/Mustache/Cache/NoopCache.php | 7 + src/Mustache/Engine.php | 141 +++++++----------- .../Test/Cache/FilesystemCacheTest.php | 58 +++++++ test/Mustache/Test/EngineTest.php | 12 +- 6 files changed, 181 insertions(+), 95 deletions(-) create mode 100644 src/Mustache/Cache.php create mode 100644 src/Mustache/Cache/FilesystemCache.php create mode 100644 src/Mustache/Cache/NoopCache.php create mode 100644 test/Mustache/Test/Cache/FilesystemCacheTest.php diff --git a/src/Mustache/Cache.php b/src/Mustache/Cache.php new file mode 100644 index 0000000..a9ab043 --- /dev/null +++ b/src/Mustache/Cache.php @@ -0,0 +1,7 @@ +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)); + } +} diff --git a/src/Mustache/Cache/NoopCache.php b/src/Mustache/Cache/NoopCache.php new file mode 100644 index 0000000..3d722ab --- /dev/null +++ b/src/Mustache/Cache/NoopCache.php @@ -0,0 +1,7 @@ + '__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. * diff --git a/test/Mustache/Test/Cache/FilesystemCacheTest.php b/test/Mustache/Test/Cache/FilesystemCacheTest.php new file mode 100644 index 0000000..ae70e73 --- /dev/null +++ b/test/Mustache/Test/Cache/FilesystemCacheTest.php @@ -0,0 +1,58 @@ +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); + } +} diff --git a/test/Mustache/Test/EngineTest.php b/test/Mustache/Test/EngineTest.php index 5c00821..5b5402b 100644 --- a/test/Mustache/Test/EngineTest.php +++ b/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()