sfViewCacheManager.class.php 26 KB

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