Zendcasts Forum

A community of developers who work with the Zend Framework and other enterprise PHP technologies

You are not logged in.

#1 2009-09-15 02:58:20

Jon Lebensold
Administrator
Registered: 2009-06-27
Posts: 279

ZC34 - using Zend_Cache to speed up Web Service calls

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

#2 2009-09-15 04:01:49

Jon Lebensold
Administrator
Registered: 2009-06-27
Posts: 279

Re: ZC34 - using Zend_Cache to speed up Web Service calls

Offline

#3 2009-09-16 10:46:21

tawfekov
Administrator
From: www
Registered: 2009-08-06
Posts: 25
Website

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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 smile

Offline

#4 2009-09-16 15:47:35

Jon Lebensold
Administrator
Registered: 2009-06-27
Posts: 279

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#5 2009-09-22 01:00:08

Gordon
Guest

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#6 2009-09-22 01:14:16

Gordon
Guest

Re: ZC34 - using Zend_Cache to speed up Web Service calls

I just downloaded it then played on Windows Media Player, seems working now.

Thanks.

Gordon

#7 2009-09-29 16:46:11

Neon
Guest

Re: ZC34 - using Zend_Cache to speed up Web Service calls

Данный пост очень неформален и информативен, спасибо Вам!

#8 2009-09-29 16:55:39

tawfekov
Administrator
From: www
Registered: 2009-08-06
Posts: 25
Website

Re: ZC34 - using Zend_Cache to speed up Web Service calls

Neon wrote:

Данный пост очень неформален и информативен, спасибо Вам!

Neon Says "This post is very informal and informative, thank you! "
thanks Neon
and thanks for google translate also

Offline

#9 2009-10-17 17:11:47

Aleks
New member
Registered: 2009-10-17
Posts: 1

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#10 2009-11-02 03:36:52

clybs
New member
From: Singapore
Registered: 2009-11-02
Posts: 1
Website

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#11 2009-11-21 22:08:43

John F
Guest

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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...

#12 2010-02-18 06:32:57

georgeenciu
New member
From: Bucharest, Romania
Registered: 2010-02-18
Posts: 7
Website

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#13 2010-02-18 08:01:26

tawfekov
Administrator
From: www
Registered: 2009-08-06
Posts: 25
Website

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#14 2010-02-18 11:06:43

georgeenciu
New member
From: Bucharest, Romania
Registered: 2010-02-18
Posts: 7
Website

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#15 2010-02-18 11:10:57

tawfekov
Administrator
From: www
Registered: 2009-08-06
Posts: 25
Website

Re: ZC34 - using Zend_Cache to speed up Web Service calls

would you mind share that class with us if that doesn't bothering you  ??
am really willing to see your class

Offline

#16 2010-02-18 11:17:48

georgeenciu
New member
From: Bucharest, Romania
Registered: 2010-02-18
Posts: 7
Website

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#17 2010-03-30 00:21:06

Ryan Johnston
Member
Registered: 2010-03-08
Posts: 21

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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 sad 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 big_smile?

Last edited by Ryan Johnston (2010-03-30 00:23:19)

Offline

#18 2010-03-30 00:49:17

georgeenciu
New member
From: Bucharest, Romania
Registered: 2010-02-18
Posts: 7
Website

Re: ZC34 - using Zend_Cache to speed up Web Service calls

try this

$cache->call(array($this, 'getPrices'), array($currency, $amount, $itemCode));

Last edited by georgeenciu (2010-03-30 00:49:43)

Offline

#19 2010-03-30 00:57:55

Ryan Johnston
Member
Registered: 2010-03-08
Posts: 21

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#20 2010-03-30 03:10:40

Ryan Johnston
Member
Registered: 2010-03-08
Posts: 21

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#21 2010-03-30 03:21:39

Ryan Johnston
Member
Registered: 2010-03-08
Posts: 21

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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

#22 2010-05-27 22:26:59

shaded2
New member
Registered: 2010-02-16
Posts: 9

Re: ZC34 - using Zend_Cache to speed up Web Service calls

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 sad . 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

Board footer

Powered by FluxBB