dedijk4/vendor/gregwar/cache/Gregwar/Cache/Cache.php

366 lines
9.7 KiB
PHP
Executable File

<?php
namespace Gregwar\Cache;
/**
* A cache system based on files
*
* @author Gregwar <g.passault@gmail.com>
*/
class Cache implements CacheInterface
{
/**
* Cache directory
*/
protected $cacheDirectory;
/**
* Use a different directory as actual cache
* @var string
*/
protected $actualCacheDirectory = null;
/**
* Prefix directories size
*
* For instance, if the file is helloworld.txt and the prefix size is
* 5, the cache file will be: h/e/l/l/o/helloworld.txt
*
* This is useful to avoid reaching a too large number of files into the
* cache system directories
* @var int
*/
protected $prefixSize = 5;
/**
* Directory mode
*
* Allows setting of the access mode for the directories created.
* @var int
*/
protected $directoryMode = 0755;
/**
* Constructs the cache system
*
* @param string $cacheDirectory the cache directory
*/
public function __construct($cacheDirectory = 'cache')
{
$this->cacheDirectory = $cacheDirectory;
}
/**
* Sets the cache directory
*
* @param string $cacheDirectory the cache directory
* @return self
*/
public function setCacheDirectory($cacheDirectory)
{
$this->cacheDirectory = $cacheDirectory;
return $this;
}
/**
* Gets the cache directory
*
* @return string the cache directory
*/
public function getCacheDirectory()
{
return $this->cacheDirectory;
}
/**
* Sets the actual cache directory
*
* @param string $actualCacheDirectory the actual cache directory
* @return self
*/
public function setActualCacheDirectory($actualCacheDirectory = null)
{
$this->actualCacheDirectory = $actualCacheDirectory;
return $this;
}
/**
* Returns the actual cache directory
*/
public function getActualCacheDirectory()
{
return $this->actualCacheDirectory ?: $this->cacheDirectory;
}
/**
* Change the prefix size
*
* @param int $prefixSize the size of the prefix directories
* @return self
*/
public function setPrefixSize($prefixSize)
{
$this->prefixSize = $prefixSize;
return $this;
}
/**
* Change the directory mode
*
* @param int $directoryMode the directory mode to use
* @return self
*/
public function setDirectoryMode($directoryMode)
{
if (!$directoryMode) {
$directoryMode = 0755;
}
$this->directoryMode = $directoryMode;
return $this;
}
/**
* Creates a directory
*
* @param string $directory the target directory
*/
protected function mkdir($directory)
{
if (!is_dir($directory)) {
@mkdir($directory, $this->directoryMode, true);
}
}
/**
* Gets the cache file name
*
* @param string $filename the name of the cache file
* @param bool $actual get the actual file or the public file
* @param bool $mkdir a boolean to enable/disable the construction of the
* cache file directory
* @return string
*/
public function getCacheFile($filename, $actual = false, $mkdir = false)
{
$path = array();
// Getting the length of the filename before the extension
$parts = explode('.', $filename);
$len = strlen($parts[0]);
for ($i=0; $i<min($len, $this->prefixSize); $i++) {
$path[] = $filename[$i];
}
$path = implode('/', $path);
if ($mkdir) {
$actualDir = $this->getActualCacheDirectory() . '/' . $path;
$this->mkdir($actualDir);
}
$path .= '/' . $filename;
if ($actual) {
return $this->getActualCacheDirectory() . '/' . $path;
} else {
return $this->getCacheDirectory() . '/' . $path;
}
}
/**
* Checks that the cache conditions are respected
*
* @param string $cacheFile the cache file
* @param array $conditions an array of conditions to check
* @return bool
* @throws \Exception
*/
protected function checkConditions($cacheFile, array $conditions = array())
{
// Implicit condition: the cache file should exist
if (!file_exists($cacheFile)) {
return false;
}
foreach ($conditions as $type => $value) {
switch ($type) {
case 'maxage':
case 'max-age':
// Return false if the file is older than $value
$age = time() - filemtime($cacheFile);
if ($age > $value) {
return false;
}
break;
case 'younger-than':
case 'youngerthan':
// Return false if the file is older than the file $value, or the files $value
$check = function($filename) use ($cacheFile) {
return !file_exists($filename) || filemtime($cacheFile) < filemtime($filename);
};
if (!is_array($value)) {
if (!$this->isRemote($value) && $check($value)) {
return false;
}
} else {
foreach ($value as $file) {
if (!$this->isRemote($file) && $check($file)) {
return false;
}
}
}
break;
default:
throw new \Exception('Cache condition '.$type.' not supported');
}
}
return true;
}
/**
* Checks if the target filename exists in the cache and if the conditions
* are respected
*
* @param string $filename the filename
* @param array $conditions the conditions to respect
* @return bool
*/
public function exists($filename, array $conditions = array())
{
$cacheFile = $this->getCacheFile($filename, true);
return $this->checkConditions($cacheFile, $conditions);
}
/**
* Alias for exists
*
* @param string $filename the filename
* @param array $conditions the conditions to respect
* @return bool
*/
public function check($filename, array $conditions = array())
{
return $this->exists($filename, $conditions);
}
/**
* Write data in the cache
*
* @param string $filename the name of the cache file
* @param string $contents the contents to store
* @return self
*/
public function set($filename, $contents = '')
{
$cacheFile = $this->getCacheFile($filename, true, true);
file_put_contents($cacheFile, $contents, \LOCK_EX);
return $this;
}
/**
* Alias for set()
*
* @param string $filename the name of the cache file
* @param string $contents the contents to store
* @return self
*/
public function write($filename, $contents = '')
{
return $this->set($filename, $contents);
}
/**
* Get data from the cache
*
* @param string $filename the cache file name
* @param array $conditions
* @return null|string
*/
public function get($filename, array $conditions = array())
{
if ($this->exists($filename, $conditions)) {
return file_get_contents($this->getCacheFile($filename, true));
} else {
return null;
}
}
/**
* Is this URL remote?
*
* @param string $file
* @return bool
*/
protected function isRemote($file)
{
if (preg_match('/^([a-z]+):\/\//', $file, $match)) {
return ($match[1] != 'file');
}
return false;
}
/**
* Get or create the cache entry
*
* @param string $filename the cache file name
* @param array $conditions an array of conditions about expiration
* @param \Closure $function the closure to call if the file does not exist
* @param bool $file returns the cache file or the file contents
* @param bool $actual returns the actual cache file
* @return string
* @throws \InvalidArgumentException
*/
public function getOrCreate($filename, array $conditions = array(), $function, $file = false, $actual = false)
{
if (!is_callable($function)) {
throw new \InvalidArgumentException('The argument $function should be callable');
}
$cacheFile = $this->getCacheFile($filename, true, true);
$data = null;
if (!$this->check($filename, $conditions)) {
if(file_exists($cacheFile)) {
unlink($cacheFile);
}
$data = call_user_func($function, $cacheFile);
// Test if the closure wrote the file or if it returned the data
if (!file_exists($cacheFile)) {
$this->set($filename, $data);
} else {
$data = file_get_contents($cacheFile);
}
}
return $file ? $this->getCacheFile($filename, $actual) : file_get_contents($cacheFile);
}
/**
* Alias to getOrCreate with $file = true
*
* @param string $filename the cache file name
* @param array $conditions an array of conditions about expiration
* @param \Closure $function the closure to call if the file does not exist
* @param bool $actual returns the actual cache file
* @return string
* @throws \InvalidArgumentException
*/
public function getOrCreateFile($filename, array $conditions = array(), $function, $actual = false)
{
return $this->getOrCreate($filename, $conditions, $function, true, $actual);
}
}