mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-16 20:36:33 +00:00
Implemented filesystem cache, one and only (see #1415)
This commit is contained in:
committed by
Fabien Potencier
parent
8af7659e3f
commit
04cc7e469d
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Twig.
|
||||||
|
*
|
||||||
|
* (c) 2014 Fabien Potencier
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface Twig_Cache_CacheInterface needs to be implemented by cache classes
|
||||||
|
*
|
||||||
|
* @author Andrew Tch <andrew@noop.lv>
|
||||||
|
*/
|
||||||
|
interface Twig_Cache_CacheInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns cache key depending on current implementation.
|
||||||
|
*
|
||||||
|
* @param string $className Template class name
|
||||||
|
* @param string $prefix Global template class prefix
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCacheKey($className, $prefix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all cached templates
|
||||||
|
*/
|
||||||
|
public function clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if given cache entity exists.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes compiled template to cache.
|
||||||
|
*
|
||||||
|
* @param $key
|
||||||
|
* @param $content
|
||||||
|
*/
|
||||||
|
public function write($key, $content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads cache file. This function returns nothing, it should require or eval() the compiled template.
|
||||||
|
*
|
||||||
|
* @param $key
|
||||||
|
*/
|
||||||
|
public function load($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns timestamp of cached template..
|
||||||
|
*
|
||||||
|
* @param $key
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getTimestamp($key);
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Twig.
|
||||||
|
*
|
||||||
|
* (c) 2014 Fabien Potencier
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Twig_Cache_FilesystemCache - the one and only officially supported cache for Twig
|
||||||
|
*
|
||||||
|
* @author Andrew Tch <andrew@noop.lv>
|
||||||
|
*/
|
||||||
|
class Twig_Cache_FilesystemCache implements Twig_Cache_CacheInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Root cache directory
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $directory;
|
||||||
|
|
||||||
|
public function __construct($directory)
|
||||||
|
{
|
||||||
|
$this->directory = $directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getCacheKey($className, $prefix)
|
||||||
|
{
|
||||||
|
$class = substr($className, strlen($prefix));
|
||||||
|
|
||||||
|
return $this->directory.'/'.substr($class, 0, 2).'/'.substr($class, 2, 2).'/'.substr($class, 4).'.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function clear()
|
||||||
|
{
|
||||||
|
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->directory), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
|
||||||
|
if ($file->isFile()) {
|
||||||
|
@unlink($file->getPathname());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function has($key)
|
||||||
|
{
|
||||||
|
return is_readable($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function load($key)
|
||||||
|
{
|
||||||
|
require_once $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function write($key, $content)
|
||||||
|
{
|
||||||
|
$dir = dirname($key);
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
|
||||||
|
throw new RuntimeException(sprintf("Unable to create the cache directory (%s).", $dir));
|
||||||
|
}
|
||||||
|
} elseif (!is_writable($dir)) {
|
||||||
|
throw new RuntimeException(sprintf("Unable to write in the cache directory (%s).", $dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmpFile = tempnam($dir, basename($key));
|
||||||
|
if (false !== @file_put_contents($tmpFile, $content)) {
|
||||||
|
// rename does not work on Win32 before 5.2.6
|
||||||
|
if (@rename($tmpFile, $key) || (@copy($tmpFile, $key) && unlink($tmpFile))) {
|
||||||
|
@chmod($key, 0666 & ~umask());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException(sprintf('Failed to write cache file "%s".', $key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getTimestamp($key)
|
||||||
|
{
|
||||||
|
return filemtime($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Twig.
|
||||||
|
*
|
||||||
|
* (c) 2014 Fabien Potencier
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Twig_Cache_MemoryCache - use this to disable caching
|
||||||
|
*
|
||||||
|
* @author Andrew Tch <andrew@noop.lv>
|
||||||
|
*/
|
||||||
|
class Twig_Cache_MemoryCache implements Twig_Cache_CacheInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Current cached files
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $data = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getCacheKey($className, $prefix)
|
||||||
|
{
|
||||||
|
return $className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function clear()
|
||||||
|
{
|
||||||
|
$this->data = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function has($key)
|
||||||
|
{
|
||||||
|
return isset($this->data[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function write($key, $content)
|
||||||
|
{
|
||||||
|
$this->data[$key] = array(
|
||||||
|
'data' => $content,
|
||||||
|
'timestamp' => time()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function load($key)
|
||||||
|
{
|
||||||
|
eval('?>'.$this->data[$key]['data']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getTimestamp($key)
|
||||||
|
{
|
||||||
|
return $this->data[$key]['timestamp'];
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
-57
@@ -213,24 +213,32 @@ class Twig_Environment
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the cache directory or false if cache is disabled.
|
* Gets the current cache implementation.
|
||||||
*
|
*
|
||||||
* @return string|false
|
* @return Twig_Cache_CacheInterface
|
||||||
*/
|
*/
|
||||||
public function getCache()
|
public function getCache()
|
||||||
{
|
{
|
||||||
return $this->cache;
|
return $this->cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the cache directory or false if cache is disabled.
|
* Sets the current cache implementation. Can be absolute path to the directory (Twig_Cache_FilesystemCache will
|
||||||
*
|
* be used then), or instance of Twig_Cache_CacheInterface if you need custom cache implementation. Anything else
|
||||||
* @param string|false $cache The absolute path to the compiled templates,
|
* will cause Twig_Cache_MemoryCache to be used.
|
||||||
* or false to disable cache
|
*
|
||||||
*/
|
* @param Twig_Cache_CacheInterface|string|mixed $cache The absolute path to the compiled templates,
|
||||||
|
* or false to disable cache
|
||||||
|
*/
|
||||||
public function setCache($cache)
|
public function setCache($cache)
|
||||||
{
|
{
|
||||||
$this->cache = $cache ? $cache : false;
|
if ($cache instanceof Twig_Cache_CacheInterface) {
|
||||||
|
$this->cache = $cache;
|
||||||
|
} elseif (is_string($cache)) {
|
||||||
|
$this->cache = new Twig_Cache_FilesystemCache($cache);
|
||||||
|
} else {
|
||||||
|
$this->cache = new Twig_Cache_MemoryCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -238,17 +246,11 @@ class Twig_Environment
|
|||||||
*
|
*
|
||||||
* @param string $name The template name
|
* @param string $name The template name
|
||||||
*
|
*
|
||||||
* @return string|false The cache file name or false when caching is disabled
|
* @return string The cache file name
|
||||||
*/
|
*/
|
||||||
public function getCacheFilename($name)
|
public function getCacheFilename($name)
|
||||||
{
|
{
|
||||||
if (false === $this->cache) {
|
return $this->cache->getCacheKey($this->getTemplateClass($name), $this->templateClassPrefix);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix));
|
|
||||||
|
|
||||||
return $this->getCache().'/'.$class[0].'/'.$class[1].'/'.$class.'.php';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,15 +328,15 @@ class Twig_Environment
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!class_exists($cls, false)) {
|
if (!class_exists($cls, false)) {
|
||||||
if (false === $cache = $this->getCacheFilename($name)) {
|
$key = $this->cache->getCacheKey($cls, $this->templateClassPrefix);
|
||||||
eval('?>'.$this->compileSource($this->getLoader()->getSource($name), $name));
|
|
||||||
} else {
|
|
||||||
if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) {
|
|
||||||
$this->writeCacheFile($cache, $this->compileSource($this->getLoader()->getSource($name), $name));
|
|
||||||
}
|
|
||||||
|
|
||||||
require_once $cache;
|
if (!$this->cache->has($key) ||
|
||||||
|
($this->isAutoReload() && !$this->isTemplateFresh($name, $this->cache->getTimestamp($key)))
|
||||||
|
) {
|
||||||
|
$this->cache->write($key, $this->compileSource($this->getLoader()->getSource($name), $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->cache->load($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->runtimeInitialized) {
|
if (!$this->runtimeInitialized) {
|
||||||
@@ -456,15 +458,7 @@ class Twig_Environment
|
|||||||
*/
|
*/
|
||||||
public function clearCacheFiles()
|
public function clearCacheFiles()
|
||||||
{
|
{
|
||||||
if (false === $this->cache) {
|
$this->cache->clear();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->cache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
|
|
||||||
if ($file->isFile()) {
|
|
||||||
@unlink($file->getPathname());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1277,28 +1271,4 @@ class Twig_Environment
|
|||||||
$this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
|
$this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function writeCacheFile($file, $content)
|
|
||||||
{
|
|
||||||
$dir = dirname($file);
|
|
||||||
if (!is_dir($dir)) {
|
|
||||||
if (false === @mkdir($dir, 0777, true)) {
|
|
||||||
clearstatcache(false, $dir);
|
|
||||||
if (!is_dir($dir)) {
|
|
||||||
throw new RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif (!is_writable($dir)) {
|
|
||||||
throw new RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir));
|
|
||||||
}
|
|
||||||
|
|
||||||
$tmpFile = tempnam($dir, basename($file));
|
|
||||||
if ((false !== @file_put_contents($tmpFile, $content)) && @rename($tmpFile, $file)) {
|
|
||||||
@chmod($file, 0666 & ~umask());
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException(sprintf('Failed to write cache file "%s".', $file));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user