Schneimi’s Dev Weblog


CakePHP Base64 encoded caching file size

Posted in CakePHP by schneimi on February 2, 2013
Tags: , , ,

I stumbled upon the problem that some cached files in CakePHP took alot more space than expected, and I read about a similar problem on StackOverflow

Well, I took a deeper look into this and it looks like the serialized data in viewVars is used for the nocache parts of the view. So the solution should be to place the part where the base64 data is output between nocache tags.


<!--nocache--><?php echo $base64Data; ?><!--/nocache-->

But doing this, I had the effect, that on some views it worked, on others it didn’t. A closer look into CakePHP revealed, that preg_match_all() is used to look for the nocache parts, which cannot handle a very large amount of data (may depend on server settings) and only finds nocache parts up to a certain amount of data. It doesn’t even throw an error, wich I checked with preg_last_error(). I read about rising pcre memory limits, but it didn’t work for me, so I had to find another solution for that problem.

In my case image data was retrieved from database and placed base64 encoded within the view in an image src tag (src=”data:image/png;base64,…”). My solution was to replace the data in the image src tag with a link to an extra action that delivers just the image data and caches it seperately. This also has the advantage, that the image can now be cached by browsers apart from the view.

The first thing I did, was to care about the image data not coming with the find, in order not to get serialized in the viewVars. But I still needed the information about if there actually was an image in the data, which brought me to setup a virtual field in my model. The virtual field does the following, if there is image data present in the field it holds 1 (TRUE), otherwise 0 (FALSE).


app/Model/YourModel.php

public $virtualFields = array(
    'image' => 'IF(image IS NOT NULL, TRUE, FALSE)'
);

http://book.cakephp.org/2.0/en/models/virtual-fields.html

For the image to retrieve and use, I setup an extra action without view in my controller and cached the data manually. Unfortunately you also have to care about deleting it when your model gets deleted or updated, but you can easily do that in the model save and delete callbacks.

http://book.cakephp.org/2.0/en/models/callback-methods.html


app/Controller/YourController.php

public function image($id) {
  $this->autoRender = false;
  $this->cacheAction = false;

  // read data from cache
  $data = Cache::read('image_'.$id);

  if (empty(data)) {
    $data = $this->YourModel->find('first', array(
        'conditions' => array(
            'YourModel.id' => $id
        ),
        'fields' => array(
            'image_type'
            'image_data'
        )
    ));

    // write data into cache
    Cache::write('image_'.$id, $data);
  }

  // send cache header for browser caching
  header('Content-Type: '.$data['YourModel']['image_type']);
  header('Cache-Control: public, max-age=28800');

  // output the raw image data
  echo $data['YourModel']['image_data];
}

In the view the image src looks like this:

<img src="/your_controller/image/<? echo $id; ?>" />

Another idea would be to generally unset the viewVars if nocache parts aren’t used in a view. But I don’t know where to hook in for that.

Advertisements