Development

/branches/1.0/lib/filter/sfCacheFilter.class.php

You must first sign up to be able to contribute.

root/branches/1.0/lib/filter/sfCacheFilter.class.php

Revision 24619, 8.0 kB (checked in by FabianLange, 5 years ago)

[1.0, 1.2, 1.3, 1.4] fixed incorrect array access of lastModified header which only was an array pre 1.0. This was effectively preventing 304 Not Modified response from working correctly. Fixed phpdoc referring to array as return type of getHttpHeader() (fixes #6633, #7539)

  • 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  * sfCacheFilter deals with page caching and action caching.
13  *
14  * @package    symfony
15  * @subpackage filter
16  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
17  * @version    SVN: $Id$
18  */
19 class sfCacheFilter extends sfFilter
20 {
21   protected
22     $cacheManager = null,
23     $request      = null,
24     $response     = null,
25     $cache        = array();
26
27   /**
28    * Initializes this Filter.
29    *
30    * @param sfContext The current application context
31    * @param array   An associative array of initialization parameters
32    *
33    * @return bool true, if initialization completes successfully, otherwise false
34    *
35    * @throws <b>sfInitializationException</b> If an error occurs while initializing this Filter
36    */
37   public function initialize($context, $parameters = array())
38   {
39     parent::initialize($context, $parameters);
40
41     $this->cacheManager = $context->getViewCacheManager();
42     $this->request      = $context->getRequest();
43     $this->response     = $context->getResponse();
44   }
45
46   /**
47    * Executes this filter.
48    *
49    * @param sfFilterChain A sfFilterChain instance
50    */
51   public function execute($filterChain)
52   {
53     // execute this filter only once, if cache is set and no GET or POST parameters
54     if (!sfConfig::get('sf_cache') || count($_GET) || count($_POST))
55     {
56       $filterChain->execute();
57
58       return;
59     }
60
61     if ($this->executeBeforeExecution())
62     {
63       $filterChain->execute();
64     }
65
66     $this->executeBeforeRendering();
67   }
68
69   public function executeBeforeExecution()
70   {
71     // register our cache configuration
72     $this->cacheManager->registerConfiguration($this->getContext()->getModuleName());
73
74     $uri = sfRouting::getInstance()->getCurrentInternalUri();
75
76     // page cache
77     $this->cache[$uri] = array('page' => false, 'action' => false);
78     $cacheable = $this->cacheManager->isCacheable($uri);
79     if ($cacheable)
80     {
81       if ($this->cacheManager->withLayout($uri))
82       {
83         $inCache = $this->getPageCache($uri);
84         $this->cache[$uri]['page'] = !$inCache;
85
86         if ($inCache)
87         {
88           // page is in cache, so no need to run execution filter
89           return false;
90         }
91       }
92       else
93       {
94         $inCache = $this->getActionCache($uri);
95         $this->cache[$uri]['action'] = !$inCache;
96       }
97     }
98
99     return true;
100   }
101
102   /**
103    * Executes this filter.
104    *
105    * @param sfFilterChain A sfFilterChain instance.
106    */
107   public function executeBeforeRendering()
108   {
109     // cache only 200 HTTP status
110     if (200 != $this->response->getStatusCode())
111     {
112       return;
113     }
114
115     $uri = sfRouting::getInstance()->getCurrentInternalUri();
116
117     // save page in cache
118     if (isset($this->cache[$uri]) && $this->cache[$uri]['page'])
119     {
120       // set some headers that deals with cache
121       if ($lifetime = $this->cacheManager->getClientLifeTime($uri, 'page'))
122       {
123         $this->response->setHttpHeader('Last-Modified', $this->response->getDate(time()), false);
124         $this->response->setHttpHeader('Expires', $this->response->getDate(time() + $lifetime), false);
125         $this->response->addCacheControlHttpHeader('max-age', $lifetime);
126       }
127
128       // set Vary headers
129       foreach ($this->cacheManager->getVary($uri, 'page') as $vary)
130       {
131         $this->response->addVaryHttpHeader($vary);
132       }
133
134       $this->setPageCache($uri);
135     }
136     else if (isset($this->cache[$uri]) && $this->cache[$uri]['action'])
137     {
138       // save action in cache
139       $this->setActionCache($uri);
140     }
141
142     // remove PHP automatic Cache-Control and Expires headers if not overwritten by application or cache
143     if ($this->response->hasHttpHeader('Last-Modified') || sfConfig::get('sf_etag'))
144     {
145       // FIXME: these headers are set by PHP sessions (see session_cache_limiter())
146       $this->response->setHttpHeader('Cache-Control', null, false);
147       $this->response->setHttpHeader('Expires', null, false);
148       $this->response->setHttpHeader('Pragma', null, false);
149     }
150
151     // Etag support
152     if (sfConfig::get('sf_etag'))
153     {
154       $etag = '"'.md5($this->response->getContent()).'"';
155       $this->response->setHttpHeader('ETag', $etag);
156
157       if ($this->request->getHttpHeader('IF_NONE_MATCH') == $etag)
158       {
159         $this->response->setStatusCode(304);
160         $this->response->setHeaderOnly(true);
161
162         if (sfConfig::get('sf_logging_enabled'))
163         {
164           $this->getContext()->getLogger()->info('{sfFilter} ETag matches If-None-Match (send 304)');
165         }
166       }
167     }
168
169     // conditional GET support
170     // never in debug mode
171     if ($this->response->hasHttpHeader('Last-Modified') && !sfConfig::get('sf_debug'))
172     {
173       $last_modified = $this->response->getHttpHeader('Last-Modified');
174       if ($this->request->getHttpHeader('IF_MODIFIED_SINCE') == $last_modified)
175       {
176         $this->response->setStatusCode(304);
177         $this->response->setHeaderOnly(true);
178
179         if (sfConfig::get('sf_logging_enabled'))
180         {
181           $this->getContext()->getLogger()->info('{sfFilter} Last-Modified matches If-Modified-Since (send 304)');
182         }
183       }
184     }
185   }
186
187   /**
188    * Sets a page template in the cache.
189    *
190    * @param string The internal URI
191    */
192   protected function setPageCache($uri)
193   {
194     if ($this->getContext()->getController()->getRenderMode() != sfView::RENDER_CLIENT)
195     {
196       return;
197     }
198
199     // save content in cache
200     $this->cacheManager->set(serialize($this->response), $uri);
201
202     if (sfConfig::get('sf_web_debug'))
203     {
204       $content = sfWebDebug::getInstance()->decorateContentWithDebug($uri, $this->response->getContent(), true);
205       $this->response->setContent($content);
206     }
207   }
208
209   /**
210    * Gets a page template from the cache.
211    *
212    * @param string The internal URI
213    */
214   protected function getPageCache($uri)
215   {
216     $context = $this->getContext();
217
218     // get the current action information
219     $moduleName = $context->getModuleName();
220     $actionName = $context->getActionName();
221
222     $retval = $this->cacheManager->get($uri);
223
224     if ($retval === null)
225     {
226       return false;
227     }
228
229     $cachedResponse = unserialize($retval);
230     $cachedResponse->setContext($context);
231
232     $controller = $context->getController();
233     if ($controller->getRenderMode() == sfView::RENDER_VAR)
234     {
235       $controller->getActionStack()->getLastEntry()->setPresentation($cachedResponse->getContent());
236       $this->response->setContent('');
237     }
238     else
239     {
240       $context->setResponse($cachedResponse);
241       $this->response = $this->getContext()->getResponse();
242
243       if (sfConfig::get('sf_web_debug'))
244       {
245         $content = sfWebDebug::getInstance()->decorateContentWithDebug($uri, $this->response->getContent(), false);
246         $this->response->setContent($content);
247       }
248     }
249
250     return true;
251   }
252
253   /**
254    * Sets an action template in the cache.
255    *
256    * @param string The internal URI
257    */
258   protected function setActionCache($uri)
259   {
260     $content = $this->response->getParameter($uri.'_action', null, 'symfony/cache');
261
262     if ($content !== null)
263     {
264       $this->cacheManager->set($content, $uri);
265     }
266   }
267
268   /**
269    * Gets an action template from the cache.
270    *
271    * @param string The internal URI
272    */
273   protected function getActionCache($uri)
274   {
275     // retrieve content from cache
276     $retval = $this->cacheManager->get($uri);
277
278     if ($retval && sfConfig::get('sf_web_debug'))
279     {
280       $cache = unserialize($retval);
281       $this->response->mergeProperties($cache['response']);
282       $cache['content'] = sfWebDebug::getInstance()->decorateContentWithDebug($uri, $cache['content'], false);
283       $retval = serialize($cache);
284     }
285
286     $this->response->setParameter('current_key', $uri.'_action', 'symfony/cache/current');
287     $this->response->setParameter($uri.'_action', $retval, 'symfony/cache');
288
289     return $retval ? true : false;
290   }
291 }
292
Note: See TracBrowser for help on using the browser.