You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 line
6.5KB

  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. use yii\di\Instance;
  13. /**
  14. * FragmentCache is used by [[\yii\base\View]] to provide caching of page fragments.
  15. *
  16. * @property string|boolean $cachedContent The cached content. False is returned if valid content is not found
  17. * in the cache. This property is read-only.
  18. *
  19. * @author Qiang Xue <qiang.xue@gmail.com>
  20. * @since 2.0
  21. */
  22. class FragmentCache extends Widget
  23. {
  24. /**
  25. * @var Cache|array|string the cache object or the application component ID of the cache object.
  26. * After the FragmentCache object is created, if you want to change this property,
  27. * you should only assign it with a cache object.
  28. * Starting from version 2.0.2, this can also be a configuration array for creating the object.
  29. */
  30. public $cache = 'cache';
  31. /**
  32. * @var integer number of seconds that the data can remain valid in cache.
  33. * Use 0 to indicate that the cached data will never expire.
  34. */
  35. public $duration = 60;
  36. /**
  37. * @var array|Dependency the dependency that the cached content depends on.
  38. * This can be either a [[Dependency]] object or a configuration array for creating the dependency object.
  39. * For example,
  40. *
  41. * ```php
  42. * [
  43. * 'class' => 'yii\caching\DbDependency',
  44. * 'sql' => 'SELECT MAX(updated_at) FROM post',
  45. * ]
  46. * ```
  47. *
  48. * would make the output cache depends on the last modified time of all posts.
  49. * If any post has its modification time changed, the cached content would be invalidated.
  50. */
  51. public $dependency;
  52. /**
  53. * @var array list of factors that would cause the variation of the content being cached.
  54. * Each factor is a string representing a variation (e.g. the language, a GET parameter).
  55. * The following variation setting will cause the content to be cached in different versions
  56. * according to the current application language:
  57. *
  58. * ```php
  59. * [
  60. * Yii::$app->language,
  61. * ]
  62. * ```
  63. */
  64. public $variations;
  65. /**
  66. * @var boolean whether to enable the fragment cache. You may use this property to turn on and off
  67. * the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests).
  68. */
  69. public $enabled = true;
  70. /**
  71. * @var array a list of placeholders for embedding dynamic contents. This property
  72. * is used internally to implement the content caching feature. Do not modify it.
  73. */
  74. public $dynamicPlaceholders;
  75. /**
  76. * Initializes the FragmentCache object.
  77. */
  78. public function init()
  79. {
  80. parent::init();
  81. $this->cache = $this->enabled ? Instance::ensure($this->cache, Cache::className()) : null;
  82. if ($this->cache instanceof Cache && $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. array_pop($this->getView()->cacheStack);
  100. $content = ob_get_clean();
  101. if ($content === false || $content === '') {
  102. return;
  103. }
  104. if (is_array($this->dependency)) {
  105. $this->dependency = Yii::createObject($this->dependency);
  106. }
  107. $data = [$content, $this->dynamicPlaceholders];
  108. $this->cache->set($this->calculateKey(), $data, $this->duration, $this->dependency);
  109. if (empty($this->getView()->cacheStack) && !empty($this->dynamicPlaceholders)) {
  110. $content = $this->updateDynamicContent($content, $this->dynamicPlaceholders);
  111. }
  112. echo $content;
  113. }
  114. }
  115. /**
  116. * @var string|boolean the cached content. False if the content is not cached.
  117. */
  118. private $_content;
  119. /**
  120. * Returns the cached content if available.
  121. * @return string|boolean the cached content. False is returned if valid content is not found in the cache.
  122. */
  123. public function getCachedContent()
  124. {
  125. if ($this->_content === null) {
  126. $this->_content = false;
  127. if ($this->cache instanceof Cache) {
  128. $key = $this->calculateKey();
  129. $data = $this->cache->get($key);
  130. if (is_array($data) && count($data) === 2) {
  131. list ($content, $placeholders) = $data;
  132. if (is_array($placeholders) && count($placeholders) > 0) {
  133. if (empty($this->getView()->cacheStack)) {
  134. // outermost cache: replace placeholder with dynamic content
  135. $content = $this->updateDynamicContent($content, $placeholders);
  136. }
  137. foreach ($placeholders as $name => $statements) {
  138. $this->getView()->addDynamicPlaceholder($name, $statements);
  139. }
  140. }
  141. $this->_content = $content;
  142. }
  143. }
  144. }
  145. return $this->_content;
  146. }
  147. /**
  148. * Replaces placeholders in content by results of evaluated dynamic statements.
  149. *
  150. * @param string $content
  151. * @param array $placeholders
  152. * @return string final content
  153. */
  154. protected function updateDynamicContent($content, $placeholders)
  155. {
  156. foreach ($placeholders as $name => $statements) {
  157. $placeholders[$name] = $this->getView()->evaluateDynamicContent($statements);
  158. }
  159. return strtr($content, $placeholders);
  160. }
  161. /**
  162. * Generates a unique key used for storing the content in cache.
  163. * The key generated depends on both [[id]] and [[variations]].
  164. * @return mixed a valid cache key
  165. */
  166. protected function calculateKey()
  167. {
  168. $factors = [__CLASS__, $this->getId()];
  169. if (is_array($this->variations)) {
  170. foreach ($this->variations as $factor) {
  171. $factors[] = $factor;
  172. }
  173. }
  174. return $factors;
  175. }
  176. }