Development

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

You must first sign up to be able to contribute.

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

Revision 30031, 30.4 kB (checked in by Kris.Wallsmith, 4 years ago)

[1.3, 1.4] added prevention of injected directory traversal in view cache (closes #8805)

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