View source
<?php
define('FILECACHE_CID_FILENAME_MAX', 200);
define('FILECACHE_CID_FILENAME_POS_BEFORE_MD5', FILECACHE_CID_FILENAME_MAX - 34);
define('FILECACHE_CACHE_BIN_REGISTRY', 'filecache_bins');
define('FILECACHE_QUICKSTART_DIRECTORY', '.ht.filecache.quickstart');
define('FILECACHE_DIRECTORY_MODE', 0700);
define('FILECACHE_FILE_MODE', 0600);
function filecache_default_directory($quickstart = TRUE) {
$dir = FILECACHE_QUICKSTART_DIRECTORY;
if (!$quickstart) {
$dir = str_replace('.quickstart', '', $dir);
}
return DRUPAL_ROOT . '/' . conf_path() . '/files/' . $dir;
}
function filecache_directory($bin = FALSE) {
$filecache_directory = variable_get('filecache_directory', FALSE);
if (is_array($filecache_directory)) {
if (is_string($bin) && array_key_exists($bin, $filecache_directory)) {
$filecache_directory = $filecache_directory[$bin];
}
else {
if (array_key_exists('default', $filecache_directory)) {
$filecache_directory = $filecache_directory['default'];
}
}
}
if (!is_string($filecache_directory)) {
$filecache_directory = filecache_default_directory();
}
return $filecache_directory;
}
function filecache_is_readdeleteonly() {
return substr(php_sapi_name(), 0, 3) == "cli" || extension_loaded('posix') && posix_getuid() == 0;
}
function get_cache_prefix($bin = FALSE) {
$prefix = str_replace('/', '_', conf_path()) . '_';
global $conf;
if (array_key_exists('cache_prefix', $conf)) {
$cache_prefix_conf = $conf['cache_prefix'];
if ($cache_prefix_conf === FALSE || is_string($cache_prefix_conf)) {
$prefix = $cache_prefix_conf;
}
elseif (is_array($cache_prefix_conf)) {
if (is_string($bin) && array_key_exists($bin, $cache_prefix_conf)) {
$prefix = $cache_prefix_conf[$bin];
}
else {
if (array_key_exists('default', $cache_prefix_conf)) {
$prefix = $cache_prefix_conf['default'];
}
}
}
}
if (is_string($prefix)) {
return $prefix;
}
else {
return "";
}
}
function filecache_registry_pathname() {
return filecache_directory() . '/' . get_cache_prefix() . FILECACHE_CACHE_BIN_REGISTRY;
}
class DrupalFileCache implements DrupalCacheInterface {
function __construct($bin) {
$this->bin = $bin;
$this->directory = filecache_directory($bin) . '/' . get_cache_prefix($bin) . $bin;
$this->ok = $this
->check_filecache_directory();
$this
->truncate_if_needed();
}
private function check_filecache_directory() {
$t = get_t();
$critical_message = FALSE;
if (!is_dir($this->directory)) {
if (!file_exists($this->directory)) {
if (filecache_is_readdeleteonly()) {
watchdog('filecache', 'Need to create directory %dir but not running in webserver and permissions of created directory would become wrong', array(
'%dir' => $this->directory,
), WATCHDOG_NOTICE);
return FALSE;
}
if (!mkdir($this->directory, FILECACHE_DIRECTORY_MODE, TRUE)) {
$critical_message = $t('%dir does not exist and cannot be created. Please check directory permissions.', array(
'%dir' => $this->directory,
));
}
}
else {
$critical_message = $t('%dir is not a directory.', array(
'%dir' => $this->directory,
));
}
}
elseif (!is_writable($this->directory)) {
$critical_message = $t('PHP cannot write to directory %dir.', array(
'%dir' => $this->directory,
));
}
if ($critical_message) {
watchdog('filecache', $critical_message, array(), WATCHDOG_CRITICAL);
return FALSE;
}
return TRUE;
}
private function truncate_if_needed() {
$registry_pathname = filecache_registry_pathname();
$registry = @unserialize(@file_get_contents($registry_pathname));
if (!isset($registry) || !is_array($registry)) {
$registry = array();
}
$registry_changed = FALSE;
$do_truncate = FALSE;
if (!array_key_exists($this->bin, $registry)) {
$registry[$this->bin] = NULL;
$registry_changed = TRUE;
$do_truncate = TRUE;
}
else {
global $conf;
foreach ($registry as $filecache_bin => $placeholder) {
$cache_class_setting = 'cache_class_' . $filecache_bin;
if (array_key_exists($cache_class_setting, $conf) && $conf[$cache_class_setting] != 'DrupalFileCache') {
unset($registry[$filecache_bin]);
$registry_changed = TRUE;
}
}
}
if ($registry_changed) {
if ($this->ok && !filecache_is_readdeleteonly()) {
file_put_contents($registry_pathname, serialize($registry));
}
}
if ($do_truncate) {
$db_cache = new DrupalDatabaseCache($this->bin);
$db_cache
->clear('*', TRUE);
$this
->delete_wildcard('');
}
}
function encode_cid($cid) {
$safe_cid = str_replace(array(
'%3A',
'%2F',
), array(
'@',
'=',
), urlencode($cid));
if (strlen($safe_cid) > FILECACHE_CID_FILENAME_MAX) {
$safe_cid = substr($safe_cid, 0, FILECACHE_CID_FILENAME_POS_BEFORE_MD5) . ',' . md5(substr($safe_cid, FILECACHE_CID_FILENAME_POS_BEFORE_MD5));
}
return $safe_cid;
}
function get($cid) {
if (!$this->ok || !is_string($cid)) {
return FALSE;
}
$filename = $this->directory . '/' . $this
->encode_cid($cid);
$this
->delete_flushed();
$content = @file_get_contents($filename);
if ($content === FALSE) {
return FALSE;
}
$cache = @unserialize($content);
if ($cache === FALSE) {
$fh = fopen($filename, 'rb');
if ($fh === FALSE) {
return FALSE;
}
if (flock($fh, LOCK_SH) === FALSE) {
fclose($fh);
return FALSE;
}
$cache = @unserialize(@stream_get_contents($fh));
if ($cache === FALSE || flock($fh, LOCK_UN) === FALSE || fclose($fh) === FALSE) {
unlink($filename);
flock($fh, LOCK_UN);
fclose($fh);
return FALSE;
}
}
$cache_flush = variable_get('filecache_flush_' . $this->bin, 0);
if ($cache->expire != CACHE_TEMPORARY && $cache->expire != CACHE_PERMANENT && ($cache->expire < REQUEST_TIME || $cache_flush && $cache->created < $cache_flush)) {
unlink($filename);
return FALSE;
}
return $cache;
}
function getMultiple(&$cids) {
$results = array();
foreach ($cids as $cid) {
$cache = $this
->get($cid);
if ($cache !== FALSE) {
$results[$cid] = $cache;
}
}
foreach (array_keys($results) as $cid) {
if (($key = array_search($cid, $cids)) !== false) {
unset($cids[$key]);
}
}
return $results;
}
function set($cid, $data, $expire = CACHE_PERMANENT) {
if (!is_string($cid)) {
return;
}
$filename = $this->directory . '/' . $this
->encode_cid($cid);
if (!$this->ok || filecache_is_readdeleteonly()) {
@unlink($filename);
return;
}
$fh = @fopen($filename, 'r+b');
if ($fh === FALSE) {
$fh = fopen($filename, 'c+b');
if ($fh !== FALSE) {
if (!chmod($filename, FILECACHE_FILE_MODE)) {
watchdog('filecache', 'Cannot chmod %filename', array(
'%filename' => $filename,
), WATCHDOG_CRITICAL);
return;
}
}
else {
watchdog('filecache', 'Cannot open %filename', array(
'%filename' => $filename,
), WATCHDOG_CRITICAL);
return;
}
}
if (flock($fh, LOCK_EX) === FALSE) {
fclose($fh);
return;
}
$cache = new StdClass();
$cache->cid = $cid;
$cache->created = REQUEST_TIME;
$cache->expire = $expire;
$cache->data = $data;
if (ftruncate($fh, 0) === FALSE || fwrite($fh, serialize($cache)) === FALSE || flock($fh, LOCK_UN) === FALSE || fclose($fh) === FALSE) {
unlink($filename);
flock($fh, LOCK_UN);
fclose($fh);
return;
}
}
function clear($cid = NULL, $wildcard = FALSE) {
global $user;
if (!$this->ok) {
return;
}
if (empty($cid)) {
if (variable_get('cache_lifetime', 0)) {
$user->cache = REQUEST_TIME;
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
if ($cache_flush == 0) {
variable_set('cache_flush_' . $this->bin, REQUEST_TIME);
}
elseif (REQUEST_TIME > $cache_flush + variable_get('cache_lifetime', 0)) {
$this
->delete_expired();
variable_set('cache_flush_' . $this->bin, 0);
}
}
else {
$this
->delete_expired();
}
}
else {
if ($wildcard) {
if ($cid == '*') {
$this
->delete_wildcard('');
}
else {
$this
->delete_wildcard($cid);
}
}
elseif (is_array($cid)) {
foreach ($cid as $one_cid) {
$this
->delete_one($one_cid);
}
}
else {
$this
->delete_one($cid);
}
}
}
protected function delete_one($cid) {
$filename = $this->directory . '/' . $this
->encode_cid($cid);
@unlink($filename);
}
protected function all($cid_prefix = '') {
$list = array();
$filename_prefix = $this
->encode_cid($cid_prefix);
$filename_prefix_len = strlen($filename_prefix);
if (is_dir($this->directory)) {
$cwd = getcwd();
chdir($this->directory);
$dh = opendir('.');
while (($filename = readdir($dh)) !== FALSE) {
if (strncmp($filename, $filename_prefix, $filename_prefix_len) === 0) {
$list[] = $filename;
}
}
closedir($dh);
chdir($cwd);
}
return $list;
}
protected function delete_wildcard($cid_prefix) {
foreach ($this
->all($cid_prefix) as $filename) {
@unlink($this->directory . '/' . $filename);
}
}
function delete_expired($cache_flush = NULL) {
if (!isset($cache_flush)) {
$cache_flush = REQUEST_TIME;
}
$cache_size = 0;
foreach ($this
->all() as $filename) {
$pathname = $this->directory . '/' . $filename;
$stat = @stat($pathname);
if ($stat === FALSE || is_dir($pathname)) {
continue;
}
$file_size = $stat['blocks'] >= 0 ? $stat['blocks'] * 512 : $stat['size'];
$cache_size += $file_size;
$content = @file_get_contents($pathname);
if ($content === FALSE) {
continue;
}
$cache = @unserialize($content);
if ($cache === FALSE) {
continue;
}
if ($cache->expire == CACHE_PERMANENT) {
continue;
}
$expiry_date = $cache->expire;
if ($cache->expire == CACHE_TEMPORARY) {
$expiry_date = $cache->created + variable_get('cache_lifetime', 0);
}
if ($expiry_date < $cache_flush) {
@unlink($pathname);
$cache_size -= $file_size;
}
}
return $cache_size;
}
protected function delete_flushed() {
static $recursion = FALSE;
if ($recursion) {
return;
}
$recursion = TRUE;
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
if ($cache_flush && $cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME) {
variable_set('cache_flush_' . $this->bin, 0);
$this
->delete_expired($cache_flush);
}
$recursion = FALSE;
}
function isEmpty() {
if (!$this->ok) {
return FALSE;
}
return count($this
->all()) === 0;
}
}