Welcome Guest, Not a member yet? Register   Sign In
MP_Cache: Simple flexible Partial Caching
#1

[eluser]Jelmer[/eluser]
Update on April 16, 2010
I've moved MP_Cache to Bitbucket, following the example of EllisLab and because of the easy integration with NetBeans. Any issues can be reported there and the documentation is also there in the Wiki. And of course I invite anyone to contribute to it there.
The new URL: http://bitbucket.org/mijnpraktijk/mp_cache

Update on August 7, 2009
I've released the 2.0 (final) of MP_Cache, which comes after 2.0b5 (beta 5). There's a lot of important bugfixes in there. And I tested it with error reporting on E_ALL so this should be the first notice & warning free version. You can find it on
[[REMOVED: new URL in update above]], and I wrote a short list of bugfixes in post 17.

I chose not to include a call() function because I don't need it. Phil Sturgeon wrote a function that might be helpfull to many in post 16 of this thread.

Original content, very much outdated:

A quick warning: the new expiration functionality only works when caching arrays. I'm writing an improved second version of MP_Cache which won't have this problem and will support checking other caches to verify if it's current ("dependencies").

I want to implement caching but neither solution provided with CodeIgniter does it for me:
- DB caching uses a controller+function format which doesn't work for me as most of the site is generated by 1 function of 1 controller and the input to that function decides what models/libraries to load and to display (which view files). (most of the url requests are in the format http://mydomain.com/page_name.htm and routed & remapped) Also I can't delete on a query basis, only on a page basis which means that for changes to my navigation menu I have to delete the entire cache...
- Output caching caches too much, since the navigation is loaded dynamicly and I would have to erase my entire cache for every little edit.

So I thought of another way which probably isn't either new or very special, but I was wondering if anyone has any thoughts on it... Why it's good and why it's not.

It works by putting the part of the output you want to cache in an array and serializing that array into a custom file that would be accessable from every page/controller/model/etc...

An example of the code I'm thinking of:
Code:
$this->load->library('MP_cache');

$example = $this->mp_cache->get('example');
if ($example === false)
{
    // parts of code that generate your $example array, like for example below
    $example = $this->my_model->get_pages_from_db();
    
    $this->mp_cache->write($example, 'example');
}
Where the get() function either returns the cached values unserialized to the variable or returns false if there's no cache. If it returns false the following code would be executed and the values would be cached afterwards (with the write() function).

Other functions would probably include delete_all() for deleting all the cache (or all cache files within a certain directory), and delete() for deleting specific caches. The above example would need the cache to be deleted when an update to the db occurs, so in keeping with the example I'd add the following code to my update function within "my_model":
Code:
$this->mp_cache->delete('example');

Any thoughts on this? Maybe it's incredibly redundant but I haven't found any such functionality and it would solve problems like:
- Compared to DB caching: Reusable. Some parts (like your site menu's) is the same on every page and probably doesnt need to be cached for each individual page (even highlighting the current page should't be a problem with this solution).
- Compared to DB caching: You only have to delete it once after a change was made to any part of it and not all the cache for every page that uses the changed information.
- Compared to output cache: Not caching everything isn't a problem.
- Different cache for different uses of a function aren't a problem as you can add a variable in the cache "name"

The downsides are:
- It's harder to keep track of your cache: when to delete and when to reuse. But that's what the library is all about: it doesn't have a build-in mechanism for managing cache which means more freedom to use it as you please.

I've just started writing the library, and I'm going to "steal" havily from the native CI cache functions - I'll share whatever comes from this. But in the meantime I'd love to hear some other points of view.
#2

[eluser]Jelmer[/eluser]
Code and explanation moved to the wiki:
http://codeigniter.com/wiki/MP_Cache:_Si...s_of_code/

[EDIT: 16/04/2010]
A newer and improved version is available at http://bitbucket.org/mijnpraktijk/mp_cache
#3

[eluser]Phil Sturgeon[/eluser]
Good man! After reading about Memcache and realizing it wasn't in the standard distobution of PHP I put this on my list of libraries to write. Now I don't have too, sweet!

Not so sure about the name though, seems a bit long.
#4

[eluser]Jelmer[/eluser]
If you think about it, it doesn't really do that much: it allows you to save, retrieve & delete arrays to files. But on top of that you have built your own cache management. In my application I've effectively mirrored parts my DB with my cache: tables are subdirectories & the filenames are the numeric ID's.

On the name: It's called MP_Cache in the wiki instead of partial_caching, and you could call it whatever you want of course by changing the class name & constructor function name. I've edited the examples from the first post to reflect the namechange from when it was still a concept in my head to the current working & tested code.
#5

[eluser]Jelmer[/eluser]
After at first deciding not to add expiration to its features I've changed my mind and added it. This added 2 optional variables to the existing functions:

$this->mp_cache->write($values, $filename, $expires)
The 3rd parameter sets in seconds the amount of time after which the cache expires and should be refreshed. When get() retrieves an expired cache it deletes the cache and outputs false.
The $expires parameter defaults to 0 in which case the cache doesn't expire.

$this->mp_cache->get($filename, $use_expires)
The get() function will handle an expired cache as explained above, but when $use_expires is set to false this call won't delete the cache and return the cache as if it hadn't expired.
The $use_expires parameter defaults to true in which case the cache will expire when it was set through the write() function.

The new version is in the Wiki and is fully backwards compatible with the old library.

A quick warning: the new expiration functionality only works when caching arrays. I'm writing an improved second version of MP_Cache which won't have this problem and will support checking other caches to verify if it's current ("dependencies"). - the new version is available at http://bitbucket.org/mijnpraktijk/mp_cache
#6

[eluser]Carlos G[/eluser]
Hi Jelmer, it's a great library this one!!! Really helpful and was a miss in the core of Codeigniter, great job!

I've test it and found this little issue, with this new functionality of "expiration" (that was a clever idea by the way of add the expiration feature in this library):

in the get method in line 53 in the condition:
Code:
if ($use_expires && $cache['mp_cache_expires_time'] > 0 && $cache['mp_cache_expires_time'] < time() )

i did have to move the $use_expires variable at the beginning of the condition (it was at the end) because when you don't use expiration an error of "index not defined" for 'mp_cache_expires_time' is raised. So just reordering the conditions works fantastic!.

Congratulations!!
#7

[eluser]Jelmer[/eluser]
That's a good point. I must admit I never really program out the notices, but I don't think this is the complete. It requires you to set the second part of the get() function always when you don't expire your cache if you don't want notice errors, and I prefer it not to require.

Wouldn't the following be a better and even logically better solution? I think the statement $cache['mp_cache_expires_time'] > 0 should be isset($cache['mp_cache_expires_time']).

Which would make it the full line:
Code:
if ($use_expires && isset($this->contents['mp_cache_expires_time']) && $this->contents['mp_cache_expires_time'] < time())

A sidenote: I've written the second version which I haven't posted yet because it hasn't been fully tested. You can find it at http://mpsimple.mijnpraktijk.com/mp_cache.htm.

EDIT: You were right though, putting the $use_expires variable first was also logically better.
#8

[eluser]Phil Sturgeon[/eluser]
Or use !empty to cover isset and > 0.

I have made a small addition to your library to allow a default cache time other than 0. I feel it would be a pain for some developers to always set this an many would probably end up scattering their code with magic numbers.

To avoid this, I added this line to the constructor:

Code:
$this->default_expiry = $this->ci->config->item('mp_cache_default_expiry');

Then made the following changes.

Code:
function write($values, $filename, $expires = NULL)
    {
        $cache_path = $this->path;
        
        if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
        {
            return;
        }
        
        if($expires === NULL)
        {
            $expires = $this->default_expiry !== FALSE ? $this->default_expiry : 0;
        }

Simple change and very helpful for me at least. It will allow developers to add in a config item to set a default expiry time, but if they do not add one it will default to 0 as it currently does.

Good library though, implementing it into PyroCMS as we speak.
#9

[eluser]Jelmer[/eluser]
Hey Phil,

Thanks for the suggestion, I've incorporated it into the new version. Still need to find the time to test everything properly... Sad
#10

[eluser]Phil Sturgeon[/eluser]
I found another bug introduced in the last release. You were not removing your metadata.

Code:
// Remove expiry information to keep array stucture intact
        unset($cache['mp_cache_expires_time']);

I added that into line 61 just before the return of the get file. This was breaking any array itterations as it thought this was part of the data!

Edit: I have been using this library a bit over the last two days and spotted the data type bug you mentioned. The reason that only arrays work is that you are modifying the variable given to you to add your extra data and assuming it is an array.

You take a parameter of mixed data type in the function declaration, then modify it as though it is an array. Then you serialize the lot. When it comes out of the .cache file and is unserialized you take the whole content of the file and the extra data is still there which breaks other things (that bug was mentioned above).

To solve this one, you can ignore my last bugfix about unsetting your extra data. Create your own array of content for the .cache file and store the passed mixed variable as one item within the array.

I have almost got this done but my lunch-time ran out. Will post you a fix if I get there first.




Theme © iAndrew 2016 - Forum software by © MyBB