Development

/branches/1.4/lib/view/sfViewCacheManager.class.php

You must first sign up to be able to contribute.

root/branches/1.4/lib/view/sfViewCacheManager.class.php

Revision 32698, 32.0 kB (checked in by fabien, 3 years ago)

[1.4] fixed a unit test

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * Cache class to cache the HTML results for actions and templates.
13  *
14  * This class uses a sfCache instance implementation to store cache.
15  *
16  * To disable all caching, you can set the [sf_cache] constant to false.
17  *
18  * @package    symfony
19  * @subpackage view
20  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
21  * @version    SVN: $Id$
22  */
23 class sfViewCacheManager
24 {
25   protected
26     $cache       = null,
27     $cacheConfig = array(),
28     $context     = null,
29     $dispatcher  = null,
30     $controller  = null,
31     $routing     = null,
32     $request     = null,
33     $loaded      = array();
34
35   /**
36    * Class constructor.
37    *
38    * @see initialize()
39    */
40   public function __construct($context, sfCache $cache, $options = array())
41   {
42     $this->initialize($context, $cache, $options);
43   }
44
45   /**
46    * Initializes the cache manager.
47    *
48    * @param sfContext $context  Current application context
49    * @param sfCache   $cache    An sfCache instance
50    */
51   public function initialize($context, sfCache $cache, $options = array())
52   {
53     $this->context    = $context;
54     $this->dispatcher = $context->getEventDispatcher();
55     $this->controller = $context->getController();
56     $this->request    = $context->getRequest();
57     $this->options    = array_merge(array(
58         'cache_key_use_vary_headers' => true,
59         'cache_key_use_host_name'    => true,
60       ), $options);
61
62     if (sfConfig::get('sf_web_debug'))
63     {
64       $this->dispatcher->connect('view.cache.filter_content', array($this, 'decorateContentWithDebug'));
65     }
66
67     // empty configuration
68     $this->cacheConfig = array();
69
70     // cache instance
71     $this->cache = $cache;
72
73     // routing instance
74     $this->routing = $context->getRouting();
75   }
76
77   /**
78    * Retrieves the current cache context.
79    *
80    * @return sfContext The sfContext instance
81    */
82   public function getContext()
83   {
84     return $this->context;
85   }
86
87   /**
88    * Retrieves the current cache object.
89    *
90    * @return sfCache The current cache object
91    */
92   public function getCache()
93   {
94     return $this->cache;
95   }
96
97   /**
98    * Generates a unique cache key for an internal URI.
99    * This cache key can be used by any of the cache engines as a unique identifier to a cached resource
100    *
101    * Basically, the cache key generated for the following internal URI:
102    *   module/action?key1=value1&key2=value2
103    * Looks like:
104    *   /localhost/all/module/action/key1/value1/key2/value2
105    *
106    * @param  string $internalUri       The internal unified resource identifier
107    *                                   Accepts rules formatted like 'module/action?key1=value1&key2=value2'
108    *                                   Does not accept rules starting with a route name, except for '@sf_cache_partial'
109    * @param  string $hostName          The host name
110    *                                   Optional - defaults to the current host name bu default
111    * @param  string $vary              The vary headers, separated by |, or "all" for all vary headers
112    *                                   Defaults to 'all'
113    * @param  string $contextualPrefix  The contextual prefix for contextual partials.
114    *                                   Defaults to 'currentModule/currentAction/currentPAram1/currentvalue1'
115    *                                   Used only by the sfViewCacheManager::remove() method
116    *
117    * @return string The cache key
118    *                If some of the parameters contained wildcards (* or **), the generated key will also have wildcards
119    */
120   public function generateCacheKey($internalUri, $hostName = '', $vary = '', $contextualPrefix = '')
121   {
122     if ($callable = sfConfig::get('sf_cache_namespace_callable'))
123     {
124       if (!is_callable($callable))
125       {
126         throw new sfException(sprintf('"%s" cannot be called as a function.', var_export($callable, true)));
127       }
128
129       return call_user_func($callable, $internalUri, $hostName, $vary, $contextualPrefix, $this);
130     }
131
132     // note the special rules @sf_cache_partial and @sf_cache_fragment
133     if (strpos($internalUri, '@') === 0 && strpos($internalUri, '@sf_cache_partial') === false && strpos($internalUri, '@sf_cache_fragment') === false )
134     {
135       throw new sfException('A cache key cannot be generated for an internal URI using the @rule syntax');
136     }
137
138     $cacheKey = '';
139
140     if ($this->isContextual($internalUri))
141     {
142       // Contextual partial
143       if (!$contextualPrefix)
144       {
145         list($route_name, $params) = $this->controller->convertUrlStringToParameters($this->routing->getCurrentInternalUri());
146
147         // if there is no module/action, it means that we have a 404 and the user is trying to cache it
148         if (!isset($params['module']) || !isset($params['action']))
149         {
150           $params['module'] = sfConfig::get('sf_error_404_module');
151           $params['action'] = sfConfig::get('sf_error_404_action');
152         }
153         $cacheKey = $this->convertParametersToKey($params);
154       }
155       else
156       {
157         $cacheKey = $contextualPrefix;
158       }
159       list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
160       $cacheKey .= sprintf('/%s/%s/%s', $params['module'], $params['action'], isset($params['sf_cache_key']) ? $params['sf_cache_key'] : '');
161     }
162     else
163     {
164       // Regular action or non-contextual partial
165       list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
166       if ($route_name == 'sf_cache_partial')
167       {
168         $cacheKey = 'sf_cache_partial/';
169         $cacheKey .= $this->convertParametersToKey($params);
170       }
171       elseif( $route_name == 'sf_cache_fragment' )
172       {
173         $cacheKey = 'sf_cache_fragment/';
174         $cacheKey .= $this->convertParametersToFragmentKey($params);
175       }
176       else
177       {
178         $cacheKey .= $this->convertParametersToKey($params);
179       }
180     }
181
182     // add vary headers
183     if ($varyPart = $this->getCacheKeyVaryHeaderPart($internalUri, $vary))
184     {
185       $cacheKey = '/'.$varyPart.'/'.ltrim($cacheKey, '/');
186     }
187
188     // add hostname
189     if ($hostNamePart = $this->getCacheKeyHostNamePart($hostName))
190     {
191       $cacheKey = '/'.$hostNamePart.'/'.ltrim($cacheKey, '/');
192     }
193
194     // normalize to a leading slash
195     if (0 !== strpos($cacheKey, '/'))
196     {
197       $cacheKey = '/'.$cacheKey;
198     }
199
200     // distinguish multiple slashes
201     while (false !== strpos($cacheKey, '//'))
202     {
203       $cacheKey = str_replace('//', '/'.substr(sha1($cacheKey), 0, 7).'/', $cacheKey);
204     }
205
206     // prevent directory traversal
207     $cacheKey = strtr($cacheKey, array(
208       '/.'  => '/_.',
209       '/_'  => '/__',
210       '\\.' => '\\_.',
211       '\\_' => '\\__',
212     ));
213
214     return $cacheKey;
215   }
216
217   /**
218    * Gets the vary header part of view cache key.
219    *
220    * @param  string $vary
221    * @return string
222    */
223   protected function getCacheKeyVaryHeaderPart($internalUri, $vary = '')
224   {
225     if (!$this->options['cache_key_use_vary_headers'])
226     {
227       return '';
228     }
229
230     // prefix with vary headers
231     if (!$vary)
232     {
233       $varyHeaders = $this->getVary($internalUri);
234
235       if (!$varyHeaders)
236       {
237         return 'all';
238       }
239
240       sort($varyHeaders);
241       $request = $this->context->getRequest();
242       $varys = array();
243
244       foreach ($varyHeaders as $header)
245       {
246         $varys[] = $header . '-' . preg_replace('/\W+/', '_', $request->getHttpHeader($header));
247       }
248       $vary = implode($varys, '-');
249     }
250
251     return $vary;
252   }
253
254   /**
255    * Gets the hostname part of view cache key.
256    *
257    * @param string $hostName
258    * @return void
259    */
260   protected function getCacheKeyHostNamePart($hostName = '')
261   {
262     if (!$this->options['cache_key_use_host_name'])
263     {
264       return '';
265     }
266
267     if (!$hostName)
268     {
269       $hostName = $this->context->getRequest()->getHost();
270     }
271
272     $hostName = preg_replace('/[^a-z0-9\*]/i', '_', $hostName);
273     $hostName = preg_replace('/_+/', '_', $hostName);
274
275     return strtolower($hostName);
276   }
277
278   /**
279    * Transforms an associative array of parameters from an URI into a unique key
280    *
281    * @param  array $params  Associative array of parameters from the URI (including, at least, module and action)
282    *
283    * @return string Unique key
284    */
285   protected function convertParametersToKey($params)
286   {
287     if(!isset($params['module']) || !isset($params['action']))
288     {
289       throw new sfException('A cache key must contain both a module and an action parameter');
290     }
291     $module = $params['module'];
292     unset($params['module']);
293     $action = $params['action'];
294     unset($params['action']);
295     ksort($params);
296     $cacheKey = sprintf('%s/%s', $module, $action);
297     foreach ($params as $key => $value)
298     {
299       $cacheKey .= sprintf('/%s/%s', $key, $value);
300     }
301
302     return $cacheKey;
303   }
304
305   /**
306    * Transforms an associative array of parameters from an URI into a unique key
307    * This method is only used for fragment
308    *
309    * @param  array $params  Associative array of parameters from the URI (including, at least, module and action)
310    *
311    * @return string Unique key
312    */
313   protected function convertParametersToFragmentKey($params)
314   {
315     // module should be __sf_cache_fragment and action should be the
316     // fragment name, also used as sf_cache_key parameter
317     // these parameters can be removed from the generated kay
318     unset($params['module'] , $params['action']);
319
320     ksort($params);
321     foreach ($params as $key => $value)
322     {
323       $cacheKey .= sprintf('%s/%s/', $key, $value);
324     }
325
326     return rtrim( $cacheKey , '/' );
327   }
328
329   /**
330    * Adds a cache to the manager.
331    *
332    * @param string $moduleName  Module name
333    * @param string $actionName  Action name
334    * @param array  $options     Options for the cache
335    */
336   public function addCache($moduleName, $actionName, $options = array())
337   {
338     // normalize vary headers
339     if (isset($options['vary']))
340     {
341       foreach ($options['vary'] as $key => $name)
342       {
343         $options['vary'][$key] = strtr(strtolower($name), '_', '-');
344       }
345     }
346
347     $options['lifeTime'] = isset($options['lifeTime']) ? $options['lifeTime'] : 0;
348     if (!isset($this->cacheConfig[$moduleName]))
349     {
350       $this->cacheConfig[$moduleName] = array();
351     }
352     $this->cacheConfig[$moduleName][$actionName] = array(
353       'withLayout'     => isset($options['withLayout']) ? $options['withLayout'] : false,
354       'lifeTime'       => $options['lifeTime'],
355       'clientLifeTime' => isset($options['clientLifeTime']) ? $options['clientLifeTime'] : $options['lifeTime'],
356       'contextual'     => isset($options['contextual']) ? $options['contextual'] : false,
357       'vary'           => isset($options['vary']) ? $options['vary'] : array(),
358     );
359   }
360
361   /**
362    * Registers configuration options for the cache.
363    *
364    * @param string $moduleName  Module name
365    */
366   public function registerConfiguration($moduleName)
367   {
368     if (!isset($this->loaded[$moduleName]))
369     {
370       require($this->context->getConfigCache()->checkConfig('modules/'.$moduleName.'/config/cache.yml'));
371       $this->loaded[$moduleName] = true;
372     }
373   }
374
375   /**
376    * Retrieves the layout from the cache option list.
377    *
378    * @param  string $internalUri  Internal uniform resource identifier
379    *
380    * @return bool true, if have layout otherwise false
381    */
382   public function withLayout($internalUri)
383   {
384     return $this->getCacheConfig($internalUri, 'withLayout', false);
385   }
386
387   /**
388    * Retrieves lifetime from the cache option list.
389    *
390    * @param  string $internalUri  Internal uniform resource identifier
391    *
392    * @return int LifeTime
393    */
394   public function getLifeTime($internalUri)
395   {
396     return $this->getCacheConfig($internalUri, 'lifeTime', 0);
397   }
398
399   /**
400    * Retrieves client lifetime from the cache option list
401    *
402    * @param  string $internalUri  Internal uniform resource identifier
403    *
404    * @return int Client lifetime
405    */
406   public function getClientLifeTime($internalUri)
407   {
408     return $this->getCacheConfig($internalUri, 'clientLifeTime', 0);
409   }
410
411   /**
412    * Retrieves contextual option from the cache option list.
413    *
414    * @param  string $internalUri  Internal uniform resource identifier
415    *
416    * @return boolean true, if is contextual otherwise false
417    */
418   public function isContextual($internalUri)
419   {
420     return $this->getCacheConfig($internalUri, 'contextual', false);
421   }
422
423   /**
424    * Retrieves vary option from the cache option list.
425    *
426    * @param  string $internalUri  Internal uniform resource identifier
427    *
428    * @return array Vary options for the cache
429    */
430   public function getVary($internalUri)
431   {
432     return $this->getCacheConfig($internalUri, 'vary', array());
433   }
434
435   /**
436    * Gets a config option from the cache.
437    *
438    * @param string $internalUri   Internal uniform resource identifier
439    * @param string $key           Option name
440    * @param string $defaultValue  Default value of the option
441    *
442    * @return mixed Value of the option
443    */
444   protected function getCacheConfig($internalUri, $key, $defaultValue = null)
445   {
446     list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
447
448     if (!isset($params['module']))
449     {
450         return $defaultValue;
451     }
452
453     $this->registerConfiguration($params['module']);
454
455     $value = $defaultValue;
456     if (isset($this->cacheConfig[$params['module']][$params['action']][$key]))
457     {
458       $value = $this->cacheConfig[$params['module']][$params['action']][$key];
459     }
460     else if (isset($this->cacheConfig[$params['module']]['DEFAULT'][$key]))
461     {
462       $value = $this->cacheConfig[$params['module']]['DEFAULT'][$key];
463     }
464
465     return $value;
466   }
467
468   /**
469    * Returns true if the current content is cacheable.
470    *
471    * Possible break in backward compatibility: If the sf_lazy_cache_key
472    * setting is turned on in settings.yml, this method is not used when
473    * initially checking a partial's cacheability.
474    *
475    * @see sfPartialView, isActionCacheable()
476    *
477    * @param  string $internalUri  Internal uniform resource identifier
478    *
479    * @return bool true, if the content is cacheable otherwise false
480    */
481   public function isCacheable($internalUri)
482   {
483     if ($this->request instanceof sfWebRequest && !$this->request->isMethod(sfRequest::GET))
484     {
485       return false;
486     }
487
488     list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
489
490     if (!isset($params['module']))
491     {
492         return false;
493     }
494
495     $this->registerConfiguration($params['module']);
496
497     if (isset($this->cacheConfig[$params['module']][$params['action']]))
498     {
499       return ($this->cacheConfig[$params['module']][$params['action']]['lifeTime'] > 0);
500     }
501     else if (isset($this->cacheConfig[$params['module']]['DEFAULT']))
502     {
503       return ($this->cacheConfig[$params['module']]['DEFAULT']['lifeTime'] > 0);
504     }
505
506     return false;
507   }
508
509   /**
510    * Returns true if the action is cacheable.
511    *
512    * @param  string $moduleName A module name
513    * @param  string $actionName An action or partial template name
514    *
515    * @return boolean True if the action is cacheable
516    *
517    * @see isCacheable()
518    */
519   public function isActionCacheable($moduleName, $actionName)
520   {
521     if ($this->request instanceof sfWebRequest && !$this->request->isMethod(sfRequest::GET))
522     {
523       return false;
524     }
525
526     $this->registerConfiguration($moduleName);
527
528     if (isset($this->cacheConfig[$moduleName][$actionName]))
529     {
530       return $this->cacheConfig[$moduleName][$actionName]['lifeTime'] > 0;
531     }
532     else if (isset($this->cacheConfig[$moduleName]['DEFAULT']))
533     {
534       return $this->cacheConfig[$moduleName]['DEFAULT']['lifeTime'] > 0;
535     }
536
537     return false;
538   }
539
540   /**
541    * Retrieves content in the cache.
542    *
543    * @param  string $internalUri  Internal uniform resource identifier
544    *
545    * @return string The content in the cache
546    */
547   public function get($internalUri)
548   {
549     // no cache or no cache set for this action
550     if (!$this->isCacheable($internalUri) || $this->ignore())
551     {
552       return null;
553     }
554
555     $retval = $this->cache->get($this->generateCacheKey($internalUri));
556
557     if (sfConfig::get('sf_logging_enabled'))
558     {
559       $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Cache for "%s" %s', $internalUri, $retval !== null ? 'exists' : 'does not exist'))));
560     }
561
562     return $retval;
563   }
564
565   /**
566    * Returns true if there is a cache.
567    *
568    * @param  string $internalUri  Internal uniform resource identifier
569    *
570    * @return bool true, if there is a cache otherwise false
571    */
572   public function has($internalUri)
573   {
574     if (!$this->isCacheable($internalUri) || $this->ignore())
575     {
576       return null;
577     }
578
579     return $this->cache->has($this->generateCacheKey($internalUri));
580   }
581
582   /**
583    * Ignores the cache functionality.
584    *
585    * @return bool true, if the cache is ignore otherwise false
586    */
587   protected function ignore()
588   {
589     // ignore cache parameter? (only available in debug mode)
590     if (sfConfig::get('sf_debug') && $this->context->getRequest()->getAttribute('sf_ignore_cache'))
591     {
592       if (sfConfig::get('sf_logging_enabled'))
593       {
594         $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Discard cache')));
595       }
596
597       return true;
598     }
599
600     return false;
601   }
602
603   /**
604    * Sets the cache content.
605    *
606    * @param  string $data         Data to put in the cache
607    * @param  string $internalUri  Internal uniform resource identifier
608    *
609    * @return boolean true, if the data get set successfully otherwise false
610    */
611   public function set($data, $internalUri)
612   {
613     if (!$this->isCacheable($internalUri))
614     {
615       return false;
616     }
617
618     try
619     {
620       $ret = $this->cache->set($this->generateCacheKey($internalUri), $data, $this->getLifeTime($internalUri));
621     }
622     catch (Exception $e)
623     {
624       return false;
625     }
626
627     if (sfConfig::get('sf_logging_enabled'))
628     {
629       $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Save cache for "%s"', $internalUri))));
630     }
631
632     return true;
633   }
634
635   /**
636    * Removes the content in the cache.
637    *
638    * @param  string $internalUri       Internal uniform resource identifier
639    * @param  string $hostName          The host name
640    * @param  string $vary              The vary headers, separated by |, or "all" for all vary headers
641    * @param  string $contextualPrefix  The removal prefix for contextual partials. Defaults to '**' (all actions, all params)
642    *
643    * @return bool true, if the remove happened, false otherwise
644    */
645   public function remove($internalUri, $hostName = '', $vary = '', $contextualPrefix = '**')
646   {
647     if (sfConfig::get('sf_logging_enabled'))
648     {
649       $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Remove cache for "%s"', $internalUri))));
650     }
651
652     $cacheKey = $this->generateCacheKey($internalUri, $hostName, $vary, $contextualPrefix);
653
654     if(strpos($cacheKey, '*'))
655     {
656       return $this->cache->removePattern($cacheKey);
657     }
658     elseif ($this->cache->has($cacheKey))
659     {
660       return $this->cache->remove($cacheKey);
661     }
662   }
663
664   /**
665    * Retrieves the last modified time.
666    *
667    * @param  string $internalUri  Internal uniform resource identifier
668    *
669    * @return int    The last modified datetime
670    */
671   public function getLastModified($internalUri)
672   {
673     if (!$this->isCacheable($internalUri))
674     {
675       return 0;
676     }
677
678     return $this->cache->getLastModified($this->generateCacheKey($internalUri));
679   }
680
681   /**
682    * Retrieves the timeout.
683    *
684    * @param  string $internalUri  Internal uniform resource identifier
685    *
686    * @return int    The timeout datetime
687    */
688   public function getTimeout($internalUri)
689   {
690     if (!$this->isCacheable($internalUri))
691     {
692       return 0;
693     }
694
695     return $this->cache->getTimeout($this->generateCacheKey($internalUri));
696   }
697
698   /**
699    * Starts the fragment cache.
700    *
701    * @param  string $name            Unique fragment name
702    * @param  string $lifeTime        Life time for the cache
703    * @param  string $clientLifeTime  Client life time for the cache
704    * @param  array  $vary            Vary options for the cache
705    *
706    * @return bool true, if success otherwise false
707    */
708   public function start($name, $lifeTime, $clientLifeTime = null, $vary = array())
709   {
710     if (!$clientLifeTime)
711     {
712       $clientLifeTime = $lifeTime;
713     }
714
715     // fetch fragment uri
716     $internalUri = $this->getFragmentUri( $name );
717
718     // add cache config to cache manager
719     list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
720     // module should be __sf_cache_fragment and action should be the name of the fragment
721     $this->addCache($params['module'], $params['action'], array('withLayout' => false, 'lifeTime' => $lifeTime, 'clientLifeTime' => $clientLifeTime, 'vary' => $vary));
722
723     // get data from cache if available
724     // note that sf_cache_key is automatically add on getFragmentUri()
725     $data = $this->get( $internalUri );
726     if ($data !== null)
727     {
728       return $data;
729     }
730     else
731     {
732       ob_start();
733       ob_implicit_flush(0);
734
735       return null;
736     }
737   }
738
739   /**
740    * Stops the fragment cache.
741    *
742    * @param  string $name Unique fragment name
743    *
744    * @return bool true, if success otherwise false
745    */
746   public function stop( $name )
747   {
748     $data = ob_get_clean();
749
750     try
751     {
752       $this->set( $data, $this->getFragmentUri( $name ) );
753     }
754     catch (Exception $e)
755     {
756     }
757
758     return $data;
759   }
760
761   /**
762    * Computes a fragment internal URI.
763    *
764    * @param  string $name      The fragement name
765    *
766    * @return string The internal URI
767    */
768   public function getFragmentUri( $name )
769   {
770     // in fragement case, module is __sf_cache_fragment and action is the fragment name
771     // module and action are required to the set() procedure and isCacheable() method
772     return sprintf('@sf_cache_fragment?module=%s&action=%s&sf_cache_key=%s', '__sf_cache_fragment' , $name , $name );
773   }
774
775   /**
776    * Computes the cache key based on the passed parameters.
777    *
778    * @param array $parameters  An array of parameters
779    */
780   public function computeCacheKey(array $parameters)
781   {
782     if (isset($parameters['sf_cache_key']))
783     {
784       return $parameters['sf_cache_key'];
785     }
786
787     if (sfConfig::get('sf_logging_enabled'))
788     {
789       $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Generate cache key')));
790     }
791     ksort($parameters);
792
793     return md5(serialize($parameters));
794   }
795
796   /**
797    * Checks that the supplied parameters include a cache key.
798    *
799    * If no 'sf_cache_key' parameter is present one is added to the array as
800    * it is passed by reference.
801    *
802    * @param  array  $parameters An array of parameters
803    *
804    * @return string The cache key
805    */
806   public function checkCacheKey(array & $parameters)
807   {
808     $parameters['sf_cache_key'] = $this->computeCacheKey($parameters);
809
810     return $parameters['sf_cache_key'];
811   }
812
813   /**
814    * Computes a partial internal URI.
815    *
816    * @param  string $module    The module name
817    * @param  string $action    The action name
818    * @param  string $cacheKey  The cache key
819    *
820    * @return string The internal URI
821    */
822   public function getPartialUri($module, $action, $cacheKey)
823   {
824     return sprintf('@sf_cache_partial?module=%s&action=%s&sf_cache_key=%s', $module, $action, $cacheKey);
825   }
826
827   /**
828    * Returns whether a partial template is in the cache.
829    *
830    * @param  string $module    The module name
831    * @param  string $action    The action name
832    * @param  string $cacheKey  The cache key
833    *
834    * @return bool true if a partial is in the cache, false otherwise
835    */
836   public function hasPartialCache($module, $action, $cacheKey)
837   {
838     return $this->has($this->getPartialUri($module, $action, $cacheKey));
839   }
840
841   /**
842    * Gets a partial template from the cache.
843    *
844    * @param  string $module    The module name
845    * @param  string $action    The action name
846    * @param  string $cacheKey  The cache key
847    *
848    * @return string The cache content
849    */
850   public function getPartialCache($module, $action, $cacheKey)
851   {
852     $uri = $this->getPartialUri($module, $action, $cacheKey);
853
854     if (!$this->isCacheable($uri))
855     {
856       return null;
857     }
858
859     // retrieve content from cache
860     $cache = $this->get($uri);
861
862     if (null === $cache)
863     {
864       return null;
865     }
866
867     $cache = unserialize($cache);
868     $content = $cache['content'];
869     $this->context->getResponse()->merge($cache['response']);
870
871     if (sfConfig::get('sf_web_debug'))
872     {
873       $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => false)), $content)->getReturnValue();
874     }
875
876     return $content;
877   }
878
879   /**
880    * Sets an action template in the cache.
881    *
882    * @param  string $module    The module name
883    * @param  string $action    The action name
884    * @param  string $cacheKey  The cache key
885    * @param  string $content   The content to cache
886    *
887    * @return string The cached content
888    */
889   public function setPartialCache($module, $action, $cacheKey, $content)
890   {
891     $uri = $this->getPartialUri($module, $action, $cacheKey);
892     if (!$this->isCacheable($uri))
893     {
894       return $content;
895     }
896
897     $saved = $this->set(serialize(array('content' => $content, 'response' => $this->context->getResponse())), $uri);
898
899     if ($saved && sfConfig::get('sf_web_debug'))
900     {
901       $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => true)), $content)->getReturnValue();
902     }
903
904     return $content;
905   }
906
907   /**
908    * Returns whether an action template is in the cache.
909    *
910    * @param  string  $uri  The internal URI
911    *
912    * @return bool true if an action is in the cache, false otherwise
913    */
914   public function hasActionCache($uri)
915   {
916     return $this->has($uri) && !$this->withLayout($uri);
917   }
918
919   /**
920    * Gets an action template from the cache.
921    *
922    * @param  string $uri  The internal URI
923    *
924    * @return array  An array composed of the cached content and the view attribute holder
925    */
926   public function getActionCache($uri)
927   {
928     if (!$this->isCacheable($uri) || $this->withLayout($uri))
929     {
930       return null;
931     }
932
933     // retrieve content from cache
934     $cache = $this->get($uri);
935
936     if (null === $cache)
937     {
938       return null;
939     }
940
941     $cache = unserialize($cache);
942     $content = $cache['content'];
943     $cache['response']->setEventDispatcher($this->dispatcher);
944     $this->context->getResponse()->copyProperties($cache['response']);
945
946     if (sfConfig::get('sf_web_debug'))
947     {
948       $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => false)), $content)->getReturnValue();
949     }
950
951     return array($content, $cache['decoratorTemplate']);
952   }
953
954   /**
955    * Sets an action template in the cache.
956    *
957    * @param  string $uri                The internal URI
958    * @param  string $content            The content to cache
959    * @param  string $decoratorTemplate  The view attribute holder to cache
960    *
961    * @return string The cached content
962    */
963   public function setActionCache($uri, $content, $decoratorTemplate)
964   {
965     if (!$this->isCacheable($uri) || $this->withLayout($uri))
966     {
967       return $content;
968     }
969
970     $saved = $this->set(serialize(array('content' => $content, 'decoratorTemplate' => $decoratorTemplate, 'response' => $this->context->getResponse())), $uri);
971
972     if ($saved && sfConfig::get('sf_web_debug'))
973     {
974       $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => true)), $content)->getReturnValue();
975     }
976
977     return $content;
978   }
979
980   /**
981    * Sets a page in the cache.
982    *
983    * @param string $uri  The internal URI
984    */
985   public function setPageCache($uri)
986   {
987     if (sfView::RENDER_CLIENT != $this->controller->getRenderMode())
988     {
989       return;
990     }
991
992     // save content in cache
993     $saved = $this->set(serialize($this->context->getResponse()), $uri);
994
995     if ($saved && sfConfig::get('sf_web_debug'))
996     {
997       $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => true)), $this->context->getResponse()->getContent())->getReturnValue();
998
999       $this->context->getResponse()->setContent($content);
1000     }
1001   }
1002
1003   /**
1004    * Gets a page from the cache.
1005    *
1006    * @param  string $uri  The internal URI
1007    *
1008    * @return string The cached page
1009    */
1010   public function getPageCache($uri)
1011   {
1012     $retval = $this->get($uri);
1013
1014     if (null === $retval)
1015     {
1016       return false;
1017     }
1018
1019     $cachedResponse = unserialize($retval);
1020     $cachedResponse->setEventDispatcher($this->dispatcher);
1021
1022     if (sfView::RENDER_VAR == $this->controller->getRenderMode())
1023     {
1024       $this->controller->getActionStack()->getLastEntry()->setPresentation($cachedResponse->getContent());
1025       $this->context->getResponse()->setContent('');
1026     }
1027     else
1028     {
1029       $this->context->setResponse($cachedResponse);
1030
1031       if (sfConfig::get('sf_web_debug'))
1032       {
1033         $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => false)), $this->context->getResponse()->getContent())->getReturnValue();
1034
1035         $this->context->getResponse()->setContent($content);
1036       }
1037     }
1038
1039     return true;
1040   }
1041
1042   /**
1043    * Returns the current request's cache key.
1044    *
1045    * This cache key is calculated based on the routing factory's current URI
1046    * and any GET parameters from the current request factory.
1047    *
1048    * @return string The cache key for the current request
1049    */
1050   public function getCurrentCacheKey()
1051   {
1052     $cacheKey = $this->routing->getCurrentInternalUri();
1053
1054     if ($getParameters = $this->request->getGetParameters())
1055     {
1056       $cacheKey .= false === strpos($cacheKey, '?') ? '?' : '&';
1057       $cacheKey .= http_build_query($getParameters, null, '&');
1058     }
1059
1060     return $cacheKey;
1061   }
1062
1063   /**
1064    * Listens to the 'view.cache.filter_content' event to decorate a chunk of HTML with cache information.
1065    *
1066    * @param sfEvent $event   A sfEvent instance
1067    * @param string  $content The HTML content
1068    *
1069    * @return string The decorated HTML string
1070    */
1071   public function decorateContentWithDebug(sfEvent $event, $content)
1072   {
1073     // don't decorate if not html or if content is null
1074     if (!$content || false === strpos($event['response']->getContentType(), 'html'))
1075     {
1076       return $content;
1077     }
1078
1079     $this->context->getConfiguration()->loadHelpers(array('Helper', 'Url', 'Asset', 'Tag'));
1080
1081     $sf_cache_key = $this->generateCacheKey($event['uri']);
1082     $bgColor      = $event['new'] ? '#9ff' : '#ff9';
1083     $lastModified = $this->cache->getLastModified($sf_cache_key);
1084     $cacheKey     = $this->cache->getOption('prefix').$sf_cache_key;
1085     $id           = md5($event['uri']);
1086
1087     return '
1088       <div id="main_'.$id.'" class="sfWebDebugActionCache" style="border: 1px solid #f00">
1089       <div id="sub_main_'.$id.'" class="sfWebDebugCache" style="background-color: '.$bgColor.'; border-right: 1px solid #f00; border-bottom: 1px solid #f00;">
1090       <div style="height: 16px; padding: 2px"><a href="#" onclick="sfWebDebugToggle(\'sub_main_info_'.$id.'\'); return false;"><strong>cache information</strong></a>&nbsp;<a href="#" onclick="sfWebDebugToggle(\'sub_main_'.$id.'\'); document.getElementById(\'main_'.$id.'\').style.border = \'none\'; return false;">'.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/close.png', array('alt' => 'close')).'</a>&nbsp;</div>
1091         <div style="padding: 2px; display: none" id="sub_main_info_'.$id.'">
1092         [uri]&nbsp;'.htmlspecialchars($event['uri'], ENT_QUOTES, sfConfig::get('sf_charset')).'<br />
1093         [key&nbsp;for&nbsp;cache]&nbsp;'.htmlspecialchars($cacheKey, ENT_QUOTES, sfConfig::get('sf_charset')).'<br />
1094         [life&nbsp;time]&nbsp;'.$this->getLifeTime($event['uri']).'&nbsp;seconds<br />
1095         [last&nbsp;modified]&nbsp;'.(time() - $lastModified).'&nbsp;seconds<br />
1096         &nbsp;<br />&nbsp;
1097         </div>
1098       </div><div>
1099       '.$content.'
1100       </div></div>
1101     ';
1102   }
1103 }
1104
Note: See TracBrowser for help on using the browser.