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