Development

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

You must first sign up to be able to contribute.

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

Revision 17468, 14.8 kB (checked in by fabien, 5 years ago)

[1.0, 1.1, 1.2, 1.3] fixed cache manager when the cache partial or component is not from the same module as the same action (closes #6265, #5814)

  • 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 $cacheClass class to store cache.
15  * All cache files are stored in files in the [sf_root_dir].'/cache/'.[sf_app].'/html' directory.
16  * To disable all caching, you can set to false [sf_cache] constant.
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     $controller         = null,
30     $loaded             = array();
31
32   /**
33    * Initializes the cache manager.
34    *
35    * @param sfContext Current application context
36    * @param sfCache Type of the cache
37    * @param array Cache parameters
38    */
39   public function initialize($context, $cacheClass, $cacheParameters = array())
40   {
41     $this->context    = $context;
42     $this->controller = $context->getController();
43
44     // empty configuration
45     $this->cacheConfig = array();
46
47     // create cache instance
48     $this->cache = new $cacheClass();
49     $this->cache->initialize($cacheParameters);
50
51     // register a named route for our partial cache (at the end)
52     $r = sfRouting::getInstance();
53     if (!$r->hasRouteName('sf_cache_partial'))
54     {
55       $r->connect('sf_cache_partial', '/sf_cache_partial/:module/:action/:sf_cache_key.', array(), array());
56     }
57   }
58
59   /**
60    * Retrieves the current cache context.
61    *
62    * @return sfContext The sfContext instance
63    */
64   public function getContext()
65   {
66     return $this->context;
67   }
68
69   /**
70    * Retrieves the current cache type.
71    *
72    * @return sfCache The current cache type
73    */
74   public function getCache()
75   {
76     return $this->cache;
77   }
78
79   /**
80    * Generates namespaces for the cache manager
81    *
82    * @param string Internal unified resource identifier.
83    *
84    * @return array Path and filename for the current namespace
85    *
86    * @throws <b>sfException</b> if the generation fails
87    */
88   public function generateNamespace($internalUri)
89   {
90     if ($callable = sfConfig::get('sf_cache_namespace_callable'))
91     {
92       if (!is_callable($callable))
93       {
94         throw new sfException(sprintf('"%s" cannot be called as a function.', var_export($callable, true)));
95       }
96
97       return call_user_func($callable, $internalUri);
98     }
99
100     // generate uri
101     // we want our URL with / only
102     $oldUrlFormat = sfConfig::get('sf_url_format');
103     sfConfig::set('sf_url_format', 'PATH');
104     if ($this->isContextual($internalUri))
105     {
106       list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
107       $uri = $this->controller->genUrl(sfRouting::getInstance()->getCurrentInternalUri()).sprintf('/%s/%s/%s', $params['module'], $params['action'], $params['sf_cache_key']);
108     }
109     else
110     {
111       $uri = $this->controller->genUrl($internalUri);
112     }
113     sfConfig::set('sf_url_format', $oldUrlFormat);
114
115     // prefix with vary headers
116     $varyHeaders = $this->getVary($internalUri);
117     if ($varyHeaders)
118     {
119       sort($varyHeaders);
120       $request = $this->getContext()->getRequest();
121       $vary = '';
122
123       foreach ($varyHeaders as $header)
124       {
125         $vary .= $request->getHttpHeader($header).'|';
126       }
127
128       $vary = $vary;
129     }
130     else
131     {
132       $vary = 'all';
133     }
134
135     // prefix with hostname
136     $request = $this->context->getRequest();
137     $hostName = $request->getHost();
138     $hostName = preg_replace('/[^a-z0-9]/i', '_', $hostName);
139     $hostName = strtolower(preg_replace('/_+/', '_', $hostName));
140
141     $uri = '/'.$hostName.'/'.$vary.'/'.$uri;
142
143     // replace multiple /
144     $uri = preg_replace('#/+#', '/', $uri);
145
146     return array(dirname($uri), basename($uri));
147   }
148
149   /**
150    * Adds a cache to the manager.
151    *
152    * @param string Module name
153    * @param string Action name
154    * @param array Options for the cache
155    */
156   public function addCache($moduleName, $actionName, $options = array())
157   {
158     // normalize vary headers
159     if (isset($options['vary']))
160     {
161       foreach ($options['vary'] as $key => $name)
162       {
163         $options['vary'][$key] = strtr(strtolower($name), '_', '-');
164       }
165     }
166
167     $options['lifeTime'] = isset($options['lifeTime']) ? $options['lifeTime'] : 0;
168     if (!isset($this->cacheConfig[$moduleName]))
169     {
170       $this->cacheConfig[$moduleName] = array();
171     }
172     $this->cacheConfig[$moduleName][$actionName] = array(
173       'withLayout'     => isset($options['withLayout']) ? $options['withLayout'] : false,
174       'lifeTime'       => $options['lifeTime'],
175       'clientLifeTime' => isset($options['clientLifeTime']) ? $options['clientLifeTime'] : $options['lifeTime'],
176       'contextual'     => isset($options['contextual']) ? $options['contextual'] : false,
177       'vary'           => isset($options['vary']) ? $options['vary'] : array(),
178     );
179   }
180
181   /**
182    * Registers configuration options for the cache.
183    *
184    * @param string Module name
185    */
186   public function registerConfiguration($moduleName)
187   {
188     if (!isset($this->loaded[$moduleName]))
189     {
190       require(sfConfigCache::getInstance()->checkConfig(sfConfig::get('sf_app_module_dir_name').'/'.$moduleName.'/'.sfConfig::get('sf_app_module_config_dir_name').'/cache.yml'));
191       $this->loaded[$moduleName] = true;
192     }
193   }
194
195   /**
196    * Retrieves the layout from the cache option list.
197    *
198    * @param string Internal uniform resource identifier
199    *
200    * @return boolean true, if have layout otherwise false
201    */
202   public function withLayout($internalUri)
203   {
204     return $this->getCacheConfig($internalUri, 'withLayout', false);
205   }
206
207   /**
208    * Retrieves lifetime from the cache option list.
209    *
210    * @param string Internal uniform resource identifier
211    *
212    * @return int LifeTime
213    */
214   public function getLifeTime($internalUri)
215   {
216     return $this->getCacheConfig($internalUri, 'lifeTime', 0);
217   }
218
219   /**
220    * Retrieves client lifetime from the cache option list
221    *
222    * @param string Internal uniform resource identifier
223    *
224    * @return int Client lifetime
225    */
226   public function getClientLifeTime($internalUri)
227   {
228     return $this->getCacheConfig($internalUri, 'clientLifeTime', 0);
229   }
230
231   /**
232    * Retrieves contextual option from the cache option list.
233    *
234    * @param string Internal uniform resource identifier
235    *
236    * @return boolean true, if is contextual otherwise false
237    */
238   public function isContextual($internalUri)
239   {
240     return $this->getCacheConfig($internalUri, 'contextual', false);
241   }
242
243   /**
244    * Retrieves vary option from the cache option list.
245    *
246    * @param string Internal uniform resource identifier
247    *
248    * @return array Vary options for the cache
249    */
250   public function getVary($internalUri)
251   {
252     return $this->getCacheConfig($internalUri, 'vary', array());
253   }
254
255   /**
256    * Gets a config option from the cache.
257    *
258    * @param string Internal uniform resource identifier
259    * @param string Option name
260    * @param string Default value of the option
261    *
262    * @return mixed Value of the option
263    */
264   protected function getCacheConfig($internalUri, $key, $defaultValue = null)
265   {
266     list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
267
268     $this->registerConfiguration($params['module']);
269
270     $value = $defaultValue;
271     if (isset($this->cacheConfig[$params['module']][$params['action']][$key]))
272     {
273       $value = $this->cacheConfig[$params['module']][$params['action']][$key];
274     }
275     else if (isset($this->cacheConfig[$params['module']]['DEFAULT'][$key]))
276     {
277       $value = $this->cacheConfig[$params['module']]['DEFAULT'][$key];
278     }
279
280     return $value;
281   }
282
283   /**
284    * Returns true if the current content is cacheable.
285    *
286    * Possible break in backward compatibility: If the sf_lazy_cache_key
287    * setting is turned on in settings.yml, this method is not used when
288    * initially checking a partial's cacheability.
289    *
290    * @see sfPartialView, isActionCacheable()
291    *
292    * @param  string $internalUri  Internal uniform resource identifier
293    *
294    * @return bool true, if the content is cacheable otherwise false
295    */
296   public function isCacheable($internalUri)
297   {
298     if (count($_GET) || count($_POST))
299     {
300       return false;
301     }
302
303     list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
304
305     $this->registerConfiguration($params['module']);
306
307     if (isset($this->cacheConfig[$params['module']][$params['action']]))
308     {
309       return ($this->cacheConfig[$params['module']][$params['action']]['lifeTime'] > 0);
310     }
311     else if (isset($this->cacheConfig[$params['module']]['DEFAULT']))
312     {
313       return ($this->cacheConfig[$params['module']]['DEFAULT']['lifeTime'] > 0);
314     }
315
316     return false;
317   }
318
319   /**
320    * Returns true if the action is cacheable.
321    *
322    * @param  string $moduleName A module name
323    * @param  string $actionName An action or partial template name
324    *
325    * @return boolean True if the action is cacheable
326    *
327    * @see isCacheable()
328    */
329   public function isActionCacheable($moduleName, $actionName)
330   {
331     if (count($_GET) || count($_POST))
332     {
333       return false;
334     }
335
336     $this->registerConfiguration($moduleName);
337
338     if (isset($this->cacheConfig[$moduleName][$actionName]))
339     {
340       return $this->cacheConfig[$moduleName][$actionName]['lifeTime'] > 0;
341     }
342     else if (isset($this->cacheConfig[$moduleName]['DEFAULT']))
343     {
344       return $this->cacheConfig[$moduleName]['DEFAULT']['lifeTime'] > 0;
345     }
346
347     return false;
348   }
349
350   /**
351    * Retrieves namespace for the current cache.
352    *
353    * @param string Internal uniform resource identifier
354    *
355    * @return string The data of the cache
356    */
357   public function get($internalUri)
358   {
359     // no cache or no cache set for this action
360     if (!$this->isCacheable($internalUri) || $this->ignore())
361     {
362       return null;
363     }
364
365     list($namespace, $id) = $this->generateNamespace($internalUri);
366
367     $this->cache->setLifeTime($this->getLifeTime($internalUri));
368
369     $retval = $this->cache->get($id, $namespace);
370
371     if (sfConfig::get('sf_logging_enabled'))
372     {
373       $this->getContext()->getLogger()->info(sprintf('{sfViewCacheManager} cache for "%s" %s', $internalUri, ($retval !== null ? 'exists' : 'does not exist')));
374     }
375
376     return $retval;
377   }
378
379   /**
380    * Returns true if there is a cache.
381    *
382    * @param string Internal uniform resource identifier
383    *
384    * @return boolean true, if there is a cache otherwise false
385    */
386   public function has($internalUri)
387   {
388     if (!$this->isCacheable($internalUri) || $this->ignore())
389     {
390       return null;
391     }
392
393     list($namespace, $id) = $this->generateNamespace($internalUri);
394
395     $this->cache->setLifeTime($this->getLifeTime($internalUri));
396
397     return $this->cache->has($id, $namespace);
398   }
399
400   /**
401    * Ignores the cache functionality.
402    *
403    * @return boolean true, if the cache is ignore otherwise false
404    */
405   protected function ignore()
406   {
407     // ignore cache parameter? (only available in debug mode)
408     if (sfConfig::get('sf_debug') && $this->getContext()->getRequest()->getParameter('_sf_ignore_cache', false, 'symfony/request/sfWebRequest') == true)
409     {
410       if (sfConfig::get('sf_logging_enabled'))
411       {
412         $this->getContext()->getLogger()->info('{sfViewCacheManager} discard cache');
413       }
414
415       return true;
416     }
417
418     return false;
419   }
420
421   /**
422    * Sets the cache content
423    *
424    * @param string Data to put in the cache
425    * @param string Internal uniform resource identifier
426    *
427    * @return boolean true, if the data get set successfully otherwise false
428    */
429   public function set($data, $internalUri)
430   {
431     if (!$this->isCacheable($internalUri))
432     {
433       return false;
434     }
435
436     list($namespace, $id) = $this->generateNamespace($internalUri);
437
438     try
439     {
440       $ret = $this->cache->set($id, $namespace, $data);
441     }
442     catch (Exception $e)
443     {
444       return false;
445     }
446
447     if (sfConfig::get('sf_logging_enabled'))
448     {
449       $this->context->getLogger()->info(sprintf('{sfViewCacheManager} save cache for "%s"', $internalUri));
450     }
451
452     return true;
453   }
454
455   /**
456    * Removes the cache for the current namespace.
457    *
458    * @param string Internal uniform resource identifier
459    *
460    * @return boolean true, if the remove happend otherwise false
461    */
462   public function remove($internalUri)
463   {
464     list($namespace, $id) = $this->generateNamespace($internalUri);
465
466     if (sfConfig::get('sf_logging_enabled'))
467     {
468       $this->context->getLogger()->info(sprintf('{sfViewCacheManager} remove cache for "%s"', $internalUri));
469     }
470
471     if ($this->cache->has($id, $namespace))
472     {
473       $this->cache->remove($id, $namespace);
474     }
475   }
476
477   /**
478    * Retrieves the last modified time.
479    *
480    * @param string Internal uniform resource identifier
481    *
482    * @return string Last modified datetime for the current namespace
483    */
484   public function lastModified($internalUri)
485   {
486     if (!$this->isCacheable($internalUri))
487     {
488       return null;
489     }
490
491     list($namespace, $id) = $this->generateNamespace($internalUri);
492
493     return $this->cache->lastModified($id, $namespace);
494   }
495
496   /**
497    * Starts the fragment cache.
498    *
499    * @param string Unique fragment name
500    * @param string Life time for the cache
501    * @param string Client life time for the cache
502    * @param array Vary options for the cache
503    *
504    * @return boolean true, if success otherwise false
505    */
506   public function start($name, $lifeTime, $clientLifeTime = null, $vary = array())
507   {
508     $internalUri = sfRouting::getInstance()->getCurrentInternalUri();
509
510     if (!$clientLifeTime)
511     {
512       $clientLifeTime = $lifeTime;
513     }
514
515     // add cache config to cache manager
516     list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
517     $this->addCache($params['module'], $params['action'], array('withLayout' => false, 'lifeTime' => $lifeTime, 'clientLifeTime' => $clientLifeTime, 'vary' => $vary));
518
519     // get data from cache if available
520     $data = $this->get($internalUri.(strpos($internalUri, '?') ? '&' : '?').'_sf_cache_key='.$name);
521     if ($data !== null)
522     {
523       return $data;
524     }
525     else
526     {
527       ob_start();
528       ob_implicit_flush(0);
529
530       return null;
531     }
532   }
533
534   /**
535    * Stops the fragment cache.
536    *
537    * @param string Unique fragment name
538    *
539    * @return boolean true, if success otherwise false
540    */
541   public function stop($name)
542   {
543     $data = ob_get_clean();
544
545     // save content to cache
546     $internalUri = sfRouting::getInstance()->getCurrentInternalUri();
547     try
548     {
549       $this->set($data, $internalUri.(strpos($internalUri, '?') ? '&' : '?').'_sf_cache_key='.$name);
550     }
551     catch (Exception $e)
552     {
553     }
554
555     return $data;
556   }
557
558   /**
559    * Executes the shutdown procedure.
560    */
561   public function shutdown()
562   {
563   }
564 }
565
Note: See TracBrowser for help on using the browser.