imagecms/ImageCMS

View on GitHub
application/libraries/cache.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

if (!defined('BASEPATH')) {
    exit('No direct script access allowed');
}

/**
 * Image CMS
 * Cache Class
 */
class Cache
{

    public $CI;

    public $get = 0;

    public $set = 0;

    public $disableCache = 0;

    //Cache config
    // TODO: Rewrite auto_clean to fetch date from DB
    public $_Config = [
                       'store'           => 'cache',
                       'auto_clean'      => 500, //Random number to run _Clean();
                       'auto_clean_life' => 3600,
                       'auto_clean_all'  => TRUE,
                       'ttl'             => 3600,
                      ]; //one hour

    public function __construct() {
        $this->CI = & get_instance();
        if ($this->CI->config->item('cache_path') != '') {
            $this->_Config['default_store'] = $this->CI->config->item('cache_path');
            $this->_Config['store'] = $this->CI->config->item('cache_path');
        } else {
            $this->_Config['default_store'] = BASEPATH . 'cache/';
            $this->_Config['store'] = BASEPATH . 'cache/';
        }
        $this->disableCache = (boolean) $this->CI->config->item('disable_cache');

        // Is cache folder wratible?
        if (!is_writable($this->_Config['store'])) {
            $this->log_cache_error('Constructor :: Store ' . $this->_Config['store'] . ' is not writable');
        }

        // autoclean if random is 1
        if (($this->_Config['auto_clean'] !== false) && (rand(1, $this->_Config['auto_clean']) === 1)) {
            $this->Clean();
        }
    }

    /**
     * Fetch Cached File
     *
     * @param string $key
     *
     * @return mixed
     */
    public function fetch($key, $group = FALSE) {
        if ($this->disableCache === true) {
            return false;
        }

        $this->set_group($group);

        if (($ret = $this->_fetch($key)) === false) {
            return false;
        } else {
            return $ret;
        }
    }

    /**
     * @param string $key
     */
    private function _fetch($key) {
        $file = $this->_Config['store'] . 'cache_' . $this->generatekey($key);
        $this->set_default_group();

        if (!file_exists($file)) {
            return FALSE;
        }

        if (!($fp = fopen($file, 'r'))) {
            $this->log_cache_error('Fetch :: Error Opening File ' . $file);
            return FALSE;
        }

        // Only reading
        flock($fp, LOCK_SH);

        // Cache data
        $data = unserialize(file_get_contents($file));
        fclose($fp);

        // if cache not expried return cache file
        if (time() < $data['expire']) {
            $this->get++;
            return $data['cache'];
        } else {
            return FALSE;
        }
    }

    /**
     * Fetch cached function
     */
    public function fetch_func($object, $func, $args = []) {

        $file = $this->_Config['store'] . 'cache_' . $this->generatekey(get_class($object) . '::' . $func . '::' . serialize($args));
        $this->set_default_group();

        if (!file_exists($file)) {
            return false;
        }

        if (!($fp = fopen($file, 'r'))) {
            $this->log_cache_error('Fetch :: Error Opening File ' . $file);
            return false;
        }

        flock($fp, LOCK_SH);

        $data = unserialize(file_get_contents($file));
        fclose($fp);

        if (time() < $data['expire']) {
            $this->get++;
            return $data['cache'];
        } else {
            return FALSE;
        }
    }

    /**
     * Store Cache Item
     *
     * @param string  $key
     * @param mixed   $data
     * @param int     $ttl
     *
     * @return bool
     */
    public function store($key, $data, $ttl = false, $group = false) {
        if (!$ttl) {
            $ttl = $this->_Config['ttl'];
        }

        $this->set_group($group);

        $file = $this->_Config['store'] . 'cache_' . $this->generatekey($key);
        $data = serialize(['expire' => ($ttl + time()), 'cache' => $data]);

        if (!($fp = fopen($file, 'a+'))) {
            $this->log_cache_error('Store :: Error Opening file ' . $file);
        }

        flock($fp, LOCK_EX);
        fseek($fp, 0);
        ftruncate($fp, 0);   // Clear file

        if (fwrite($fp, $data) === false) {
            $this->log_cache_error('Store :: Error writing to file ' . $file);
        }

        fclose($fp);

        $this->set_default_group();
        $this->set++;

        return true;
    }

    /**
     * Group Function
     *
     * @param string $group
     *
     * @access public
     */
    public function set_group($group) {
        if ($group == FALSE) {
            $this->_Config['store'] = $this->_Config['default_store'];
            return;
        }

        if (!is_dir($this->_Config['store'] . $group)) {
            mkdir($this->_Config['store'] . $group);
            @chmod($this->_Config['store'] . $group, 0777);
        }
        $this->_Config['store'] .= $group . '/';
    }

    public function set_default_group() {
        $this->_Config['store'] = $this->_Config['default_store'];
    }

    /**
     * Cache Function
     *
     * @return mixed
     * @access public
     */
    public function call($func = [], $args = [], $ttl = false) {
        if ($ttl == false) {
            $ttl = $this->_Config['ttl'];
        }

        $arguments = func_get_args();

        //class_name::function
        $key = get_class($arguments[0][0]) . '::' . $arguments[0][1] . '::' . serialize($args);

        if (($cache = $this->fetch($key)) !== false) {
            $this->set_default_group();
            return $cache;
        } else {

            $target = array_shift($arguments);
            $result = call_user_func_array($target, $args);

            $this->set_default_group();

            if (!$this->store($key, $result, false)) {
                return false;
            }

            return $result;
        }
    }

    public function Clean() {
        if (!($dh = opendir($this->_Config['store']))) {
            $this->log_cache_error('Clean :: Error Opening Store ' . $this->_Config['store']);
            return false;
        }

        $this->log_cache_error('Clean :: Autoclean started');

        $n = 0;

        while ($file = readdir($dh)) {
            $stat = stat($this->_Config['store']);
            if (($file != '.') && ($file != '..') && ($file != 'index.html')) {

                if (substr($file, 0, 6) != 'cache_' && $file != 'hooks.php' && $file != '.' && $file != '..' && $file != '/' && strstr($file, '.') != TRUE && (time() - $stat['mtime']) > $this->_Config['auto_clean_life']) {

                    $files_all = opendir('./system/cache/' . $file);
                    while (false !== ($fileT = readdir($files_all))) {
                        $stat = stat($this->_Config['store']);
                        // echo $stat['mtime'];
                        if ($fileT != '.' && $fileT != '..' && $fileT != '/' && (time() - @$stat['mtime']) > $this->_Config['auto_clean_life']) {
                            @unlink('./system/cache/' . $file . '/' . $fileT);
                            $n++;
                        }
                    }
                }

                if (strstr($file, '.') === TRUE && (time() - $stat['mtime']) > $this->_Config['auto_clean_life']) {
                    @unlink($file);
                    $n++;
                }
            }
        }

        $this->log_cache_error('Clean :: Autoclean done');

        return $n;
    }

    /**
     * Delete Cache Item
     *
     * @param string $key
     *
     * @return bool
     */
    public function delete($key, $group = FALSE) {
        $this->set_group($group);

        $file = $this->_Config['store'] . 'cache_' . $this->generatekey($key);

        $this->set_default_group();

        if (file_exists($file)) {
            return @unlink($file);
        } else {
            return false;
        }
    }

    /**
     * Delete group folder
     *
     * @param string $group
     * @access public
     */
    public function delete_group($group) {
        if ($group != '.' AND $group != '..' AND $group != 'templates_c') {
            $file = BASEPATH . 'cache/' . $group;
            $this->CI->load->helper('file');
            delete_files($file);
        }
    }

    /**
     * Delete Cached Function
     *
     * @param $object
     * @param $func
     * @param array $args
     * @return bool
     */
    public function delete_func($object, $func, $args = []) {
        $file = $this->_Config['store'] . 'cache_' . $this->generatekey(get_class($object) . '::' . $func . '::' . serialize($args));
        $this->set_default_group();

        if (file_exists($file)) {
            return @unlink($file);
        } else {
            return false;
        }
    }

    /**
     * Delete All Cache Items
     *
     * @return integer
     * @access public
     */
    public function delete_all() {
        $n = 0;

        $cache_store_dir = $this->_Config['store'] . '/';
        if (is_dir($cache_store_dir) and ( $root_dir_handle = opendir($cache_store_dir))) {
            while (false !== ($file = readdir($root_dir_handle))) {

                if (substr($file, 0, 6) != 'cache_' && $file != 'hooks.php' && $file != '.' && $file != '..' && $file != '/') {
                    $cache_sub_dir = $cache_store_dir . $file . '/';
                    if (is_dir($cache_sub_dir) and ( $sub_dir_handle = opendir($cache_sub_dir))) {
                        while (FALSE !== ($fileT = readdir($sub_dir_handle))) {

                            if ($fileT != '.' && $fileT != '..' && $fileT != '/') {

                                $n++;
                                @unlink($cache_sub_dir . $fileT);
                            }
                        }
                    }
                }
                if (substr($file, 0, 6) == 'cache_' || $file == 'hooks.php' || strstr($file, '.') === TRUE) {

                    $n++;
                    @unlink($cache_store_dir . $file);
                }
            }
        }

        $cache_sub_dir = $cache_store_dir . 'templates_c/HTML/';
        if (is_dir($cache_sub_dir) and ( $sub_dir_handle = opendir($cache_sub_dir))) {
            while (false !== ($fileT = readdir($sub_dir_handle))) {

                if ($fileT != '.' && $fileT != '..' && $fileT != '/') {

                    @unlink($cache_sub_dir . $fileT);
                }
            }
        }

        $this->log_cache_error('All cache files deleted');

        return $n;
    }

    public function clearCacheFolder($folder = NULL) {

        if ($folder !== NULL) {
            if ($files_all = opendir('./system/cache/' . $folder . '/')) {
                while (false !== ($fileT = readdir($files_all))) {

                    if ($fileT != '.' && $fileT != '..' && $fileT != '/' && substr($fileT, 0, 6) == 'cache_' || $fileT != 'hooks.php') {
                        @unlink('./system/cache/' . $folder . '/' . $fileT);
                    }
                }
            } else {
                log_message('error', 'Library cache, Function clearCacheFolder , opendir Patch:' . './system/cache/' . $folder . '/' . ' RETURN FALSE');
                return false;
            }
        } else {
            log_message('error', 'Library cache, Function clearCacheFolder. Do not get the name of the directory to delete cache');
            return false;
        }
    }

    public function cache_file() {
        $n = 0;

        $cache_store_dir = $this->_Config['store'] . '/';
        if (is_dir($cache_store_dir) and ( $root_dir_handle = opendir($cache_store_dir))) {
            while (false !== ($file = readdir($root_dir_handle))) {

                if (substr($file, 0, 6) != 'cache_' && $file != 'hooks.php' && $file != '.' && $file != '..' && $file != '/') {
                    $cache_sub_dir = $cache_store_dir . $file . '/';
                    if (is_dir($cache_sub_dir) and ( $sub_dir_handle = opendir($cache_sub_dir))) {
                        while (false !== ($fileT = readdir($sub_dir_handle))) {

                            if ($fileT != '.' && $fileT != '..' && $fileT != '/' && strstr($fileT, '~') != TRUE) {
                                $n++;
                            }
                        }
                    }
                }
                if (substr($file, 0, 6) == 'cache_' || $file == 'hooks.php' && strstr($fileT, '~') != TRUE) {
                    $n++;
                }
            }
        }

        $this->log_cache_error('All cache files deleted');

        return $n;
    }

    /**
     * @param string $key
     * @return string
     */
    public function generatekey($key) {
        return md5($key);
    }

    /**
     * @param string $msg
     * @return bool
     */
    private function log_cache_error($msg) {
        $log_path = APPPATH . 'logs/';

        $filepath = $log_path . 'cache_log-' . date('Y-m-d') . EXT;
        $message = '';

        if (!file_exists($filepath)) {
            $message .= '<' . "?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed'); ?" . ">\n\n";
        }

        if (!$fp = @fopen($filepath, FOPEN_WRITE_CREATE)) {
            return FALSE;
        }

        $message .= date('Y-m-d H:i:s') . ' --> ' . $msg . "\n";

        flock($fp, LOCK_EX);
        fwrite($fp, $message);
        flock($fp, LOCK_UN);
        fclose($fp);

        @chmod($filepath, FILE_WRITE_MODE);
    }

}

/* End of cache.php */