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\caching; |
||
9 | |||
10 | use Yii; |
||
11 | use yii\base\InvalidConfigException; |
||
12 | use yii\db\Connection; |
||
13 | use yii\db\PdoValue; |
||
14 | use yii\db\Query; |
||
15 | use yii\di\Instance; |
||
16 | |||
17 | /** |
||
18 | * DbCache implements a cache application component by storing cached data in a database. |
||
19 | * |
||
20 | * By default, DbCache stores session data in a DB table named 'cache'. This table |
||
21 | * must be pre-created. The table name can be changed by setting [[cacheTable]]. |
||
22 | * |
||
23 | * Please refer to [[Cache]] for common cache operations that are supported by DbCache. |
||
24 | * |
||
25 | * The following example shows how you can configure the application to use DbCache: |
||
26 | * |
||
27 | * ```php |
||
28 | * 'cache' => [ |
||
29 | * 'class' => 'yii\caching\DbCache', |
||
30 | * // 'db' => 'mydb', |
||
31 | * // 'cacheTable' => 'my_cache', |
||
32 | * ] |
||
33 | * ``` |
||
34 | * |
||
35 | * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview). |
||
36 | * |
||
37 | * @author Qiang Xue <[email protected]> |
||
38 | * @since 2.0 |
||
39 | */ |
||
40 | class DbCache extends Cache |
||
41 | { |
||
42 | /** |
||
43 | * @var Connection|array|string the DB connection object or the application component ID of the DB connection. |
||
44 | * After the DbCache object is created, if you want to change this property, you should only assign it |
||
45 | * with a DB connection object. |
||
46 | * Starting from version 2.0.2, this can also be a configuration array for creating the object. |
||
47 | */ |
||
48 | public $db = 'db'; |
||
49 | /** |
||
50 | * @var string name of the DB table to store cache content. |
||
51 | * The table should be pre-created as follows: |
||
52 | * |
||
53 | * ```php |
||
54 | * CREATE TABLE cache ( |
||
55 | * id char(128) NOT NULL PRIMARY KEY, |
||
56 | * expire int(11), |
||
57 | * data BLOB |
||
58 | * ); |
||
59 | * ``` |
||
60 | * |
||
61 | * where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB type |
||
62 | * that can be used for some popular DBMS: |
||
63 | * |
||
64 | * - MySQL: LONGBLOB |
||
65 | * - PostgreSQL: BYTEA |
||
66 | * - MSSQL: BLOB |
||
67 | * |
||
68 | * When using DbCache in a production server, we recommend you create a DB index for the 'expire' |
||
69 | * column in the cache table to improve the performance. |
||
70 | */ |
||
71 | public $cacheTable = '{{%cache}}'; |
||
72 | /** |
||
73 | * @var int the probability (parts per million) that garbage collection (GC) should be performed |
||
74 | * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance. |
||
75 | * This number should be between 0 and 1000000. A value 0 meaning no GC will be performed at all. |
||
76 | */ |
||
77 | public $gcProbability = 100; |
||
78 | |||
79 | |||
80 | /** |
||
81 | * Initializes the DbCache component. |
||
82 | * This method will initialize the [[db]] property to make sure it refers to a valid DB connection. |
||
83 | * @throws InvalidConfigException if [[db]] is invalid. |
||
84 | */ |
||
85 | 39 | public function init() |
|
86 | { |
||
87 | 39 | parent::init(); |
|
88 | 39 | $this->db = Instance::ensure($this->db, Connection::className()); |
|
0 ignored issues
–
show
|
|||
89 | 39 | } |
|
90 | |||
91 | /** |
||
92 | * Checks whether a specified key exists in the cache. |
||
93 | * This can be faster than getting the value from the cache if the data is big. |
||
94 | * Note that this method does not check whether the dependency associated |
||
95 | * with the cached data, if there is any, has changed. So a call to [[get]] |
||
96 | * may return false while exists returns true. |
||
97 | * @param mixed $key a key identifying the cached value. This can be a simple string or |
||
98 | * a complex data structure consisting of factors representing the key. |
||
99 | * @return bool true if a value exists in cache, false if the value is not in the cache or expired. |
||
100 | */ |
||
101 | 2 | public function exists($key) |
|
102 | { |
||
103 | 2 | $key = $this->buildKey($key); |
|
104 | |||
105 | 2 | $query = new Query(); |
|
106 | 2 | $query->select(['COUNT(*)']) |
|
107 | 2 | ->from($this->cacheTable) |
|
108 | 2 | ->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', [':id' => $key]); |
|
109 | 2 | if ($this->db->enableQueryCache) { |
|
110 | // temporarily disable and re-enable query caching |
||
111 | 2 | $this->db->enableQueryCache = false; |
|
112 | 2 | $result = $query->createCommand($this->db)->queryScalar(); |
|
113 | 2 | $this->db->enableQueryCache = true; |
|
114 | } else { |
||
115 | $result = $query->createCommand($this->db)->queryScalar(); |
||
116 | } |
||
117 | |||
118 | 2 | return $result > 0; |
|
119 | } |
||
120 | |||
121 | /** |
||
122 | * Retrieves a value from cache with a specified key. |
||
123 | * This is the implementation of the method declared in the parent class. |
||
124 | * @param string $key a unique key identifying the cached value |
||
125 | * @return string|false the value stored in cache, false if the value is not in the cache or expired. |
||
126 | */ |
||
127 | 32 | protected function getValue($key) |
|
128 | { |
||
129 | 32 | $query = new Query(); |
|
130 | 32 | $query->select(['data']) |
|
131 | 32 | ->from($this->cacheTable) |
|
132 | 32 | ->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', [':id' => $key]); |
|
133 | 32 | if ($this->db->enableQueryCache) { |
|
134 | // temporarily disable and re-enable query caching |
||
135 | 32 | $this->db->enableQueryCache = false; |
|
136 | 32 | $result = $query->createCommand($this->db)->queryScalar(); |
|
137 | 32 | $this->db->enableQueryCache = true; |
|
138 | |||
139 | 32 | return $result; |
|
140 | } |
||
141 | |||
142 | return $query->createCommand($this->db)->queryScalar(); |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * Retrieves multiple values from cache with the specified keys. |
||
147 | * @param array $keys a list of keys identifying the cached values |
||
148 | * @return array a list of cached values indexed by the keys |
||
149 | */ |
||
150 | 4 | protected function getValues($keys) |
|
151 | { |
||
152 | 4 | if (empty($keys)) { |
|
153 | return []; |
||
154 | } |
||
155 | 4 | $query = new Query(); |
|
156 | 4 | $query->select(['id', 'data']) |
|
157 | 4 | ->from($this->cacheTable) |
|
158 | 4 | ->where(['id' => $keys]) |
|
159 | 4 | ->andWhere('([[expire]] = 0 OR [[expire]] > ' . time() . ')'); |
|
160 | |||
161 | 4 | if ($this->db->enableQueryCache) { |
|
162 | 4 | $this->db->enableQueryCache = false; |
|
163 | 4 | $rows = $query->createCommand($this->db)->queryAll(); |
|
164 | 4 | $this->db->enableQueryCache = true; |
|
165 | } else { |
||
166 | $rows = $query->createCommand($this->db)->queryAll(); |
||
167 | } |
||
168 | |||
169 | 4 | $results = []; |
|
170 | 4 | foreach ($keys as $key) { |
|
171 | 4 | $results[$key] = false; |
|
172 | } |
||
173 | 4 | foreach ($rows as $row) { |
|
174 | 4 | if (is_resource($row['data']) && get_resource_type($row['data']) === 'stream') { |
|
175 | 2 | $results[$row['id']] = stream_get_contents($row['data']); |
|
176 | } else { |
||
177 | 4 | $results[$row['id']] = $row['data']; |
|
178 | } |
||
179 | } |
||
180 | |||
181 | 4 | return $results; |
|
182 | } |
||
183 | |||
184 | /** |
||
185 | * Stores a value identified by a key in cache. |
||
186 | * This is the implementation of the method declared in the parent class. |
||
187 | * |
||
188 | * @param string $key the key identifying the value to be cached |
||
189 | * @param string $value the value to be cached. Other types (if you have disabled [[serializer]]) cannot be saved. |
||
190 | * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire. |
||
191 | * @return bool true if the value is successfully stored into cache, false otherwise |
||
192 | */ |
||
193 | 32 | protected function setValue($key, $value, $duration) |
|
194 | { |
||
195 | try { |
||
196 | $this->db->noCache(function (Connection $db) use ($key, $value, $duration) { |
||
197 | 32 | $db->createCommand()->upsert($this->cacheTable, [ |
|
198 | 32 | 'id' => $key, |
|
199 | 32 | 'expire' => $duration > 0 ? $duration + time() : 0, |
|
200 | 32 | 'data' => new PdoValue($value, \PDO::PARAM_LOB), |
|
201 | 32 | ])->execute(); |
|
202 | 32 | }); |
|
203 | |||
204 | 32 | $this->gc(); |
|
205 | |||
206 | 32 | return true; |
|
207 | } catch (\Exception $e) { |
||
208 | Yii::warning("Unable to update or insert cache data: {$e->getMessage()}", __METHOD__); |
||
209 | |||
210 | return false; |
||
211 | } |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * Stores a value identified by a key into cache if the cache does not contain this key. |
||
216 | * This is the implementation of the method declared in the parent class. |
||
217 | * |
||
218 | * @param string $key the key identifying the value to be cached |
||
219 | * @param string $value the value to be cached. Other types (if you have disabled [[serializer]]) cannot be saved. |
||
220 | * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire. |
||
221 | * @return bool true if the value is successfully stored into cache, false otherwise |
||
222 | */ |
||
223 | 6 | protected function addValue($key, $value, $duration) |
|
224 | { |
||
225 | 6 | $this->gc(); |
|
226 | |||
227 | try { |
||
228 | $this->db->noCache(function (Connection $db) use ($key, $value, $duration) { |
||
229 | 6 | $db->createCommand() |
|
230 | 6 | ->insert($this->cacheTable, [ |
|
231 | 6 | 'id' => $key, |
|
232 | 6 | 'expire' => $duration > 0 ? $duration + time() : 0, |
|
233 | 6 | 'data' => new PdoValue($value, \PDO::PARAM_LOB), |
|
234 | 6 | ])->execute(); |
|
235 | 6 | }); |
|
236 | |||
237 | 6 | return true; |
|
238 | 4 | } catch (\Exception $e) { |
|
239 | 4 | Yii::warning("Unable to insert cache data: {$e->getMessage()}", __METHOD__); |
|
240 | |||
241 | 4 | return false; |
|
242 | } |
||
243 | } |
||
244 | |||
245 | /** |
||
246 | * Deletes a value with the specified key from cache |
||
247 | * This is the implementation of the method declared in the parent class. |
||
248 | * @param string $key the key of the value to be deleted |
||
249 | * @return bool if no error happens during deletion |
||
250 | */ |
||
251 | protected function deleteValue($key) |
||
252 | { |
||
253 | 2 | $this->db->noCache(function (Connection $db) use ($key) { |
|
254 | 2 | $db->createCommand() |
|
255 | 2 | ->delete($this->cacheTable, ['id' => $key]) |
|
256 | 2 | ->execute(); |
|
257 | 2 | }); |
|
258 | |||
259 | 2 | return true; |
|
260 | } |
||
261 | |||
262 | /** |
||
263 | * Removes the expired data values. |
||
264 | * @param bool $force whether to enforce the garbage collection regardless of [[gcProbability]]. |
||
265 | * Defaults to false, meaning the actual deletion happens with the probability as specified by [[gcProbability]]. |
||
266 | */ |
||
267 | 34 | public function gc($force = false) |
|
268 | { |
||
269 | 34 | if ($force || mt_rand(0, 1000000) < $this->gcProbability) { |
|
270 | $this->db->createCommand() |
||
271 | ->delete($this->cacheTable, '[[expire]] > 0 AND [[expire]] < ' . time()) |
||
272 | ->execute(); |
||
273 | } |
||
274 | 34 | } |
|
275 | |||
276 | /** |
||
277 | * Deletes all values from cache. |
||
278 | * This is the implementation of the method declared in the parent class. |
||
279 | * @return bool whether the flush operation was successful. |
||
280 | */ |
||
281 | 22 | protected function flushValues() |
|
282 | { |
||
283 | 22 | $this->db->createCommand() |
|
284 | 22 | ->delete($this->cacheTable) |
|
285 | 22 | ->execute(); |
|
286 | |||
287 | 22 | return true; |
|
288 | } |
||
289 | } |
||
290 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.