1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Charcoal\Admin\Template\System; |
4
|
|
|
|
5
|
|
|
use APCUIterator; |
6
|
|
|
use APCIterator; |
7
|
|
|
use RuntimeException; |
8
|
|
|
|
9
|
|
|
use Stash\Driver\Apc; |
10
|
|
|
use Stash\Driver\Memcache; |
11
|
|
|
|
12
|
|
|
use Pimple\Container; |
13
|
|
|
|
14
|
|
|
// From 'charcoal-admin' |
15
|
|
|
use Charcoal\Admin\AdminTemplate; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Cache information. |
19
|
|
|
*/ |
20
|
|
|
class ClearCacheTemplate extends AdminTemplate |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* Cache service. |
24
|
|
|
* |
25
|
|
|
* @var \Stash\Pool |
26
|
|
|
*/ |
27
|
|
|
private $cache; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Summary of cache. |
31
|
|
|
* |
32
|
|
|
* @var array |
33
|
|
|
*/ |
34
|
|
|
private $cacheInfo; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Cache service config. |
38
|
|
|
* |
39
|
|
|
* @var \Charcoal\App\Config\CacheConfig |
40
|
|
|
*/ |
41
|
|
|
private $cacheConfig; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Driver Name => Class Name. |
45
|
|
|
* |
46
|
|
|
* @var \Stash\Interfaces\DriverInterface |
47
|
|
|
*/ |
48
|
|
|
private $cacheDriver; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Driver Name => Class Name. |
52
|
|
|
* |
53
|
|
|
* @var array |
54
|
|
|
*/ |
55
|
|
|
private $availableCacheDrivers; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Retrieve the title of the page. |
59
|
|
|
* |
60
|
|
|
* @return \Charcoal\Translator\Translation|string|null |
61
|
|
|
*/ |
62
|
|
|
public function title() |
63
|
|
|
{ |
64
|
|
|
if ($this->title === null) { |
65
|
|
|
$this->setTitle($this->translator()->translation('Cache information')); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
return $this->title; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @return \Charcoal\Admin\Widget\SidemenuWidgetInterface|null |
73
|
|
|
*/ |
74
|
|
|
public function sidemenu() |
75
|
|
|
{ |
76
|
|
|
if ($this->sidemenu === null) { |
77
|
|
|
$this->sidemenu = $this->createSidemenu('system'); |
|
|
|
|
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
return $this->sidemenu; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param boolean $force Whether to reload cache information. |
85
|
|
|
* @return array |
86
|
|
|
*/ |
87
|
|
|
public function cacheInfo($force = false) |
88
|
|
|
{ |
89
|
|
|
if ($this->cacheInfo === null || $force === true) { |
90
|
|
|
$flip = array_flip($this->availableCacheDrivers); |
91
|
|
|
$driver = get_class($this->cache->getDriver()); |
92
|
|
|
$cacheType = isset($flip['\\'.$driver]) ? $flip['\\'.$driver] : $driver; |
93
|
|
|
|
94
|
|
|
# $globalItems = $this->globalCacheItems(); |
95
|
|
|
$pageItems = $this->pagesCacheItems(); |
96
|
|
|
$objectItems = $this->objectsCacheItems(); |
97
|
|
|
$this->cacheInfo = [ |
98
|
|
|
'type' => $cacheType, |
99
|
|
|
'active' => $this->cacheConfig['active'], |
100
|
|
|
'global' => $this->globalCacheInfo(), |
101
|
|
|
'pages' => $this->pagesCacheInfo(), |
102
|
|
|
'objects' => $this->objectsCacheInfo(), |
103
|
|
|
# 'global_items' => $globalItems, |
104
|
|
|
'pages_items' => $pageItems, |
105
|
|
|
'objects_items' => $objectItems, |
106
|
|
|
# 'num_global_items' => count($globalItems), |
107
|
|
|
'num_pages_items' => count($pageItems), |
108
|
|
|
'num_objects_items' => count($objectItems), |
109
|
|
|
]; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
return $this->cacheInfo; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* @return string |
117
|
|
|
*/ |
118
|
|
|
private function getGlobalCacheKey() |
119
|
|
|
{ |
120
|
|
|
return '/::'.$this->cache->getNamespace().'::/'; |
|
|
|
|
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* @return array |
125
|
|
|
*/ |
126
|
|
|
private function globalCacheInfo() |
127
|
|
|
{ |
128
|
|
|
if ($this->isApc()) { |
129
|
|
|
$cacheKey = $this->getGlobalCacheKey(); |
130
|
|
|
return $this->apcCacheInfo($cacheKey); |
131
|
|
|
} else { |
132
|
|
|
return [ |
133
|
|
|
'num_entries' => 0, |
134
|
|
|
'total_size' => 0, |
135
|
|
|
'average_size' => 0, |
136
|
|
|
'total_hits' => 0, |
137
|
|
|
'average_hits' => 0, |
138
|
|
|
]; |
139
|
|
|
} |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @return array |
144
|
|
|
*/ |
145
|
|
|
private function globalCacheItems() |
|
|
|
|
146
|
|
|
{ |
147
|
|
|
if ($this->isApc()) { |
148
|
|
|
$cacheKey = $this->getGlobalCacheKey(); |
149
|
|
|
return $this->apcCacheItems($cacheKey); |
|
|
|
|
150
|
|
|
} else { |
151
|
|
|
return []; |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* @return string |
157
|
|
|
*/ |
158
|
|
|
private function getPagesCacheKey() |
159
|
|
|
{ |
160
|
|
|
return '/::'.$this->cache->getNamespace().'::request::|::'.$this->cache->getNamespace().'::template::/'; |
|
|
|
|
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @return array |
165
|
|
|
*/ |
166
|
|
|
private function pagesCacheInfo() |
167
|
|
|
{ |
168
|
|
|
if ($this->isApc()) { |
169
|
|
|
$cacheKey = $this->getPagesCacheKey(); |
170
|
|
|
return $this->apcCacheInfo($cacheKey); |
171
|
|
|
} else { |
172
|
|
|
return [ |
173
|
|
|
'num_entries' => 0, |
174
|
|
|
'total_size' => 0, |
175
|
|
|
'average_size' => 0, |
176
|
|
|
'total_hits' => 0, |
177
|
|
|
'average_hits' => 0, |
178
|
|
|
]; |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* @return array |
184
|
|
|
*/ |
185
|
|
|
private function pagesCacheItems() |
186
|
|
|
{ |
187
|
|
|
if ($this->isApc()) { |
188
|
|
|
$cacheKey = $this->getPagesCacheKey(); |
189
|
|
|
return $this->apcCacheItems($cacheKey); |
|
|
|
|
190
|
|
|
} else { |
191
|
|
|
return []; |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* @return string |
197
|
|
|
*/ |
198
|
|
|
private function getObjectsCacheKey() |
199
|
|
|
{ |
200
|
|
|
return '/::'.$this->cache->getNamespace().'::object::|::'.$this->cache->getNamespace().'::metadata::/'; |
|
|
|
|
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* @return array |
205
|
|
|
*/ |
206
|
|
|
private function objectsCacheInfo() |
207
|
|
|
{ |
208
|
|
|
if ($this->isApc()) { |
209
|
|
|
$cacheKey = $this->getObjectsCacheKey(); |
210
|
|
|
return $this->apcCacheInfo($cacheKey); |
211
|
|
|
} else { |
212
|
|
|
return [ |
213
|
|
|
'num_entries' => 0, |
214
|
|
|
'total_size' => 0, |
215
|
|
|
'average_size' => 0, |
216
|
|
|
'total_hits' => 0, |
217
|
|
|
'average_hits' => 0, |
218
|
|
|
]; |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* @return array |
224
|
|
|
*/ |
225
|
|
|
private function objectsCacheItems() |
226
|
|
|
{ |
227
|
|
|
if ($this->isApc()) { |
228
|
|
|
$cacheKey = $this->getObjectsCacheKey(); |
229
|
|
|
return $this->apcCacheItems($cacheKey); |
|
|
|
|
230
|
|
|
} else { |
231
|
|
|
return []; |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* @param string $key The cache key to look at. |
237
|
|
|
* @return array |
238
|
|
|
*/ |
239
|
|
|
private function apcCacheInfo($key) |
240
|
|
|
{ |
241
|
|
|
$iter = $this->createApcIterator($key); |
242
|
|
|
|
243
|
|
|
$numEntries = 0; |
244
|
|
|
$sizeTotal = 0; |
245
|
|
|
$hitsTotal = 0; |
246
|
|
|
$ttlTotal = 0; |
247
|
|
|
foreach ($iter as $item) { |
248
|
|
|
$numEntries++; |
249
|
|
|
$sizeTotal += $item['mem_size']; |
250
|
|
|
$hitsTotal += $item['num_hits']; |
251
|
|
|
$ttlTotal += $item['ttl']; |
252
|
|
|
} |
253
|
|
|
$sizeAvg = $numEntries ? ($sizeTotal / $numEntries) : 0; |
254
|
|
|
$hitsAvg = $numEntries ? ($hitsTotal / $numEntries) : 0; |
255
|
|
|
return [ |
256
|
|
|
'num_entries' => $numEntries, |
257
|
|
|
'total_size' => $this->formatBytes($sizeTotal), |
258
|
|
|
'average_size' => $this->formatBytes($sizeAvg), |
259
|
|
|
'total_hits' => $hitsTotal, |
260
|
|
|
'average_hits' => $hitsAvg, |
261
|
|
|
]; |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* @param string $key The cache key to look at. |
266
|
|
|
* @return array|\Generator |
267
|
|
|
*/ |
268
|
|
|
private function apcCacheItems($key) |
269
|
|
|
{ |
270
|
|
|
$iter = $this->createApcIterator($key); |
271
|
|
|
|
272
|
|
|
foreach ($iter as $item) { |
273
|
|
|
$item['ident'] = $this->formatApcCacheKey($item['key']); |
274
|
|
|
$item['size'] = $this->formatBytes($item['mem_size']); |
275
|
|
|
$item['created'] = date('Y-m-d H:i:s', $item['creation_time']); |
276
|
|
|
$item['expiry'] = date('Y-m-d H:i:s', ($item['creation_time']+$item['ttl'])); |
277
|
|
|
yield $item; |
278
|
|
|
} |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* @param string $key The cache item key to load. |
283
|
|
|
* @throws RuntimeException If the APC Iterator class is missing. |
284
|
|
|
* @return \APCIterator|\APCUIterator|null |
285
|
|
|
*/ |
286
|
|
|
private function createApcIterator($key) |
287
|
|
|
{ |
288
|
|
|
if (class_exists('\\APCUIterator', false)) { |
289
|
|
|
return new \APCUIterator($key); |
290
|
|
|
} elseif (class_exists('\\APCIterator', false)) { |
291
|
|
|
return new \APCIterator('user', $key); |
292
|
|
|
} else { |
293
|
|
|
throw new RuntimeException('Cache uses APC but no iterator could be found.'); |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* @return boolean |
299
|
|
|
*/ |
300
|
|
|
private function isApc() |
301
|
|
|
{ |
302
|
|
|
return is_a($this->cache->getDriver(), Apc::class); |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* @return boolean |
307
|
|
|
*/ |
308
|
|
|
private function isMemcache() |
|
|
|
|
309
|
|
|
{ |
310
|
|
|
return is_a($this->cache->getDriver(), Memcache::class); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* Human-readable identifier format. |
315
|
|
|
* |
316
|
|
|
* @param string $key The cache item key to format. |
317
|
|
|
* @return string |
318
|
|
|
*/ |
319
|
|
|
private function formatApcCacheKey($key) |
320
|
|
|
{ |
321
|
|
|
$nss = $this->cache->getNamespace(); |
322
|
|
|
$key = str_replace($nss, '', strstr($key, $nss.'::')); |
|
|
|
|
323
|
|
|
$key = preg_replace([ '/:+/', '/\.+/' ], [ '⇒', '/' ], trim($key, ':')); |
324
|
|
|
return $key; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Human-readable bytes format. |
329
|
|
|
* |
330
|
|
|
* @param integer $bytes The number of bytes to format. |
331
|
|
|
* @return string |
332
|
|
|
*/ |
333
|
|
|
private function formatBytes($bytes) |
334
|
|
|
{ |
335
|
|
|
if ($bytes === 0) { |
336
|
|
|
return 0; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
$units = [ 'B', 'KB', 'MB', 'GB', 'TB' ]; |
340
|
|
|
$base = log($bytes, 1024); |
341
|
|
|
$floor = floor($base); |
342
|
|
|
$unit = $units[$floor]; |
343
|
|
|
$size = round(pow(1024, ($base - $floor)), 2); |
344
|
|
|
|
345
|
|
|
$locale = localeconv(); |
346
|
|
|
$size = number_format($size, 2, $locale['decimal_point'], $locale['thousands_sep']); |
347
|
|
|
|
348
|
|
|
return rtrim($size, '.0').' '.$unit; |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* @param Container $container Pimple DI Container. |
353
|
|
|
* @return void |
354
|
|
|
*/ |
355
|
|
|
protected function setDependencies(Container $container) |
356
|
|
|
{ |
357
|
|
|
parent::setDependencies($container); |
358
|
|
|
|
359
|
|
|
$this->availableCacheDrivers = $container['cache/available-drivers']; |
360
|
|
|
$this->cacheDriver = $container['cache/driver']; |
361
|
|
|
$this->cache = $container['cache']; |
362
|
|
|
$this->cacheConfig = $container['cache/config']; |
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.