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.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
interface Mustache_Cache
|
||||
{
|
||||
public function get($key);
|
||||
public function put($key, $value);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
class Mustache_Cache_NoopCache implements Mustache_Cache
|
||||
{
|
||||
public function get($key) { return null; }
|
||||
public function put($key, $value) {}
|
||||
}
|
||||
+49
-90
@@ -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)) {
|
||||
$cached = $this->getCache()->get($source);
|
||||
if (!$cached) {
|
||||
$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 {
|
||||
$this->log(
|
||||
Mustache_Logger::WARNING,
|
||||
'Template cache disabled, evaluating "{className}" class at runtime',
|
||||
'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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user