Welcome Guest, Not a member yet? Register   Sign In
CachedController v1.0 - cache BY OBJECT->METHOD. any method, not all the page by URI
#1

[eluser]sdbruder[/eluser]
I was toying with a simple HMVC scheme, using real controlers in libraries subdir (it works!), but stumbled in the following dilema: the output cache is by URI, not by controller, its not designed to be multi-controller aware.

SO.. Ive made my own Cacheable Controller. It uses the same cache infra-structure, and it caches BY METHOD.

Im using what php calls 'overloading' (its not classic overloading, but fit my need here) to create cached versions of any method, so if I want to cache $mycontroller->index() I call $mycontroller->indexCached();

Works for any method.

Autoload it, extend your Controller from it and that's it. The same ideia can be easily adapted in other places as well, not only controllers.

PS: Not tested under PHP4.

First, an obvious example:

Ive made a simple menu MVC, one entry in the menu for each file under /PAGES/ subdir (if the page is static, the content of the static page is there).
For each page the menu controller scandir('/pages/') {ob_start(); include() it, ob_end_clean() to drop the content if its a static page, etcetera }

Wonderfully simple to operate after, but 'costly' to do it every page hit..

So...

Code:
class Menu extends CachedController {

    function Menu() {
        parent::CachedController();
        $this->methodcache(60);
    }

    function listfiles($dir) {
        // costly function
    }

    function index($dir = '/') {
        $data['menu'] = $this->listfilesCached($dir); // here is the cache magic tnkx to __call()
        $data['current'] = $_SERVER['REQUEST_URI'];
        $ret = $this->load->view('menu_view',$data,TRUE);
        return $ret;
    }
}

Now the code per se:

Code:
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

/**
* Cached Controller, an Cached version of CI Controller
*
* @package>>   CodeIgniter
* @author> >   Sergio Devojno Bruder
*/

class CachedController extends Controller {

    var $methodcache_expiration = 0;

    function CachedController() {
        parent::Controller();
    }

    function methodcache($time) {
        $this->methodcache_expiration = ( ! is_numeric($time)) ? 0 : $time;
    }


    function _cache_write($id,&$output) {

        if ( $this->methodcache_expiration <= 0 ) {
            return FALSE;
        }

        $cache_path = $this->config->item('cache_path');
        $cache_path = ($cache_path == '') ? BASEPATH.'cache/' : $cache_path;

        if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path)) {
            return FALSE;
        }

        // Build the file path.  The file name is an MD5 hash of the $id array
        $idstr = '';
        foreach($id as $e) {
            $idstr .= $e.'::';
        }

        $filepath = $cache_path.md5($idstr);
        if ( ! $fp = @fopen($filepath, 'wb')) {
            log_message('error', "Unable to write cache file: ".$filepath);
            return;
        }

        $expire = time() + ($this->methodcache_expiration * 60);
        flock($fp, LOCK_EX);
        fwrite($fp, $expire.'TS---&gt;'.serialize($output));
        flock($fp, LOCK_UN);
        fclose($fp);
        @chmod($filepath, 0777);

        log_message('debug', "Cache file written: ".$filepath);
        return TRUE;
    }

    function _cache($id,&$value) {
        $cache_path = $this->config->item('cache_path');
        $cache_path = ($cache_path == '') ? BASEPATH.'cache/' : $cache_path;

        if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path)) {
            return FALSE;
        }
        // Build the file path.  The file name is an MD5 hash of the $id array
        $idstr = '';
        foreach($id as $e) {
            $idstr .= $e.'::';
        }

        $filepath = $cache_path.md5($idstr);
        if ( ! @file_exists($filepath)) {
            return FALSE;
        }
        if ( ! $fp = @fopen($filepath, 'rb')) {
            return FALSE;
        }
        flock($fp, LOCK_SH);
        $cache = '';
        if (filesize($filepath) > 0) {
            $cache = fread($fp, filesize($filepath));
        }
        flock($fp, LOCK_UN);
        fclose($fp);

        // Strip out the embedded timestamp>>  
        if ( ! preg_match("/(\d+TS---&gt;)/", $cache, $match)) {
            return FALSE;
        }

        // Has the file expired? If so we'll delete it.
        if (time() >= trim(str_replace('TS---&gt;', '', $match['1']))) {
            @unlink($filepath);
            log_message('debug', "Cache file has expired. File deleted");
            return FALSE;
        }

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


    function __call ($member, $arguments) {
        $fn_id = array($member,$arguments);
        if (substr($member,-6) == 'Cached') {
            $method_name = substr($member,0,-6);
            if (method_exists($this,$method_name)) {
                if ($this->methodcache_expiration > 0) {
                    if (!$this->_cache($fn_id, &$ret)) {
                        $ret = call_user_method_array($method_name, &$this, $arguments);
                        $this->_cache_write($fn_id, $ret);
                    }
                } else {
                    $ret = call_user_method_array($method_name, &$this, $arguments);
                }
                return $ret;
            } else {
                throw new Exception("Call to undefined method ".get_class($this)."::{$method_name}()");
            }
        } else {
            throw new Exception("Call to undefined method ".get_class($this)."::{$method_name}()");
        }
    }

}

?&gt;
#2

[eluser]sdbruder[/eluser]
In the current state of this code, its more usable for HMVC folks, IMHO.

To be usable & handly for a larger audience, we need to implement it in the load object, in the model, etc.

I'll try latter.

UPDATE: Ive made a V1.1, now with controller and model cached versions, if anyone is interested I'll upload it

UPDATE2: And all of this without system hacking, only extending Controller and Model in your application.
#3

[eluser]Maks Baum[/eluser]
Good job Smile
I was thinking about writing some helper to cache some data, but thank God I used 'search form'
Now I only have to extend MY_Controller yours and I'll have everything what I want to have Smile
#4

[eluser]Maks Baum[/eluser]
[quote author="sdbruder" date="1209736550"]
UPDATE: Ive made a V1.1, now with controller and model cached versions, if anyone is interested I'll upload it
[/quote]
so.... where Can I grab it? Wink
#5

[eluser]sdbruder[/eluser]
I was busy in the last days finishing my shiny new nerd toy, a fanless mini-itx linux server (1GHz VIA C7 1GB 750GB) to be the time machine target to my macs..

I will upload it tomorrow after some polish, I promise!
#6

[eluser]sdbruder[/eluser]
See: http://ellislab.com/forums/viewthread/81213/

or download it directly: http://sergio.bruder.com.br/cachedobjects/




Theme © iAndrew 2016 - Forum software by © MyBB