CacheableWidgetBehavior.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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\behaviors;
  8. use yii\base\Behavior;
  9. use yii\base\InvalidConfigException;
  10. use yii\base\Widget;
  11. use yii\base\WidgetEvent;
  12. use yii\caching\CacheInterface;
  13. use yii\caching\Dependency;
  14. use yii\di\Instance;
  15. /**
  16. * Cacheable widget behavior automatically caches widget contents according to duration and dependencies specified.
  17. *
  18. * The behavior may be used without any configuration if an application has `cache` component configured.
  19. * By default the widget will be cached for one minute.
  20. *
  21. * The following example will cache the posts widget for an indefinite duration until any post is modified.
  22. *
  23. * ```php
  24. * use yii\behaviors\CacheableWidgetBehavior;
  25. *
  26. * public function behaviors()
  27. * {
  28. * return [
  29. * [
  30. * 'class' => CacheableWidgetBehavior::className(),
  31. * 'cacheDuration' => 0,
  32. * 'cacheDependency' => [
  33. * 'class' => 'yii\caching\DbDependency',
  34. * 'sql' => 'SELECT MAX(updated_at) FROM posts',
  35. * ],
  36. * ],
  37. * ];
  38. * }
  39. * ```
  40. *
  41. * @author Nikolay Oleynikov <oleynikovny@mail.ru>
  42. * @since 2.0.14
  43. */
  44. class CacheableWidgetBehavior extends Behavior
  45. {
  46. /**
  47. * @var CacheInterface|string|array a cache object or a cache component ID
  48. * or a configuration array for creating a cache object.
  49. * Defaults to the `cache` application component.
  50. */
  51. public $cache = 'cache';
  52. /**
  53. * @var int cache duration in seconds.
  54. * Set to `0` to indicate that the cached data will never expire.
  55. * Defaults to 60 seconds or 1 minute.
  56. */
  57. public $cacheDuration = 60;
  58. /**
  59. * @var Dependency|array|null a cache dependency or a configuration array
  60. * for creating a cache dependency or `null` meaning no cache dependency.
  61. *
  62. * For example,
  63. *
  64. * ```php
  65. * [
  66. * 'class' => 'yii\caching\DbDependency',
  67. * 'sql' => 'SELECT MAX(updated_at) FROM posts',
  68. * ]
  69. * ```
  70. *
  71. * would make the widget cache depend on the last modified time of all posts.
  72. * If any post has its modification time changed, the cached content would be invalidated.
  73. */
  74. public $cacheDependency;
  75. /**
  76. * @var string[]|string an array of strings or a single string which would cause
  77. * the variation of the content being cached (e.g. an application language, a GET parameter).
  78. *
  79. * The following variation setting will cause the content to be cached in different versions
  80. * according to the current application language:
  81. *
  82. * ```php
  83. * [
  84. * Yii::$app->language,
  85. * ]
  86. * ```
  87. */
  88. public $cacheKeyVariations = [];
  89. /**
  90. * @var bool whether to enable caching or not. Allows to turn the widget caching
  91. * on and off according to specific conditions.
  92. * The following configuration will disable caching when a special GET parameter is passed:
  93. *
  94. * ```php
  95. * empty(Yii::$app->request->get('disable-caching'))
  96. * ```
  97. */
  98. public $cacheEnabled = true;
  99. /**
  100. * {@inheritdoc}
  101. */
  102. public function attach($owner)
  103. {
  104. parent::attach($owner);
  105. $this->initializeEventHandlers();
  106. }
  107. /**
  108. * Begins fragment caching. Prevents owner widget from execution
  109. * if its contents can be retrieved from the cache.
  110. *
  111. * @param WidgetEvent $event `Widget::EVENT_BEFORE_RUN` event.
  112. */
  113. public function beforeRun($event)
  114. {
  115. $cacheKey = $this->getCacheKey();
  116. $fragmentCacheConfiguration = $this->getFragmentCacheConfiguration();
  117. if (!$this->owner->view->beginCache($cacheKey, $fragmentCacheConfiguration)) {
  118. $event->isValid = false;
  119. }
  120. }
  121. /**
  122. * Outputs widget contents and ends fragment caching.
  123. *
  124. * @param WidgetEvent $event `Widget::EVENT_AFTER_RUN` event.
  125. */
  126. public function afterRun($event)
  127. {
  128. echo $event->result;
  129. $event->result = null;
  130. $this->owner->view->endCache();
  131. }
  132. /**
  133. * Initializes widget event handlers.
  134. */
  135. private function initializeEventHandlers()
  136. {
  137. $this->owner->on(Widget::EVENT_BEFORE_RUN, [$this, 'beforeRun']);
  138. $this->owner->on(Widget::EVENT_AFTER_RUN, [$this, 'afterRun']);
  139. }
  140. /**
  141. * Returns the cache instance.
  142. *
  143. * @return CacheInterface cache instance.
  144. * @throws InvalidConfigException if cache instance instantiation fails.
  145. */
  146. private function getCacheInstance()
  147. {
  148. $cacheInterface = 'yii\caching\CacheInterface';
  149. return Instance::ensure($this->cache, $cacheInterface);
  150. }
  151. /**
  152. * Returns the widget cache key.
  153. *
  154. * @return string[] an array of strings representing the cache key.
  155. */
  156. private function getCacheKey()
  157. {
  158. // `$cacheKeyVariations` may be a `string` and needs to be cast to an `array`.
  159. $cacheKey = array_merge(
  160. (array)get_class($this->owner),
  161. (array)$this->cacheKeyVariations
  162. );
  163. return $cacheKey;
  164. }
  165. /**
  166. * Returns a fragment cache widget configuration array.
  167. *
  168. * @return array a fragment cache widget configuration array.
  169. */
  170. private function getFragmentCacheConfiguration()
  171. {
  172. $cache = $this->getCacheInstance();
  173. $fragmentCacheConfiguration = [
  174. 'cache' => $cache,
  175. 'duration' => $this->cacheDuration,
  176. 'dependency' => $this->cacheDependency,
  177. 'enabled' => $this->cacheEnabled,
  178. ];
  179. return $fragmentCacheConfiguration;
  180. }
  181. }