lib/Ajde/Cache.php
<?php
class Ajde_Cache extends Ajde_Object_Singleton
{
protected $_hashContext;
protected $_hashFinal;
protected $_lastModified = [];
protected $_contents;
protected $_enabled = true;
/**
* @staticvar Ajde_Cache $instance
*
* @return Ajde_Cache
*/
public static function getInstance()
{
static $instance;
return $instance === null ? $instance = new self() : $instance;
}
protected function __construct()
{
$this->_enabled = config('layout.cache.enabled');
}
public function isEnabled()
{
return $this->_enabled;
}
public function enable()
{
$this->_enabled = true;
}
public function disable()
{
$this->_enabled = false;
}
public function getHashContext()
{
if (!isset($this->_hashContext)) {
$this->_hashContext = hash_init('md5');
}
return $this->_hashContext;
}
public function updateHash($data)
{
if (!isset($this->_hashFinal)) {
hash_update($this->getHashContext(), $data);
}
}
public function addFile($filename)
{
if (!isset($this->_hashFinal)) {
if (is_file(LOCAL_ROOT.$filename)) {
hash_update_file($this->getHashContext(), LOCAL_ROOT.$filename);
$this->addLastModified(filemtime(LOCAL_ROOT.$filename));
}
}
}
public function getHash()
{
if (!isset($this->_hashFinal)) {
$this->_hashFinal = hash_final($this->getHashContext());
}
return $this->_hashFinal;
}
public function addLastModified($timestamp)
{
$this->_lastModified[] = $timestamp;
}
public function getLastModified()
{
if (empty($this->_lastModified)) {
return time();
}
return max($this->_lastModified);
}
public function ETagMatch($serverETag = null)
{
if (empty($serverETag) && isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$serverETag = $_SERVER['HTTP_IF_NONE_MATCH'];
}
return $serverETag == $this->getHash();
}
public function modifiedSince()
{
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
return $this->getLastModified() > strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
}
return false;
}
public function setContents($contents)
{
$this->set('contents', $contents);
}
public function getContents()
{
return $this->get('contents');
}
public function saveResponse()
{
$response = Ajde::app()->getResponse();
$document = Ajde::app()->getDocument();
// Expires and Cache-Control
if ($document->getCacheControl() == Ajde_Document::CACHE_CONTROL_NOCACHE || $this->isEnabled() == false) {
$response->addHeader('Expires', gmdate('D, d M Y H:i:s \G\M\T', time()));
$response->addHeader('Cache-Control', Ajde_Document::CACHE_CONTROL_NOCACHE.', max-age=0');
} else {
$response->addHeader('Expires', gmdate('D, d M Y H:i:s \G\M\T', time() + $document->getMaxAge()));
$response->addHeader('Cache-Control', $document->getCacheControl().', max-age='.$document->getMaxAge());
}
// Content
if (($this->ETagMatch() && !$this->modifiedSince()) && $this->isEnabled()) {
$response->setResponseType(Ajde_Http_Response::RESPONSE_TYPE_NOT_MODIFIED);
$response->setData(false);
} else {
$response->addHeader('Last-Modified', gmdate('D, d M Y H:i:s', $this->getLastModified()).' GMT');
$response->addHeader('Etag', $this->getHash());
$response->addHeader('Content-Type', $document->getContentType());
$response->setData($this->hasContents() ? $this->getContents() : false);
}
}
/**
* Remember a cached value in the cache directory as a file.
*
* @param string $key
* @param callable $callback
* @param int $ttl in seconds, defaults to 3600 (one hour)
*
* @return mixed
*/
public static function remember($key, Closure $callback, $ttl = 3600)
{
$safeKey = substr(preg_replace('/[^a-zA-Z0-9_-]/', '-', $key), 0, 100);
$cacheFilename = CACHE_DIR.'STATIC_CACHE_'.$safeKey;
if (file_exists(LOCAL_ROOT.$cacheFilename) && filemtime(LOCAL_ROOT.$cacheFilename) > (time() - $ttl)) {
return json_decode(file_get_contents(LOCAL_ROOT.$cacheFilename));
} else {
$result = $callback->__invoke();
file_put_contents(LOCAL_ROOT.$cacheFilename, json_encode($result));
return $result;
}
}
}