FragmentCache.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\widgets;
  8. use Yii;
  9. use yii\base\Widget;
  10. use yii\caching\Cache;
  11. use yii\caching\Dependency;
  12. /**
  13. *
  14. * @property string|boolean $cachedContent The cached content. False is returned if valid content is not found
  15. * in the cache. This property is read-only.
  16. *
  17. * @author Qiang Xue <[email protected]>
  18. * @since 2.0
  19. */
  20. class FragmentCache extends Widget
  21. {
  22. /**
  23. * @var Cache|string the cache object or the application component ID of the cache object.
  24. * After the FragmentCache object is created, if you want to change this property,
  25. * you should only assign it with a cache object.
  26. */
  27. public $cache = 'cache';
  28. /**
  29. * @var integer number of seconds that the data can remain valid in cache.
  30. * Use 0 to indicate that the cached data will never expire.
  31. */
  32. public $duration = 60;
  33. /**
  34. * @var array|Dependency the dependency that the cached content depends on.
  35. * This can be either a [[Dependency]] object or a configuration array for creating the dependency object.
  36. * For example,
  37. *
  38. * ~~~
  39. * [
  40. * 'class' => 'yii\caching\DbDependency',
  41. * 'sql' => 'SELECT MAX(lastModified) FROM Post',
  42. * ]
  43. * ~~~
  44. *
  45. * would make the output cache depends on the last modified time of all posts.
  46. * If any post has its modification time changed, the cached content would be invalidated.
  47. */
  48. public $dependency;
  49. /**
  50. * @var array list of factors that would cause the variation of the content being cached.
  51. * Each factor is a string representing a variation (e.g. the language, a GET parameter).
  52. * The following variation setting will cause the content to be cached in different versions
  53. * according to the current application language:
  54. *
  55. * ~~~
  56. * [
  57. * Yii::$app->language,
  58. * ]
  59. */
  60. public $variations;
  61. /**
  62. * @var boolean whether to enable the fragment cache. You may use this property to turn on and off
  63. * the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests).
  64. */
  65. public $enabled = true;
  66. /**
  67. * @var array a list of placeholders for embedding dynamic contents. This property
  68. * is used internally to implement the content caching feature. Do not modify it.
  69. */
  70. public $dynamicPlaceholders;
  71. /**
  72. * Initializes the FragmentCache object.
  73. */
  74. public function init()
  75. {
  76. parent::init();
  77. if (!$this->enabled) {
  78. $this->cache = null;
  79. } elseif (is_string($this->cache)) {
  80. $this->cache = Yii::$app->getComponent($this->cache);
  81. }
  82. if ($this->getCachedContent() === false) {
  83. $this->getView()->cacheStack[] = $this;
  84. ob_start();
  85. ob_implicit_flush(false);
  86. }
  87. }
  88. /**
  89. * Marks the end of content to be cached.
  90. * Content displayed before this method call and after [[init()]]
  91. * will be captured and saved in cache.
  92. * This method does nothing if valid content is already found in cache.
  93. */
  94. public function run()
  95. {
  96. if (($content = $this->getCachedContent()) !== false) {
  97. echo $content;
  98. } elseif ($this->cache instanceof Cache) {
  99. $content = ob_get_clean();
  100. array_pop($this->getView()->cacheStack);
  101. if (is_array($this->dependency)) {
  102. $this->dependency = Yii::createObject($this->dependency);
  103. }
  104. $data = [$content, $this->dynamicPlaceholders];
  105. $this->cache->set($this->calculateKey(), $data, $this->duration, $this->dependency);
  106. if (empty($this->getView()->cacheStack) && !empty($this->dynamicPlaceholders)) {
  107. $content = $this->updateDynamicContent($content, $this->dynamicPlaceholders);
  108. }
  109. echo $content;
  110. }
  111. }
  112. /**
  113. * @var string|boolean the cached content. False if the content is not cached.
  114. */
  115. private $_content;
  116. /**
  117. * Returns the cached content if available.
  118. * @return string|boolean the cached content. False is returned if valid content is not found in the cache.
  119. */
  120. public function getCachedContent()
  121. {
  122. if ($this->_content === null) {
  123. $this->_content = false;
  124. if ($this->cache instanceof Cache) {
  125. $key = $this->calculateKey();
  126. $data = $this->cache->get($key);
  127. if (is_array($data) && count($data) === 2) {
  128. list ($content, $placeholders) = $data;
  129. if (is_array($placeholders) && count($placeholders) > 0) {
  130. if (empty($this->getView()->cacheStack)) {
  131. // outermost cache: replace placeholder with dynamic content
  132. $content = $this->updateDynamicContent($content, $placeholders);
  133. }
  134. foreach ($placeholders as $name => $statements) {
  135. $this->getView()->addDynamicPlaceholder($name, $statements);
  136. }
  137. }
  138. $this->_content = $content;
  139. }
  140. }
  141. }
  142. return $this->_content;
  143. }
  144. protected function updateDynamicContent($content, $placeholders)
  145. {
  146. foreach ($placeholders as $name => $statements) {
  147. $placeholders[$name] = $this->getView()->evaluateDynamicContent($statements);
  148. }
  149. return strtr($content, $placeholders);
  150. }
  151. /**
  152. * Generates a unique key used for storing the content in cache.
  153. * The key generated depends on both [[id]] and [[variations]].
  154. * @return mixed a valid cache key
  155. */
  156. protected function calculateKey()
  157. {
  158. $factors = [__CLASS__, $this->getId()];
  159. if (is_array($this->variations)) {
  160. foreach ($this->variations as $factor) {
  161. $factors[] = $factor;
  162. }
  163. }
  164. return $factors;
  165. }
  166. }