You are not logged in.
A short video showing how you can test and implement Zend_Cache on a class that makes a really slow request (like a web service call).
This is part 4 in a four part series on Google Docs and Google maps. While this example shows how to cache a Class to a file, you could easily modify the code to work with other caching backends such as a memory-based caching engine or something like Zend Optimizer or APC.
Previous Parts
Show Synopsis
0:00 - What is caching
4:15 - preparing our bootstrap (for later on)
6:25 - Reviewing the class we want to unit test
10:25 - writing our first iteration of the Zend_Cache
13:22 - front options and back options
15:00 - looking at what Zend_Cache is caching
18:25 - Moving caching into the bootstrap
Offline
Offline
Hi jon ,
Thanks for this video for me zend_cache is very handy tool and am using it as standalone class
its really handy , but what i'd like to know ..... what about simplexml caching
the problem and the solution
Thank you very much ![]()
Offline
here's my frontend:
[code=php]
<?php
/**
* load() and save() methods for Frontend_Class have been overwridden to support XML serialization using
* App_XmlSerializeAdapter methods
*
*/
class App_XmlCache extends Zend_Cache_Frontend_Class
{
/**
* Test if a cache is available for the given id and (if yes) return it (false else)
*
* @param string $id Cache id
* @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
* @param boolean $doNotUnserialize Do not serialize (even if automatic_serialization is true) => for internal use
* @return mixed|false Cached datas
*/
public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false)
{
if (!$this->_options['caching']) {
return false;
}
$id = $this->_id($id); // cache id may need prefix
$this->_lastId = $id;
self::_validateIdOrTag($id);
$data = $this->_backend->load($id, $doNotTestCacheValidity);
if ($data===false) {
// no cache available
return false;
}
if ((!$doNotUnserialize) && $this->_options['automatic_serialization']) {
// we need to unserialize before sending the result
$data = array('',App_XmlSerializeAdapter::UnserializeSimpleXml($data));
}
return $data;
}
public function save($data, $id = null, $tags = array(), $specificLifetime = false, $priority = 8)
{
if (!$this->_options['caching']) {
return true;
}
if ($id === null) {
$id = $this->_lastId;
} else {
$id = $this->_id($id);
}
self::_validateIdOrTag($id);
self::_validateTagsArray($tags);
if ($this->_options['automatic_serialization']) {
// we need to serialize datas before storing them
$data = App_XmlSerializeAdapter::SerializeSimpleXml($data);
} else {
if (!is_string($data)) {
Zend_Cache::throwException("Datas must be string or set automatic_serialization = true");
}
}
// automatic cleaning
if ($this->_options['automatic_cleaning_factor'] > 0) {
$rand = rand(1, $this->_options['automatic_cleaning_factor']);
if ($rand==1) {
if ($this->_extendedBackend) {
// New way
if ($this->_backendCapabilities['automatic_cleaning']) {
$this->clean(Zend_Cache::CLEANING_MODE_OLD);
} else {
$this->_log('Zend_Cache_Core::save() / automatic cleaning is not available/necessary with this backend');
}
} else {
// Deprecated way (will be removed in next major version)
if (method_exists($this->_backend, 'isAutomaticCleaningAvailable') && ($this->_backend->isAutomaticCleaningAvailable())) {
$this->clean(Zend_Cache::CLEANING_MODE_OLD);
} else {
$this->_log('Zend_Cache_Core::save() / automatic cleaning is not available/necessary with this backend');
}
}
}
}
if ($this->_options['ignore_user_abort']) {
$abort = ignore_user_abort(true);
}
if (($this->_extendedBackend) && ($this->_backendCapabilities['priority'])) {
$result = $this->_backend->save($data, $id, $tags, $specificLifetime, $priority);
} else {
$result = $this->_backend->save($data, $id, $tags, $specificLifetime);
}
if ($this->_options['ignore_user_abort']) {
ignore_user_abort($abort);
}
if (!$result) {
// maybe the cache is corrupted, so we remove it !
if ($this->_options['logging']) {
$this->_log("Zend_Cache_Core::save() : impossible to save cache (id=$id)");
}
$this->remove($id);
return false;
}
if ($this->_options['write_control']) {
$data2 = $this->_backend->load($id, true);
if ($data!=$data2) {
$this->_log('Zend_Cache_Core::save() / write_control : written and read data do not match');
$this->_backend->remove($id);
return false;
}
}
return true;
}
}
[/code]
and the XmlSerializer class:
[code=php]
<?php
class App_XmlSerializeAdapter
{
public static function SerializeSimpleXml($toserialize)
{
if(is_array($toserialize))
{
if($toserialize[1] instanceof SimpleXMLElement)
{
$stdClass->type = get_class($toserialize[1]);
$stdClass->data = $toserialize[1]->asXml();
return serialize($stdClass);
}
}
return serialize($toserialize);
}
public static function UnserializeSimpleXml($tounserialize)
{
$tounserialize = unserialize($tounserialize);
if (is_array($tounserialize))
{
return $tounserialize[1];
}
if($tounserialize instanceof stdClass)
{
if($tounserialize->type == "SimpleXMLElement")
$tounserialize = simplexml_load_string($tounserialize->data);
}
return $tounserialize;
}
}
[/code]
Offline
Hi Jon,
It is marvelous vedios! Thank you so much for your great work!
But the vedio 'Using Zend_Cache to speed up Web Service calls' is broken, I tried several times and from different Url, it just stop in the half way.
Could you replace it with a full vedio or a zip download link?
King Regards,
Gordon
I just downloaded it then played on Windows Media Player, seems working now.
Thanks.
Gordon
Данный пост очень неформален и информативен, спасибо Вам!
Данный пост очень неформален и информативен, спасибо Вам!
Neon Says "This post is very informal and informative, thank you! "
thanks Neon
and thanks for google translate also
Offline
Hey Jon,
thanks so much for the Screencast. For me, as a new member to the zend framework community, it was very useful and handy to understand the caching functionality.
Also like to say, that all the other videos are great too!
Cheers,
Aleks
Offline
Hi Jon,
Very nice tutorials. Anyway, I did get int some problems when I tried the formt you gave in the config for the frontendOptions:
[code=php]
$frontendOptions = array(
'lifetime' => 7200,
'debug_header' => false,
'regexps' => array(
'^/$' => array('cache' => true),
'^/urlexpander/' => array('cache' => false),
'^/urltrimmer/' => array('cache' => false),
'^/currencyconverter/' => array('cache' => false),
'^/contact/' => array('cache' => true),
)
);
[/code]
Everything else get's cached even if I placed the value to false thereby making dynamic pages not working properly. Anyways, got it working by just removing the forward slashes.
[code=php]
$frontendOptions = array(
'lifetime' => 7200,
'debug_header' => false,
'regexps' => array(
'^/$' => array('cache' => true),
'^/urlexpander' => array('cache' => false),
'^/urltrimmer' => array('cache' => false),
'^/currencyconverter' => array('cache' => false),
'^/contact' => array('cache' => true),
)
);
[/code]
Regards,
burn
Last edited by clybs (2009-11-02 03:37:17)
Offline
I was wondering how exactly you specify the custom Zend_Cache_Frontend_Class when you create the Zend_Cache object?
Something like,
$cache = Zend_Cache::factory('XmlCache', 'File', $frontOptions, $backOptions);
does not work...
Hi Jon,
Thank you for the extraordinary videos that have been not only a good source to learn but also an inspiration to us. I was reviewing the video you did on Caching and I have some difficulties in cleaning the cache that I haven't been able to clarify.
My example is simple. I am caching a class, with all its methods.
[code=php]
<?php
class Order {
public function foo($param1){
// stuff
}
public function bar($param2){
// stuff
}
}
$order = Zend_Cache::factory(
'Class',
'File',
array(
'cached_entity' => new Order()
),
array(
'cache_dir' => "/path/to/cache/"
)
);
[/code]
That class actually does some database calls, so whenever I will call a function it will either get me some data from the cache or make the call and create the cache from which it will serve data in the future. So at a certain moment I will have in the local cache a bunch of data from the calls made to the foo() and bar() methods.
So here is my question: Is there a way to actually perform a partially clean operation, and to clean only the data from the foo method?
I could realize that by calling $order->clean() I get a complete removal of the cache but is there a way to remove only the cache made by the one method [and param(s)] ? Or at least tag them in some way.
At some point in the application it becomes really useful to rebuild the cache done by only one method and leave the rest of the cache as it is. For example if the class retrieves all the images uploaded by an user by its user id, and the user uploads a new one, it is really helpful to reset the cache for that user and not for all the users.
Thank you,
George
Offline
hi georgeenciu ,
I suggest you to change form class caching to function caching
then use the tagging options
in this way you can clean your cache upon your needs
I hope this will help you
Offline
hi,
thanks for the reply. i actually got another idea while trying to implement the function cache you have sugested.
i don't know how best practice is this or how does it follow (or not) zend framework's directions but i've created a class that extends Zend_Cache_Frontend_Class and overrides the method __call() in order to add tags to each caching.
i add a tag for the method and one for the combinations of parameters. this way the tags get added automatically and i can reset either all the outputs from one method or from one particular combination of method-params.
i've also implemented a clean method.
all this worked very well and the code changes in the app were minor.
thank you
Offline
would you mind share that class with us if that doesn't bothering you ??
am really willing to see your class
Offline
sure, no problem... here is the code
[code=php]
class App_Cache extends Zend_Cache_Frontend_Class
{
private function makeUniqueTag($name, $params)
{
return md5($name . '__' . serialize($params));
}
public function __call($name, $parameters)
{
$this->setTagsArray(array($name, $this->makeUniqueTag($name, $parameters)));
return parent::__call($name, $parameters);
}
public function cleanByTags(array $tags)
{
return $this->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, $tags);
}
}
[/code]
this does the job preety well for methods with fewer scalar params(1 or 2), but i think it gets preety ugly for managing when coming to large numbers of params or arrays/objects
let me know if you see any improvements
thanks
Offline
Hey everyone, I have a big big problem and need someone's help for a solution plz?
I'm trying to use Zend_Cache 'Function' frontend driver to cache a class method. Basically I have created another method in the class
[code=php]
<?php
public function getPricesCached($currency, $amount, $itemCode)
{
$managerHelper = new Zend_Controller_Action_Helper_Cache();
$cache = $managerHelper->getManager()->getCache('price');
$pricesArray = $cache->call('getPrices', array($currency, $amount, $itemCode));
return $pricesArray;
}
public function getPrices($currency, $amount, $itemCode)
{
// Huge calculations go here [....]
return array('testCurrency' => $currency, 'testAmount' => $amount, 'testItem' => $itemCode);
}
?>
[/code]
Then I can instantiate and call $classInstance->getPricesCached();
The reason I am doing this is I would like to only cache getPrices() function as when I enter parameters it has to do a lot of calculations.
The advantage of having it calculated with $cache->call() is I can pass in the parameters and if they were to match it can pull the return value from cache rather than having to recalculate.
Any ideas? I've tried
[code=php]
<?php
$cache->call('getPrices', array($currency, $amount, $itemCode));
$cache->call('$this->getPrices', array($currency, $amount, $itemCode));
$cache->call('this->getPrices', array($currency, $amount, $itemCode));
?>
[/code]
and none work
I get the following errors. Something to do with it not finding a function, however I want to adapt this to find the method.
[code=php]
<?php
Warning: call_user_func_array() [function.call-user-func-array]: First argument is expected to be a valid callback, 'this->getPrices' was given in /usr/local/lib/Zend/Framework/1.10.2/library/Zend/Cache/Frontend/Function.php on line 102
?>
[/code]
Please help
?
Last edited by Ryan Johnston (2010-03-30 00:23:19)
Offline
try this
$cache->call(array($this, 'getPrices'), array($currency, $amount, $itemCode));Last edited by georgeenciu (2010-03-30 00:49:43)
Offline
Didn't work. It said "Message: Incorrect function name"
Atm I have this working (from the Controller) except the class itself also references $this->getPrices(); often and I have no idea how to have it cache that way. Hence why I thought the "Function" Frontend Driver was the way to go.
From Controller
[code=php]
$frontOptions = array(
'cached_entity' => My_Singleton_Class::getInstance(),
'cache_by_default' => FALSE,
'cached_methods' => array('getPrices')
);
$backOptions = array('cache_dir' => APPLICATION_PATH . '/../temp/cache/price');
$this->_cachedClass = Zend_Cache::factory('Class', 'File', $frontOptions, $backOptions);
// Working from Controller
$pricesArray = $this->_cachedClass->getPrices('USD', 50, 'mw2010');
[/code]
Call From Class itself
[code=php]
// Any Ideas??
// Not caching from within class itself.
$pricesArray = $this->getPrices('USD', 50, 'mw2010');
[/code]
Offline
Working solution if anyone needs it:
Basically duplicated Zend_Cache_Frontend_Function and modified it to convert call($name, [...] to an associative array of 'class' and 'name' then as george said passed array(class, name) to any instances of call_user_func_array(). Put this in my lib folder under my own prefixes etc.
Now I can just call Zend_Cache::_makeFrontend to make the frontend from the class and pass it to Zend_Cache::factory().
Last edited by Ryan Johnston (2010-03-30 03:11:26)
Offline
If there is a better solution, eg: using 'Class' Frontend Driver. Please let me know.
Bare in mind that controller->class calls work with Class Frontend Driver but any calls withing the class eg: $this->getPrice() aren't cached. No idea how to achieve this using 'Class' Frontend Driver.
Offline
I'm a little confused as to how this works when your class has database calls. I followed this procedure for one of my classes that included several functions with calls to the database.
There were no errors but i dont believe any caching in taking place especially since my tmp directory is empty.
Note that the paramerters for my database calls a very dynamic, so most calls are different. How would zend know when to cache and when not to. Either way as it stands right now nothing is being cached
. Can anyone help? Here is the code.
public function preDispatch()
{
/*$this->filterService*/ $filterServiceClass= new App_FilterService();
$frontOptions = array( 'cached_entity' => $filterServiceClass);
$backOptions = array('cachce_dir'=>APPLICATION_PATH.'/../tmp');
$this->filterService = Zend_Cache::factory('Class', 'File',$frontOptions, $backOptions);
$this->userService = new App_UserService();
Zend_Registry::get('log')->info( __METHOD__ . " filter service class should now be cached");
}then i use $this->filterService->somefunction() throughout the rest of the controller.
Offline