Welcome Guest, Not a member yet? Register   Sign In
a lazy cache solution - no change on your current code
#1

[eluser]vizofan[/eluser]
I'm really a lazy developer and i did not realise the importance of cache until my blog slowed down when rendering a page with a lot of images.

At this stage i do not want too much change on my code to put the cache in. I did some analysis and noticed that, in most of the case, we only need cache for particular partition of the page. For example, I want to cache the content of my blog post, but no cache on the log in status (image I logged in and someone else visited the page, they could read the cached part as well).

So I started to search for some cache library, or at least solution which could meet my
criteria:

1. Must be easy to apply onto applications without cache
2. Capability to cache only part of a page (rather than what the default $CI->output is doing)
3. Ability to clear particular cache when needed
4. work in background, not change the way of codeigniter works

I did not find any solution doing exactly i wanted. Some need quite a lot of change to check if cached or not.

Then I did some experiment and comes to this particularly lazy solution!

I extended original Loader class, and overridden the method of view(). In this view() method, I check, save, retrieve the cache and organised the cache files same as $CI->output did. The only difference is, Output class works out the filename of cache based on your URI, my new Load class generated the file name based on URI, view path and parameters passed in.

Criteria 1 & 2 is done. Now I do not need to change my controllers to get my cache working. Great!

I also noticed new issue:
I have a list of recent blog posts which sits on everypage, now it is cached many times as the cache file differs in each URI

I need some way to indicate that this view is not related to URI. So I did a new method to pass an index name for the cache into view() method.

Here comes the code, as expected by programmers.
#2

[eluser]vizofan[/eluser]
Code:
<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* To the programmers did not read the words above
*
* You do not need to change the way you load your view, just place this file
* in system/application/libraries/MY_Loader.php
* and the cache will work
*
* If some parts sits cross whole site, displayed on every page,
* call $CI->load->indexed_cacheview()
*
* The codes to generate cache file and read from it are from Output class
*
* @author Neil Fan [email protected]
*/

class MY_Loader extends CI_Loader
{
    public $cacheview_expiration = 60;
    

    public function view($view, $vars = array(), $return = FALSE)
    {
        $CI =& get_instance();    
    
        $cache_index =    $CI->config->item('base_url').
                $CI->config->item('index_page').
                $CI->uri->uri_string().'#'.
                $view.'#'. var_export($vars, TRUE) ;
    
        return $this->indexed_cacheview(md5($cache_index), $view, $vars, $return);

    }

    public function indexed_cacheview($cache_index, $view, $vars = array(), $return = FALSE)
    {
        $CI =& get_instance();    
    
        $cache_path = $CI->config->item('cache_path');
        $cache_path = ($cache_path == '') ? BASEPATH.'cache/' : $cache_path;

        if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
        {
            return parent::view($view, $vars, $return);
        }

        $cache_path .= $cache_index ;
                
        $cache = $this->_get_cache($cache_path);

        if($cache)
        {
            if($return===TRUE)
            {
                return $cache;
            }
            $CI->output->append_output($cache);
        }
        else
        {
            $buffer = parent::view($view, $vars, TRUE);
            $this->_write_cache($cache_path, $buffer);

            // Return the file data if requested
            if ($return === TRUE)
            {        
                return $buffer;
            }

            $CI->output->append_output($buffer);
        }

    }

    // --------------------------------------------------------------------
    
    /**
     * Write a Cache File
     *
     * @access    public
     * @return    void
     */    
    function _write_cache($cache_path, $output)
    {
        if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
        {
            log_message('error', "Unable to write cache file: ".$cache_path);
            return;
        }
        
        $expire = time() + ($this->cacheview_expiration * 60);
        
        if (flock($fp, LOCK_EX))
        {
            fwrite($fp, $expire.'TS--->'.$output);
            flock($fp, LOCK_UN);
        }
        else
        {
            log_message('error', "Unable to secure a file lock for file at: ".$cache_path);
            return;
        }
        fclose($fp);
        @chmod($cache_path, DIR_WRITE_MODE);

        log_message('debug', "Cache file written: ".$cache_path);
    }

    // --------------------------------------------------------------------
    
    /**
     * Update/serve a cached file
     *
     * @access    public
     * @return    void
     */    
    function _get_cache($cache_path)
    {
        
        if ( ! @file_exists($cache_path))
        {
            return FALSE;
        }
    
        if ( ! $fp = @fopen($cache_path, FOPEN_READ))
        {
            return FALSE;
        }
            
        flock($fp, LOCK_SH);
        
        $cache = '';
        if (filesize($cache_path) > 0)
        {
            $cache = fread($fp, filesize($cache_path));
        }
    
        flock($fp, LOCK_UN);
        fclose($fp);
                    
        // Strip out the embedded timestamp        
        if ( ! preg_match("/(\d+TS--->)/", $cache, $match))
        {
            return FALSE;
        }
        
        // Has the file expired? If so we'll delete it.
        if (time() >= trim(str_replace('TS--->', '', $match['1'])))
        {        
            @unlink($cache_path);
            log_message('debug', "Cache file has expired. File deleted");
            return FALSE;
        }

        // Display the cache
        log_message('debug', "Cache file is current. Sending it to browser.");        
        return str_replace($match['0'], '', $cache);
    }




}
#3

[eluser]vizofan[/eluser]
And I could also package as a Cache library and fit into my blog site. Works pretty good.

Also give the ability to clear cache in this library.

Will share the Cache Library code soon.
#4

[eluser]Eric Barnes[/eluser]
You could also just use the included cache driver in 2.0.x - http://ellislab.com/codeigniter/user-gui...ching.html




Theme © iAndrew 2016 - Forum software by © MyBB