Development

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

You must first sign up to be able to contribute.

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

Revision 7724, 19.3 kB (checked in by fabien, 5 years ago)

changed sfWebResponse::*PartialCache?() signatures, fixed phpdoc (WIP)

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Rev Date
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     $loaded      = array();
33
34   /**
35    * Class constructor.
36    *
37    * @see initialize()
38    */
39   public function __construct($context, sfCache $cache)
40   {
41     $this->initialize($context, $cache);
42   }
43
44   /**
45    * Initializes the cache manager.
46    *
47    * @param sfContext Current application context
48    * @param sfCache   An sfCache instance
49    */
50   public function initialize($context, sfCache $cache)
51   {
52     $this->context    = $context;
53     $this->dispatcher = $context->getEventDispatcher();
54     $this->controller = $context->getController();
55
56     // empty configuration
57     $this->cacheConfig = array();
58
59     // cache instance
60     $this->cache = $cache;
61
62     // register a named route for our partial cache (at the end)
63     $this->routing = $context->getRouting();
64     if (!$this->routing->hasRouteName('sf_cache_partial'))
65     {
66       $this->routing->connect('sf_cache_partial', '/sf_cache_partial/:module/:action/:sf_cache_key.', array(), array());
67     }
68   }
69
70   /**
71    * Retrieves the current cache context.
72    *
73    * @return sfContext The sfContext instance
74    */
75   public function getContext()
76   {
77     return $this->context;
78   }
79
80   /**
81    * Retrieves the current cache object.
82    *
83    * @return sfCache The current cache object
84    */
85   public function getCache()
86   {
87     return $this->cache;
88   }
89
90   /**
91    * Generates a unique cache key for an internal URI.
92    *
93    * @param  string The internal unified resource identifier.
94    *
95    * @return string The cache key
96    */
97   public function generateCacheKey($internalUri)
98   {
99     if ($callable = sfConfig::get('sf_cache_namespace_callable'))
100     {
101       if (!is_callable($callable))
102       {
103         throw new sfException(sprintf('"%s" cannot be called as a function.', var_export($callable, true)));
104       }
105
106       return call_user_func($callable, $internalUri);
107     }
108
109     // generate uri
110     // we want our URL with / only
111     $oldUrlFormat = sfConfig::get('sf_url_format');
112     sfConfig::set('sf_url_format', 'PATH');
113     if ($this->isContextual($internalUri))
114     {
115       list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
116       $uri = $this->controller->genUrl($this->routing->getCurrentInternalUri()).sprintf('/%s/%s/%s', $params['module'], $params['action'], $params['sf_cache_key']);
117     }
118     else
119     {
120       $uri = $this->controller->genUrl($internalUri);
121     }
122     sfConfig::set('sf_url_format', $oldUrlFormat);
123
124     // prefix with vary headers
125     $varyHeaders = $this->getVary($internalUri);
126     if ($varyHeaders)
127     {
128       sort($varyHeaders);
129       $request = $this->context->getRequest();
130       $vary = '';
131
132       foreach ($varyHeaders as $header)
133       {
134         $vary .= $request->getHttpHeader($header).'|';
135       }
136
137       $vary = $vary;
138     }
139     else
140     {
141       $vary = 'all';
142     }
143
144     // prefix with hostname
145     $request = $this->context->getRequest();
146     $hostName = $request->getHost();
147     $hostName = preg_replace('/[^a-z0-9]/i', '_', $hostName);
148     $hostName = strtolower(preg_replace('/_+/', '_', $hostName));
149
150     $uri = '/'.$hostName.'/'.$vary.'/'.$uri;
151
152     // replace multiple /
153     $uri = preg_replace('#/+#', '/', $uri);
154
155     return $uri;
156   }
157
158   /**
159    * Adds a cache to the manager.
160    *
161    * @param string Module name
162    * @param string Action name
163    * @param array Options for the cache
164    */
165   public function addCache($moduleName, $actionName, $options = array())
166   {
167     // normalize vary headers
168     foreach ($options['vary'] as $key => $name)
169     {
170       $options['vary'][$key] = strtr(strtolower($name), '_', '-');
171     }
172
173     $options['lifeTime'] = isset($options['lifeTime']) ? $options['lifeTime'] : 0;
174     if (!isset($this->cacheConfig[$moduleName]))
175     {
176       $this->cacheConfig[$moduleName] = array();
177     }
178     $this->cacheConfig[$moduleName][$actionName] = array(
179       'withLayout'     => isset($options['withLayout']) ? $options['withLayout'] : false,
180       'lifeTime'       => $options['lifeTime'],
181       'clientLifeTime' => isset($options['clientLifeTime']) && $options['clientLifeTime'] ? $options['clientLifeTime'] : $options['lifeTime'],
182       'contextual'     => isset($options['contextual']) ? $options['contextual'] : false,
183       'vary'           => isset($options['vary']) ? $options['vary'] : array(),
184     );
185   }
186
187   /**
188    * Registers configuration options for the cache.
189    *
190    * @param string Module name
191    */
192   public function registerConfiguration($moduleName)
193   {
194     if (!isset($this->loaded[$moduleName]))
195     {
196       require($this->context->getConfigCache()->checkConfig('modules/'.$moduleName.'/config/cache.yml'));
197       $this->loaded[$moduleName] = true;
198     }
199   }
200
201   /**
202    * Retrieves the layout from the cache option list.
203    *
204    * @param string Internal uniform resource identifier
205    *
206    * @return boolean true, if have layout otherwise false
207    */
208   public function withLayout($internalUri)
209   {
210     return $this->getCacheConfig($internalUri, 'withLayout', false);
211   }
212
213   /**
214    * Retrieves lifetime from the cache option list.
215    *
216    * @param string Internal uniform resource identifier
217    *
218    * @return int LifeTime
219    */
220   public function getLifeTime($internalUri)
221   {
222     return $this->getCacheConfig($internalUri, 'lifeTime', 0);
223   }
224
225   /**
226    * Retrieves client lifetime from the cache option list
227    *
228    * @param string Internal uniform resource identifier
229    *
230    * @return int Client lifetime
231    */
232   public function getClientLifeTime($internalUri)
233   {
234     return $this->getCacheConfig($internalUri, 'clientLifeTime', 0);
235   }
236
237   /**
238    * Retrieves contextual option from the cache option list.
239    *
240    * @param string Internal uniform resource identifier
241    *
242    * @return boolean true, if is contextual otherwise false
243    */
244   public function isContextual($internalUri)
245   {
246     return $this->getCacheConfig($internalUri, 'contextual', false);
247   }
248
249   /**
250    * Retrieves vary option from the cache option list.
251    *
252    * @param string Internal uniform resource identifier
253    *
254    * @return array Vary options for the cache
255    */
256   public function getVary($internalUri)
257   {
258     return $this->getCacheConfig($internalUri, 'vary', array());
259   }
260
261   /**
262    * Gets a config option from the cache.
263    *
264    * @param string Internal uniform resource identifier
265    * @param string Option name
266    * @param string Default value of the option
267    *
268    * @return mixed Value of the option
269    */
270   protected function getCacheConfig($internalUri, $key, $defaultValue = null)
271   {
272     list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
273
274     $value = $defaultValue;
275     if (isset($this->cacheConfig[$params['module']][$params['action']][$key]))
276     {
277       $value = $this->cacheConfig[$params['module']][$params['action']][$key];
278     }
279     else if (isset($this->cacheConfig[$params['module']]['DEFAULT'][$key]))
280     {
281       $value = $this->cacheConfig[$params['module']]['DEFAULT'][$key];
282     }
283
284     return $value;
285   }
286
287   /**
288    * Returns true if the current content is cacheable.
289    *
290    * @param string Internal uniform resource identifier
291    *
292    * @return boolean true, if the content is cacheable otherwise false
293    */
294   public function isCacheable($internalUri)
295   {
296     list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
297
298     if (isset($this->cacheConfig[$params['module']][$params['action']]))
299     {
300       return ($this->cacheConfig[$params['module']][$params['action']]['lifeTime'] > 0);
301     }
302     else if (isset($this->cacheConfig[$params['module']]['DEFAULT']))
303     {
304       return ($this->cacheConfig[$params['module']]['DEFAULT']['lifeTime'] > 0);
305     }
306
307     return false;
308   }
309
310   /**
311    * Retrieves content in the cache.
312    *
313    * @param  string Internal uniform resource identifier
314    *
315    * @return string The content in the cache
316    */
317   public function get($internalUri)
318   {
319     // no cache or no cache set for this action
320     if (!$this->isCacheable($internalUri) || $this->ignore())
321     {
322       return null;
323     }
324
325     $retval = $this->cache->get($this->generateCacheKey($internalUri));
326
327     if (sfConfig::get('sf_logging_enabled'))
328     {
329       $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Cache for "%s" %s', $internalUri, $retval !== null ? 'exists' : 'does not exist'))));
330     }
331
332     return $retval;
333   }
334
335   /**
336    * Returns true if there is a cache.
337    *
338    * @param string Internal uniform resource identifier
339    *
340    * @return boolean true, if there is a cache otherwise false
341    */
342   public function has($internalUri)
343   {
344     if (!$this->isCacheable($internalUri) || $this->ignore())
345     {
346       return null;
347     }
348
349     return $this->cache->has($this->generateCacheKey($internalUri));
350   }
351
352   /**
353    * Ignores the cache functionality.
354    *
355    * @return boolean true, if the cache is ignore otherwise false
356    */
357   protected function ignore()
358   {
359     // ignore cache parameter? (only available in debug mode)
360     if (sfConfig::get('sf_debug') && $this->context->getRequest()->getAttribute('_sf_ignore_cache'))
361     {
362       if (sfConfig::get('sf_logging_enabled'))
363       {
364         $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Discard cache')));
365       }
366
367       return true;
368     }
369
370     return false;
371   }
372
373   /**
374    * Sets the cache content.
375    *
376    * @param string Data to put in the cache
377    * @param string Internal uniform resource identifier
378    *
379    * @return boolean true, if the data get set successfully otherwise false
380    */
381   public function set($data, $internalUri)
382   {
383     if (!$this->isCacheable($internalUri))
384     {
385       return false;
386     }
387
388     try
389     {
390       $ret = $this->cache->set($this->generateCacheKey($internalUri), $data, $this->getLifeTime($internalUri));
391     }
392     catch (Exception $e)
393     {
394       return false;
395     }
396
397     if (sfConfig::get('sf_logging_enabled'))
398     {
399       $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Save cache for "%s"', $internalUri))));
400     }
401
402     return true;
403   }
404
405   /**
406    * Removes the content in the cache.
407    *
408    * @param string Internal uniform resource identifier
409    *
410    * @return boolean true, if the remove happend otherwise false
411    */
412   public function remove($internalUri)
413   {
414     if (sfConfig::get('sf_logging_enabled'))
415     {
416       $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Remove cache for "%s"', $internalUri))));
417     }
418
419     $key = $this->generateCacheKey($internalUri);
420     if ($this->cache->has($key))
421     {
422       $this->cache->remove($key);
423     }
424   }
425
426   /**
427    * Retrieves the last modified time.
428    *
429    * @param  string Internal uniform resource identifier
430    *
431    * @return int    The last modified datetime
432    */
433   public function getLastModified($internalUri)
434   {
435     if (!$this->isCacheable($internalUri))
436     {
437       return 0;
438     }
439
440     return $this->cache->getLastModified($this->generateCacheKey($internalUri));
441   }
442
443   /**
444    * Retrieves the timeout.
445    *
446    * @param  string Internal uniform resource identifier
447    *
448    * @return int    The timeout datetime
449    */
450   public function getTimeout($internalUri)
451   {
452     if (!$this->isCacheable($internalUri))
453     {
454       return 0;
455     }
456
457     return $this->cache->getTimeout($this->generateCacheKey($internalUri));
458   }
459
460   /**
461    * Starts the fragment cache.
462    *
463    * @param string Unique fragment name
464    * @param string Life time for the cache
465    * @param string Client life time for the cache
466    * @param array Vary options for the cache
467    *
468    * @return boolean true, if success otherwise false
469    */
470   public function start($name, $lifeTime, $clientLifeTime = null, $vary = array())
471   {
472     $internalUri = $this->routing->getCurrentInternalUri();
473
474     if (!$clientLifeTime)
475     {
476       $clientLifeTime = $lifeTime;
477     }
478
479     // add cache config to cache manager
480     list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
481     $this->addCache($params['module'], $params['action'], array('withLayout' => false, 'lifeTime' => $lifeTime, 'clientLifeTime' => $clientLifeTime, 'vary' => $vary));
482
483     // get data from cache if available
484     $data = $this->get($internalUri.(strpos($internalUri, '?') ? '&' : '?').'_sf_cache_key='.$name);
485     if ($data !== null)
486     {
487       return $data;
488     }
489     else
490     {
491       ob_start();
492       ob_implicit_flush(0);
493
494       return null;
495     }
496   }
497
498   /**
499    * Stops the fragment cache.
500    *
501    * @param string Unique fragment name
502    *
503    * @return boolean true, if success otherwise false
504    */
505   public function stop($name)
506   {
507     $data = ob_get_clean();
508
509     // save content to cache
510     $internalUri = $this->routing->getCurrentInternalUri();
511     try
512     {
513       $this->set($data, $internalUri.(strpos($internalUri, '?') ? '&' : '?').'_sf_cache_key='.$name);
514     }
515     catch (Exception $e)
516     {
517     }
518
519     return $data;
520   }
521
522   /**
523    * Computes the cache key based on the passed parameters.
524    *
525    * @param array An array of parameters
526    */
527   public function computeCacheKey(array $parameters)
528   {
529     return isset($parameters['sf_cache_key']) ? $parameters['sf_cache_key'] : md5(serialize($parameters));
530   }
531
532   /**
533    * Computes a partial internal URI.
534    *
535    * @param  string The module name
536    * @param  string The action name
537    * @param  string The cache key
538    *
539    * @return string The internal URI
540    */
541   public function getPartialUri($module, $action, $cacheKey)
542   {
543     return sprintf('@sf_cache_partial?module=%s&action=%s&sf_cache_key=%s', $module, $action, $cacheKey);
544   }
545
546   /**
547    * Returns whether a partial template is in the cache.
548    *
549    * @param  string The module name
550    * @param  string The action name
551    * @param  string The cache key
552    *
553    * @return Boolean true if a partial is in the cache, false otherwise
554    */
555   public function hasPartialCache($module, $action, $cacheKey)
556   {
557     return $this->has($this->getPartialUri($module, $action, $cacheKey));
558   }
559
560   /**
561    * Gets a partial template from the cache.
562    *
563    * @param  string The module name
564    * @param  string The action name
565    * @param  string The cache key
566    *
567    * @return string The cache content
568    */
569   public function getPartialCache($module, $action, $cacheKey)
570   {
571     $uri = $this->getPartialUri($module, $action, $cacheKey);
572
573     if (!$this->isCacheable($uri))
574     {
575       return null;
576     }
577
578     // retrieve content from cache
579     $cache = $this->get($uri);
580
581     if (is_null($cache))
582     {
583       return null;
584     }
585
586     $cache = unserialize($cache);
587     $content = $cache['content'];
588     $this->context->getResponse()->merge($cache['response']);
589
590     if (sfConfig::get('sf_web_debug'))
591     {
592       $content = sfWebDebug::decorateContentWithDebug($uri, $content, true);
593     }
594
595     return $content;
596   }
597
598   /**
599    * Sets an action template in the cache.
600    *
601    * @param  string The module name
602    * @param  string The action name
603    * @param  string The cache key
604    * @param  string The content to cache
605    *
606    * @return string The cached content
607    */
608   public function setPartialCache($module, $action, $cacheKey, $content)
609   {
610     $uri = $this->getPartialUri($module, $action, $cacheKey);
611     if (!$this->isCacheable($uri))
612     {
613       return $content;
614     }
615
616     $saved = $this->set(serialize(array('content' => $content, 'response' => $this->context->getResponse())), $uri);
617
618     if ($saved && sfConfig::get('sf_web_debug'))
619     {
620       $content = sfWebDebug::decorateContentWithDebug($uri, $content, true);
621     }
622
623     return $content;
624   }
625
626   /**
627    * Returns whether an action template is in the cache.
628    *
629    * @param  string  The internal URI
630    *
631    * @return Boolean true if an action is in the cache, false otherwise
632    */
633   public function hasActionCache($uri)
634   {
635     return $this->has($uri) && !$this->withLayout($uri);
636   }
637
638   /**
639    * Gets an action template from the cache.
640    *
641    * @param  string The internal URI
642    *
643    * @return array  An array composed of the cached content and the view attribute holder
644    */
645   public function getActionCache($uri)
646   {
647     if (!$this->isCacheable($uri) || $this->withLayout($uri))
648     {
649       return null;
650     }
651
652     // retrieve content from cache
653     $cache = $this->get($uri);
654
655     if (is_null($cache))
656     {
657       return null;
658     }
659
660     $cache = unserialize($cache);
661     $content = $cache['content'];
662     $cache['response']->setEventDispatcher($this->dispatcher);
663     $this->context->getResponse()->copyProperties($cache['response']);
664
665     if (sfConfig::get('sf_web_debug'))
666     {
667       $content = sfWebDebug::decorateContentWithDebug($uri, $content, false);
668     }
669
670     return array($content, $cache['attributeHolder'], $cache['decoratorTemplate']);
671   }
672
673   /**
674    * Sets an action template in the cache.
675    *
676    * @param  string The internal URI
677    * @param  string The content to cache
678    * @param  string The view attribute holder to cache
679    *
680    * @return string The cached content
681    */
682   public function setActionCache($uri, $content, $attributeHolder, $decoratorTemplate)
683   {
684     if (!$this->isCacheable($uri) || $this->withLayout($uri))
685     {
686       return $content;
687     }
688
689     $saved = $this->set(serialize(array('content' => $content, 'attributeHolder' => $attributeHolder, 'decoratorTemplate' => $decoratorTemplate, 'response' => $this->context->getResponse())), $uri);
690
691     if ($saved && sfConfig::get('sf_web_debug'))
692     {
693       $content = sfWebDebug::decorateContentWithDebug($uri, $content, true);
694     }
695
696     return $content;
697   }
698
699   /**
700    * Sets a page in the cache.
701    *
702    * @param string The internal URI
703    */
704   public function setPageCache($uri)
705   {
706     if (sfView::RENDER_CLIENT != $this->controller->getRenderMode())
707     {
708       return;
709     }
710
711     // save content in cache
712     $saved = $this->set(serialize($this->context->getResponse()), $uri);
713
714     if ($saved && sfConfig::get('sf_web_debug'))
715     {
716       $content = sfWebDebug::decorateContentWithDebug($uri, $this->context->getResponse()->getContent(), true);
717       $this->context->getResponse()->setContent($content);
718     }
719   }
720
721   /**
722    * Gets a page from the cache.
723    *
724    * @param  string The internal URI
725    *
726    * @return string The cached page
727    */
728   public function getPageCache($uri)
729   {
730     $retval = $this->get($uri);
731
732     if (is_null($retval))
733     {
734       return false;
735     }
736
737     $cachedResponse = unserialize($retval);
738     $cachedResponse->setEventDispatcher($this->dispatcher);
739
740     if (sfView::RENDER_VAR == $this->controller->getRenderMode())
741     {
742       $this->controller->getActionStack()->getLastEntry()->setPresentation($cachedResponse->getContent());
743       $this->response->setContent('');
744     }
745     else
746     {
747       $this->context->setResponse($cachedResponse);
748
749       if (sfConfig::get('sf_web_debug'))
750       {
751         $content = sfWebDebug::decorateContentWithDebug($uri, $this->context->getResponse()->getContent(), false);
752         $this->context->getResponse()->setContent($content);
753       }
754     }
755
756     return true;
757   }
758 }
759
Note: See TracBrowser for help on using the browser.