<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 4.3.2 or newer
 *
 * @package         CodeIgniter
 * @author          ExpressionEngine Dev Team
 *                  Heavily modified by Christian Koller untermStrich
 * @copyright       Copyright (c) 2008 - 2009, EllisLab, Inc.
 * @license         https://codeigniter.com/user_guide/license.html
 * @link            https://codeigniter.com
 * @since           Version 1.0
 * @filesource
 */


/**
 * Database Cache Class
 *
 * @category        Database
 * @author          ExpressionEngine Dev Team
 *                  Heavily modified by Christian Koller untermStrich
 * @link            https://codeigniter.com/user_guide/database/
 */
class CI_DB_Cache
{
    protected $CI;
    protected $db;        // allows passing of db object so that multiple database connections and returned db objects can be supported

    /**
     * Constructor
     *
     * Grabs the CI super object instance so we can access it.
     *
     * @param   object  &$db    Reference The DB object
     */
    public function __construct(&$db)
    {
        $this->CI = CI_Controller::get_instance();
        $this->db =& $db;
        $this->CI->load->helper('file');
    }


    /**
     * Set Cache Directory Path
     *
     * @param   string  $path   The path to the cache directory
     * @return  boolean
     */
    public function check_path($path='')
    {
        if ($path == '') {
            if ($this->db->cachedir == '') {
                return $this->db->cache_off();
            }

            $path = $this->db->cachedir;
        }

        $path = preg_replace('/(.+?)\/*$/', '\\1/',  $path);

        if (!is_dir($path) OR !is_really_writable($path)) {
            return $this->db->cache_off();
        }

        $this->db->cachedir = $path;
        return true;
    }


    /**
     * Retrieve a cached query
     *
     * The URI being requested will become the name of the cache sub-folder.
     * An MD5 hash of the SQL statement will become the cache file name
     *
     * @param   string  $sql    The SQL string
     * @return  string
     */
    public function read($sql)
    {
        if (!$this->check_path()) {
            return $this->db->cache_off();
        }

        $cache_area = $this->db->get_us_cache_area();
        $filepath = $this->db->cachedir.'database+'.$cache_area.'/'.md5($sql);

        if (!is_readable($filepath)) {
            return false;
        }

        $file_time = @filemtime($filepath); // returns FALSE if file does not exist
        $min_time = time()-$this->CI->config->item('cache_time');

        if ($file_time<$min_time) {
            $this->delete($cache_area);
            return false;
        }

        if (false === ($cachedata = read_file($filepath))) {
            return false;
        }

        return unserialize($cachedata);
    }


    /**
     * Write a query to a cache file
     *
     * @param   string  $sql    The SQL string
     * @param   object  $object The value object
     * @return  boolean
     */
    public function write($sql, $object)
    {
        if (!$this->check_path()) {
            return $this->db->cache_off();
        }

        $cache_area = $this->db->get_us_cache_area();
        $dir_path = $this->db->cachedir.'database+'.$cache_area.'/';

        $filename = md5($sql);

        if (!@is_dir($dir_path)) {
            if (!@mkdir($dir_path, DIR_WRITE_MODE)) {
                return false;
            }

            @chmod($dir_path, DIR_WRITE_MODE);
        }

        if (write_file($dir_path.$filename, serialize($object)) === false) {
            return false;
        }

        @chmod($dir_path.$filename, DIR_WRITE_MODE);
        return true;
    }


    /**
     * Delete cache files within a particular directory
     *
     * @param   string  $cache_area     The cache area to delete
     * @return  void
     */
    public function delete($cache_area)
    {
        $dir_path = $this->db->cachedir.'database+'.$cache_area.'/';
        
        delete_files($dir_path, true);
    }


    /**
     * Delete all existing cache files
     *
     * @return  void
     */
    public function delete_all()
    {
        $path = rtrim($this->db->cachedir, DIRECTORY_SEPARATOR);

        if (!$current_dir = @opendir($path)) {
            return;
        }

        while (false !== ($filename = @readdir($current_dir))) {
            if (strpos($filename, 'database+') === 0) {
                delete_files($path . DIRECTORY_SEPARATOR . $filename, true);
                @rmdir($path . DIRECTORY_SEPARATOR . $filename);
            }
            if (strpos($filename, 'widgets') === 0) {
                delete_files($path . DIRECTORY_SEPARATOR . $filename, true);
                @rmdir($path . DIRECTORY_SEPARATOR . $filename);
            }
            if ($this->_endsWith($filename, '.xml')) {
                @unlink($path . DIRECTORY_SEPARATOR . $filename);
            }
            if ($this->_endsWith($filename, '.json')) {
                @unlink($path . DIRECTORY_SEPARATOR . $filename);
            }
        }
        @closedir($current_dir);

        @unlink($path . DIRECTORY_SEPARATOR . 'database-translate.php');

        clearstatcache(true);
    }
    
    /**
     * Tests if this string ends with the specified suffix
     * 
     * @param   string      $str        String
     * @param   string      $suffix     The suffix
     * @return  boolean
     */
    private function _endsWith($str, $suffix)
    {
        return substr($str, -1*strlen($suffix)) == $suffix;
    }

} // END class CI_DB_Cache
