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

Debug toolbar