Merge pull request #8451 from creative-commoners/pulls/4.3/memory-cache-findtemplate

NEW Add memory cache to ThemeResourceLoader::findTemplate()
This commit is contained in:
Maxime Rainville 2018-10-08 10:38:07 +13:00 committed by GitHub
commit 36b1066413
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 101 additions and 13 deletions

View File

@ -32,3 +32,7 @@ SilverStripe\Core\Injector\Injector:
namespace: "InheritedPermissions"
args:
disable-container: true
Psr\SimpleCache\CacheInterface.ThemeResourceLoader:
factory: SilverStripe\Core\Cache\CacheFactory
constructor:
namespace: 'ThemeResourceLoader'

View File

@ -151,7 +151,7 @@ class MySQLSchemaManager extends DBSchemaManager
public function renameTable($oldTableName, $newTableName)
{
if (!$this->hasTable($oldTableName)) {
throw new LogicException('Table '. $oldTableName . ' does not exist.');
throw new LogicException('Table ' . $oldTableName . ' does not exist.');
}
return $this->query("ALTER TABLE \"$oldTableName\" RENAME \"$newTableName\"");

View File

@ -3,13 +3,16 @@
namespace SilverStripe\View;
use InvalidArgumentException;
use Psr\SimpleCache\CacheInterface;
use SilverStripe\Core\Flushable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\Core\Path;
/**
* Handles finding templates from a stack of template manifest objects.
*/
class ThemeResourceLoader
class ThemeResourceLoader implements Flushable
{
/**
@ -17,6 +20,13 @@ class ThemeResourceLoader
*/
private static $instance;
/**
* Internal memory cache for large sets of repeated calls
*
* @var array
*/
protected static $cacheData = [];
/**
* The base path of the application
*
@ -32,6 +42,11 @@ class ThemeResourceLoader
*/
protected $sets = [];
/**
* @var CacheInterface
*/
protected $cache;
/**
* @return ThemeResourceLoader
*/
@ -161,6 +176,8 @@ class ThemeResourceLoader
* format "type/name", where type is the type of template to search for
* (e.g. Includes, Layout).
*
* The results of this method will be cached for future use.
*
* @param string|array $template Template name, or template spec in array format with the keys
* 'type' (type string) and 'templates' (template hierarchy in order of precedence).
* If 'templates' is ommitted then any other item in the array will be treated as the template
@ -178,6 +195,12 @@ class ThemeResourceLoader
$themes = SSViewer::get_themes();
}
// Look for a cached result for this data set
$cacheKey = md5(json_encode($template) . json_encode($themes));
if ($this->getCache()->has($cacheKey)) {
return $this->getCache()->get($cacheKey);
}
$type = '';
if (is_array($template)) {
// Check if templates has type specified
@ -196,6 +219,7 @@ class ThemeResourceLoader
if (is_array($template)) {
$path = $this->findTemplate($template, $themes);
if ($path) {
$this->getCache()->set($cacheKey, $path);
return $path;
}
continue;
@ -205,6 +229,7 @@ class ThemeResourceLoader
// pass in templates without extensions in order for template manifest to find
// files dynamically.
if (substr($template, -3) == '.ss' && file_exists($template)) {
$this->getCache()->set($cacheKey, $template);
return $template;
}
@ -220,12 +245,14 @@ class ThemeResourceLoader
$pathParts = [ $this->base, $themePath, 'templates', $head, $type, $tail ];
$path = Path::join($pathParts) . '.ss';
if (file_exists($path)) {
$this->getCache()->set($cacheKey, $path);
return $path;
}
}
}
// No template found
$this->getCache()->set($cacheKey, null);
return null;
}
@ -337,4 +364,33 @@ class ThemeResourceLoader
}
return $paths;
}
/**
* Flush any cached data
*/
public static function flush()
{
self::inst()->getCache()->clear();
}
/**
* @return CacheInterface
*/
public function getCache()
{
if (!$this->cache) {
$this->setCache(Injector::inst()->get(CacheInterface::class . '.ThemeResourceLoader'));
}
return $this->cache;
}
/**
* @param CacheInterface $cache
* @return ThemeResourceLoader
*/
public function setCache(CacheInterface $cache)
{
$this->cache = $cache;
return $this;
}
}

View File

@ -2,6 +2,7 @@
namespace SilverStripe\Core\Tests\Manifest;
use Psr\SimpleCache\CacheInterface;
use SilverStripe\Control\Director;
use SilverStripe\Core\Manifest\ModuleLoader;
use SilverStripe\View\ThemeResourceLoader;
@ -54,6 +55,9 @@ class ThemeResourceLoaderTest extends SapphireTest
// New Loader for that root
$this->loader = new ThemeResourceLoader($this->base);
$this->loader->addSet('$default', $this->manifest);
// Ensure the cache is flushed between tests
ThemeResourceLoader::flush();
}
protected function tearDown()
@ -377,4 +381,28 @@ class ThemeResourceLoaderTest extends SapphireTest
{
$this->assertEquals($path, $this->loader->getPath($name));
}
public function testFindTemplateWithCacheMiss()
{
$mockCache = $this->createMock(CacheInterface::class);
$mockCache->expects($this->once())->method('has')->willReturn(false);
$mockCache->expects($this->never())->method('get');
$mockCache->expects($this->once())->method('set');
$loader = new ThemeResourceLoader();
$loader->setCache($mockCache);
$loader->findTemplate('Page', ['$default']);
}
public function testFindTemplateWithCacheHit()
{
$mockCache = $this->createMock(CacheInterface::class);
$mockCache->expects($this->once())->method('has')->willReturn(true);
$mockCache->expects($this->never())->method('set');
$mockCache->expects($this->once())->method('get')->willReturn('mock_template.ss');
$loader = new ThemeResourceLoader();
$loader->setCache($mockCache);
$this->assertSame('mock_template.ss', $loader->findTemplate('Page', ['$default']));
}
}