1
|
|
|
<?php |
|
|
|
|
2
|
|
|
require_once('FindSSEnvironment.php'); |
3
|
|
|
|
4
|
|
|
|
5
|
|
|
define('ADMIN_USERNAME', SS_DEFAULT_ADMIN_USERNAME); // Admin Username |
6
|
|
|
define('ADMIN_PASSWORD', SS_DEFAULT_ADMIN_PASSWORD); // Admin Password - CHANGE THIS TO ENABLE!!! |
7
|
|
|
|
8
|
|
|
///////////////// Password protect //////////////////////////////////////////////////////////////// |
9
|
|
View Code Duplication |
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) || |
|
|
|
|
10
|
|
|
$_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME ||$_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) { |
11
|
|
|
Header("WWW-Authenticate: Basic realm=\"Memcache Login\""); |
12
|
|
|
Header("HTTP/1.0 401 Unauthorized"); |
13
|
|
|
|
14
|
|
|
echo <<<EOB |
15
|
|
|
<html><body> |
16
|
|
|
<h1>Rejected!</h1> |
17
|
|
|
<big>Wrong Username or Password!</big> |
18
|
|
|
</body></html> |
19
|
|
|
EOB; |
20
|
|
|
exit; |
21
|
|
|
} |
22
|
|
|
define('THOUSAND_SEPARATOR', true); |
23
|
|
|
|
24
|
|
|
if (!extension_loaded('Zend OPcache')) { |
25
|
|
|
echo '<div style="background-color: #F2DEDE; color: #B94A48; padding: 1em;">You do not have the Zend OPcache extension loaded, sample data is being shown instead.</div>'; |
26
|
|
|
require 'data-sample.php'; |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
class OpCacheDataModel |
|
|
|
|
30
|
|
|
{ |
31
|
|
|
private $_configuration; |
32
|
|
|
private $_status; |
33
|
|
|
private $_d3Scripts = array(); |
34
|
|
|
|
35
|
|
|
public function __construct() |
36
|
|
|
{ |
37
|
|
|
$this->_configuration = opcache_get_configuration(); |
38
|
|
|
$this->_status = opcache_get_status(); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
public function getPageTitle() |
42
|
|
|
{ |
43
|
|
|
return 'PHP ' . phpversion() . " with OpCache {$this->_configuration['version']['version']}"; |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
public function getStatusDataRows() |
47
|
|
|
{ |
48
|
|
|
$rows = array(); |
49
|
|
|
foreach ($this->_status as $key => $value) { |
50
|
|
|
if ($key === 'scripts') { |
51
|
|
|
continue; |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
if (is_array($value)) { |
55
|
|
|
foreach ($value as $k => $v) { |
|
|
|
|
56
|
|
|
if ($v === false) { |
57
|
|
|
$value = 'false'; |
58
|
|
|
} |
59
|
|
|
if ($v === true) { |
60
|
|
|
$value = 'true'; |
61
|
|
|
} |
62
|
|
|
if ($k === 'used_memory' || $k === 'free_memory' || $k === 'wasted_memory') { |
63
|
|
|
$v = $this->_size_for_humans( |
64
|
|
|
$v |
65
|
|
|
); |
66
|
|
|
} |
67
|
|
|
if ($k === 'current_wasted_percentage' || $k === 'opcache_hit_rate') { |
68
|
|
|
$v = number_format( |
69
|
|
|
$v, |
70
|
|
|
2 |
71
|
|
|
) . '%'; |
72
|
|
|
} |
73
|
|
|
if ($k === 'blacklist_miss_ratio') { |
74
|
|
|
$v = number_format($v, 2) . '%'; |
75
|
|
|
} |
76
|
|
|
if ($k === 'start_time' || $k === 'last_restart_time') { |
77
|
|
|
$v = ($v ? date(DATE_RFC822, $v) : 'never'); |
78
|
|
|
} |
79
|
|
|
if (THOUSAND_SEPARATOR === true && is_int($v)) { |
80
|
|
|
$v = number_format($v); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
$rows[] = "<tr><th>$k</th><td>$v</td></tr>\n"; |
84
|
|
|
} |
85
|
|
|
continue; |
86
|
|
|
} |
87
|
|
|
if ($value === false) { |
88
|
|
|
$value = 'false'; |
89
|
|
|
} |
90
|
|
|
if ($value === true) { |
91
|
|
|
$value = 'true'; |
92
|
|
|
} |
93
|
|
|
$rows[] = "<tr><th>$key</th><td>$value</td></tr>\n"; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
return implode("\n", $rows); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
public function getConfigDataRows() |
100
|
|
|
{ |
101
|
|
|
$rows = array(); |
102
|
|
|
foreach ($this->_configuration['directives'] as $key => $value) { |
103
|
|
|
if ($value === false) { |
104
|
|
|
$value = 'false'; |
105
|
|
|
} |
106
|
|
|
if ($value === true) { |
107
|
|
|
$value = 'true'; |
108
|
|
|
} |
109
|
|
|
if ($key == 'opcache.memory_consumption') { |
110
|
|
|
$value = $this->_size_for_humans($value); |
111
|
|
|
} |
112
|
|
|
$rows[] = "<tr><th>$key</th><td>$value</td></tr>\n"; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
return implode("\n", $rows); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
public function getScriptStatusRows() |
119
|
|
|
{ |
120
|
|
|
foreach ($this->_status['scripts'] as $key => $data) { |
121
|
|
|
$dirs[dirname($key)][basename($key)] = $data; |
|
|
|
|
122
|
|
|
$this->_arrayPset($this->_d3Scripts, $key, array( |
123
|
|
|
'name' => basename($key), |
124
|
|
|
'size' => $data['memory_consumption'], |
125
|
|
|
)); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
asort($dirs); |
129
|
|
|
|
130
|
|
|
$basename = ''; |
131
|
|
|
while (true) { |
132
|
|
|
if (count($this->_d3Scripts) !=1) { |
133
|
|
|
break; |
134
|
|
|
} |
135
|
|
|
$basename .= DIRECTORY_SEPARATOR . key($this->_d3Scripts); |
136
|
|
|
$this->_d3Scripts = reset($this->_d3Scripts); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
$this->_d3Scripts = $this->_processPartition($this->_d3Scripts, $basename); |
140
|
|
|
$id = 1; |
141
|
|
|
|
142
|
|
|
$rows = array(); |
143
|
|
|
foreach ($dirs as $dir => $files) { |
144
|
|
|
$count = count($files); |
145
|
|
|
$file_plural = $count > 1 ? 's' : null; |
146
|
|
|
$m = 0; |
147
|
|
|
foreach ($files as $file => $data) { |
148
|
|
|
$m += $data["memory_consumption"]; |
149
|
|
|
} |
150
|
|
|
$m = $this->_size_for_humans($m); |
151
|
|
|
|
152
|
|
|
if ($count > 1) { |
153
|
|
|
$rows[] = '<tr>'; |
154
|
|
|
$rows[] = "<th class=\"clickable\" id=\"head-{$id}\" colspan=\"3\" onclick=\"toggleVisible('#head-{$id}', '#row-{$id}')\">{$dir} ({$count} file{$file_plural}, {$m})</th>"; |
155
|
|
|
$rows[] = '</tr>'; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
foreach ($files as $file => $data) { |
159
|
|
|
$rows[] = "<tr id=\"row-{$id}\">"; |
160
|
|
|
$rows[] = "<td>" . $this->_format_value($data["hits"]) . "</td>"; |
161
|
|
|
$rows[] = "<td>" . $this->_size_for_humans($data["memory_consumption"]) . "</td>"; |
162
|
|
|
$rows[] = $count > 1 ? "<td>{$file}</td>" : "<td>{$dir}/{$file}</td>"; |
163
|
|
|
$rows[] = '</tr>'; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
++$id; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
return implode("\n", $rows); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
public function getScriptStatusCount() |
173
|
|
|
{ |
174
|
|
|
return count($this->_status["scripts"]); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
public function getGraphDataSetJson() |
178
|
|
|
{ |
179
|
|
|
$dataset = array(); |
180
|
|
|
$dataset['memory'] = array( |
181
|
|
|
$this->_status['memory_usage']['used_memory'], |
182
|
|
|
$this->_status['memory_usage']['free_memory'], |
183
|
|
|
$this->_status['memory_usage']['wasted_memory'], |
184
|
|
|
); |
185
|
|
|
|
186
|
|
|
$dataset['keys'] = array( |
187
|
|
|
$this->_status['opcache_statistics']['num_cached_keys'], |
188
|
|
|
$this->_status['opcache_statistics']['max_cached_keys'] - $this->_status['opcache_statistics']['num_cached_keys'], |
189
|
|
|
0 |
190
|
|
|
); |
191
|
|
|
|
192
|
|
|
$dataset['hits'] = array( |
193
|
|
|
$this->_status['opcache_statistics']['misses'], |
194
|
|
|
$this->_status['opcache_statistics']['hits'], |
195
|
|
|
0, |
196
|
|
|
); |
197
|
|
|
|
198
|
|
|
$dataset['restarts'] = array( |
199
|
|
|
$this->_status['opcache_statistics']['oom_restarts'], |
200
|
|
|
$this->_status['opcache_statistics']['manual_restarts'], |
201
|
|
|
$this->_status['opcache_statistics']['hash_restarts'], |
202
|
|
|
); |
203
|
|
|
|
204
|
|
|
if (THOUSAND_SEPARATOR === true) { |
205
|
|
|
$dataset['TSEP'] = 1; |
206
|
|
|
} else { |
207
|
|
|
$dataset['TSEP'] = 0; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
return json_encode($dataset); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
public function getHumanUsedMemory() |
214
|
|
|
{ |
215
|
|
|
return $this->_size_for_humans($this->getUsedMemory()); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
public function getHumanFreeMemory() |
219
|
|
|
{ |
220
|
|
|
return $this->_size_for_humans($this->getFreeMemory()); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
public function getHumanWastedMemory() |
224
|
|
|
{ |
225
|
|
|
return $this->_size_for_humans($this->getWastedMemory()); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
public function getUsedMemory() |
|
|
|
|
229
|
|
|
{ |
230
|
|
|
return $this->_status['memory_usage']['used_memory']; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
public function getFreeMemory() |
|
|
|
|
234
|
|
|
{ |
235
|
|
|
return $this->_status['memory_usage']['free_memory']; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
public function getWastedMemory() |
|
|
|
|
239
|
|
|
{ |
240
|
|
|
return $this->_status['memory_usage']['wasted_memory']; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
public function getWastedMemoryPercentage() |
244
|
|
|
{ |
245
|
|
|
return number_format($this->_status['memory_usage']['current_wasted_percentage'], 2); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
public function getD3Scripts() |
249
|
|
|
{ |
250
|
|
|
return $this->_d3Scripts; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
private function _processPartition($value, $name = null) |
|
|
|
|
254
|
|
|
{ |
255
|
|
|
if (array_key_exists('size', $value)) { |
256
|
|
|
return $value; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
$array = array('name' => $name,'children' => array()); |
260
|
|
|
|
261
|
|
|
foreach ($value as $k => $v) { |
262
|
|
|
$array['children'][] = $this->_processPartition($v, $k); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
return $array; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
private function _format_value($value) |
|
|
|
|
269
|
|
|
{ |
270
|
|
|
if (THOUSAND_SEPARATOR === true) { |
271
|
|
|
return number_format($value); |
272
|
|
|
} else { |
273
|
|
|
return $value; |
274
|
|
|
} |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
private function _size_for_humans($bytes) |
278
|
|
|
{ |
279
|
|
|
if ($bytes > 1048576) { |
280
|
|
|
return sprintf('%.2f MB', $bytes / 1048576); |
281
|
|
|
} else { |
282
|
|
|
if ($bytes > 1024) { |
283
|
|
|
return sprintf('%.2f kB', $bytes / 1024); |
284
|
|
|
} else { |
285
|
|
|
return sprintf('%d bytes', $bytes); |
286
|
|
|
} |
287
|
|
|
} |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
// Borrowed from Laravel |
291
|
|
|
private function _arrayPset(&$array, $key, $value) |
|
|
|
|
292
|
|
|
{ |
293
|
|
|
if (is_null($key)) { |
294
|
|
|
return $array = $value; |
295
|
|
|
} |
296
|
|
|
$keys = explode(DIRECTORY_SEPARATOR, ltrim($key, DIRECTORY_SEPARATOR)); |
297
|
|
|
while (count($keys) > 1) { |
298
|
|
|
$key = array_shift($keys); |
299
|
|
|
if (! isset($array[$key]) || ! is_array($array[$key])) { |
300
|
|
|
$array[$key] = array(); |
301
|
|
|
} |
302
|
|
|
$array =& $array[$key]; |
303
|
|
|
} |
304
|
|
|
$array[array_shift($keys)] = $value; |
305
|
|
|
return $array; |
306
|
|
|
} |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
$dataModel = new OpCacheDataModel(); |
310
|
|
|
?> |
311
|
|
|
<!DOCTYPE html> |
312
|
|
|
<meta charset="utf-8"> |
313
|
|
|
<html> |
314
|
|
|
<head> |
315
|
|
|
<style> |
316
|
|
|
body { |
317
|
|
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; |
318
|
|
|
margin: 0; |
319
|
|
|
padding: 0; |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
#container { |
323
|
|
|
width: 1024px; |
324
|
|
|
margin: auto; |
325
|
|
|
position: relative; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
h1 { |
329
|
|
|
padding: 10px 0; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
table { |
333
|
|
|
border-collapse: collapse; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
tbody tr:nth-child(even) { |
337
|
|
|
background-color: #eee; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
p.capitalize { |
341
|
|
|
text-transform: capitalize; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
.tabs { |
345
|
|
|
position: relative; |
346
|
|
|
float: left; |
347
|
|
|
width: 60%; |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
.tab { |
351
|
|
|
float: left; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
.tab label { |
355
|
|
|
background: #eee; |
356
|
|
|
padding: 10px 12px; |
357
|
|
|
border: 1px solid #ccc; |
358
|
|
|
margin-left: -1px; |
359
|
|
|
position: relative; |
360
|
|
|
left: 1px; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
.tab [type=radio] { |
364
|
|
|
display: none; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
.tab th, .tab td { |
368
|
|
|
padding: 8px 12px; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
.content { |
372
|
|
|
position: absolute; |
373
|
|
|
top: 28px; |
374
|
|
|
left: 0; |
375
|
|
|
background: white; |
376
|
|
|
border: 1px solid #ccc; |
377
|
|
|
height: 450px; |
378
|
|
|
width: 100%; |
379
|
|
|
overflow: auto; |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
.content table { |
383
|
|
|
width: 100%; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
.content th, .tab:nth-child(3) td { |
387
|
|
|
text-align: left; |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
.content td { |
391
|
|
|
text-align: right; |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
.clickable { |
395
|
|
|
cursor: pointer; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
[type=radio]:checked ~ label { |
399
|
|
|
background: white; |
400
|
|
|
border-bottom: 1px solid white; |
401
|
|
|
z-index: 2; |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
[type=radio]:checked ~ label ~ .content { |
405
|
|
|
z-index: 1; |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
#graph { |
409
|
|
|
float: right; |
410
|
|
|
width: 40%; |
411
|
|
|
position: relative; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
#graph > form { |
415
|
|
|
position: absolute; |
416
|
|
|
right: 60px; |
417
|
|
|
top: -20px; |
418
|
|
|
} |
419
|
|
|
|
420
|
|
|
#graph > svg { |
421
|
|
|
position: absolute; |
422
|
|
|
top: 0; |
423
|
|
|
right: 0; |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
#stats { |
427
|
|
|
position: absolute; |
428
|
|
|
right: 125px; |
429
|
|
|
top: 145px; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
#stats th, #stats td { |
433
|
|
|
padding: 6px 10px; |
434
|
|
|
font-size: 0.8em; |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
#partition { |
438
|
|
|
position: absolute; |
439
|
|
|
width: 100%; |
440
|
|
|
height: 100%; |
441
|
|
|
z-index: 10; |
442
|
|
|
top: 0; |
443
|
|
|
left: 0; |
444
|
|
|
background: #ddd; |
445
|
|
|
display: none; |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
#close-partition { |
449
|
|
|
display: none; |
450
|
|
|
position: absolute; |
451
|
|
|
z-index: 20; |
452
|
|
|
right: 15px; |
453
|
|
|
top: 15px; |
454
|
|
|
background: #f9373d; |
455
|
|
|
color: #fff; |
456
|
|
|
padding: 12px 15px; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
#close-partition:hover { |
460
|
|
|
background: #D32F33; |
461
|
|
|
cursor: pointer; |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
#partition rect { |
465
|
|
|
stroke: #fff; |
466
|
|
|
fill: #aaa; |
467
|
|
|
fill-opacity: 1; |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
#partition rect.parent { |
471
|
|
|
cursor: pointer; |
472
|
|
|
fill: steelblue; |
473
|
|
|
} |
474
|
|
|
|
475
|
|
|
#partition text { |
476
|
|
|
pointer-events: none; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
label { |
480
|
|
|
cursor: pointer; |
481
|
|
|
} |
482
|
|
|
</style> |
483
|
|
|
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.0.1/d3.v3.min.js"></script> |
484
|
|
|
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> |
485
|
|
|
<script> |
486
|
|
|
var hidden = {}; |
487
|
|
|
function toggleVisible(head, row) { |
488
|
|
|
if (!hidden[row]) { |
489
|
|
|
d3.selectAll(row).transition().style('display', 'none'); |
490
|
|
|
hidden[row] = true; |
491
|
|
|
d3.select(head).transition().style('color', '#ccc'); |
492
|
|
|
} else { |
493
|
|
|
d3.selectAll(row).transition().style('display'); |
494
|
|
|
hidden[row] = false; |
495
|
|
|
d3.select(head).transition().style('color', '#000'); |
496
|
|
|
} |
497
|
|
|
} |
498
|
|
|
</script> |
499
|
|
|
<title><?php echo $dataModel->getPageTitle(); ?></title> |
500
|
|
|
</head> |
501
|
|
|
|
502
|
|
|
<body> |
503
|
|
|
<div id="container"> |
504
|
|
|
<h1><?php echo $dataModel->getPageTitle(); ?></h1> |
505
|
|
|
|
506
|
|
|
<div class="tabs"> |
507
|
|
|
|
508
|
|
|
<div class="tab"> |
509
|
|
|
<input type="radio" id="tab-status" name="tab-group-1" checked> |
510
|
|
|
<label for="tab-status">Status</label> |
511
|
|
|
<div class="content"> |
512
|
|
|
<table> |
513
|
|
|
<?php echo $dataModel->getStatusDataRows(); ?> |
514
|
|
|
</table> |
515
|
|
|
</div> |
516
|
|
|
</div> |
517
|
|
|
|
518
|
|
|
<div class="tab"> |
519
|
|
|
<input type="radio" id="tab-config" name="tab-group-1"> |
520
|
|
|
<label for="tab-config">Configuration</label> |
521
|
|
|
<div class="content"> |
522
|
|
|
<table> |
523
|
|
|
<?php echo $dataModel->getConfigDataRows(); ?> |
524
|
|
|
</table> |
525
|
|
|
</div> |
526
|
|
|
</div> |
527
|
|
|
|
528
|
|
|
<div class="tab"> |
529
|
|
|
<input type="radio" id="tab-scripts" name="tab-group-1"> |
530
|
|
|
<label for="tab-scripts">Scripts (<?php echo $dataModel->getScriptStatusCount(); ?>)</label> |
531
|
|
|
<div class="content"> |
532
|
|
|
<table style="font-size:0.8em;"> |
533
|
|
|
<tr> |
534
|
|
|
<th width="10%">Hits</th> |
535
|
|
|
<th width="20%">Memory</th> |
536
|
|
|
<th width="70%">Path</th> |
537
|
|
|
</tr> |
538
|
|
|
<?php echo $dataModel->getScriptStatusRows(); ?> |
539
|
|
|
</table> |
540
|
|
|
</div> |
541
|
|
|
</div> |
542
|
|
|
|
543
|
|
|
<div class="tab"> |
544
|
|
|
<input type="radio" id="tab-visualise" name="tab-group-1"> |
545
|
|
|
<label for="tab-visualise">Visualise Partition</label> |
546
|
|
|
<div class="content"></div> |
547
|
|
|
</div> |
548
|
|
|
|
549
|
|
|
</div> |
550
|
|
|
|
551
|
|
|
<div id="graph"> |
552
|
|
|
<form> |
553
|
|
|
<label><input type="radio" name="dataset" value="memory" checked> Memory</label> |
554
|
|
|
<label><input type="radio" name="dataset" value="keys"> Keys</label> |
555
|
|
|
<label><input type="radio" name="dataset" value="hits"> Hits</label> |
556
|
|
|
<label><input type="radio" name="dataset" value="restarts"> Restarts</label> |
557
|
|
|
</form> |
558
|
|
|
|
559
|
|
|
<div id="stats"></div> |
560
|
|
|
</div> |
561
|
|
|
</div> |
562
|
|
|
|
563
|
|
|
<div id="close-partition">✖ Close Visualisation</div> |
564
|
|
|
<div id="partition"></div> |
565
|
|
|
|
566
|
|
|
<script> |
567
|
|
|
var dataset = <?php echo $dataModel->getGraphDataSetJson(); ?>; |
568
|
|
|
|
569
|
|
|
var width = 400, |
570
|
|
|
height = 400, |
571
|
|
|
radius = Math.min(width, height) / 2, |
572
|
|
|
colours = ['#B41F1F', '#1FB437', '#ff7f0e']; |
573
|
|
|
|
574
|
|
|
d3.scale.customColours = function() { |
575
|
|
|
return d3.scale.ordinal().range(colours); |
576
|
|
|
}; |
577
|
|
|
|
578
|
|
|
var colour = d3.scale.customColours(); |
579
|
|
|
var pie = d3.layout.pie().sort(null); |
580
|
|
|
|
581
|
|
|
var arc = d3.svg.arc().innerRadius(radius - 20).outerRadius(radius - 50); |
582
|
|
|
var svg = d3.select("#graph").append("svg") |
583
|
|
|
.attr("width", width) |
584
|
|
|
.attr("height", height) |
585
|
|
|
.append("g") |
586
|
|
|
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); |
587
|
|
|
|
588
|
|
|
var path = svg.selectAll("path") |
589
|
|
|
.data(pie(dataset.memory)) |
590
|
|
|
.enter().append("path") |
591
|
|
|
.attr("fill", function(d, i) { return colour(i); }) |
592
|
|
|
.attr("d", arc) |
593
|
|
|
.each(function(d) { this._current = d; }); // store the initial values |
594
|
|
|
|
595
|
|
|
d3.selectAll("input").on("change", change); |
596
|
|
|
set_text("memory"); |
597
|
|
|
|
598
|
|
|
function set_text(t) { |
599
|
|
|
if (t === "memory") { |
600
|
|
|
d3.select("#stats").html( |
601
|
|
|
"<table><tr><th style='background:#B41F1F;'>Used</th><td><?php echo $dataModel->getHumanUsedMemory()?></td></tr>"+ |
602
|
|
|
"<tr><th style='background:#1FB437;'>Free</th><td><?php echo $dataModel->getHumanFreeMemory()?></td></tr>"+ |
603
|
|
|
"<tr><th style='background:#ff7f0e;' rowspan=\"2\">Wasted</th><td><?php echo $dataModel->getHumanWastedMemory()?></td></tr>"+ |
604
|
|
|
"<tr><td><?php echo $dataModel->getWastedMemoryPercentage()?>%</td></tr></table>" |
605
|
|
|
); |
606
|
|
|
} else if (t === "keys") { |
607
|
|
|
d3.select("#stats").html( |
608
|
|
|
"<table><tr><th style='background:#B41F1F;'>Cached keys</th><td>"+format_value(dataset[t][0])+"</td></tr>"+ |
609
|
|
|
"<tr><th style='background:#1FB437;'>Free Keys</th><td>"+format_value(dataset[t][1])+"</td></tr></table>" |
610
|
|
|
); |
611
|
|
|
} else if (t === "hits") { |
612
|
|
|
d3.select("#stats").html( |
613
|
|
|
"<table><tr><th style='background:#B41F1F;'>Misses</th><td>"+format_value(dataset[t][0])+"</td></tr>"+ |
614
|
|
|
"<tr><th style='background:#1FB437;'>Cache Hits</th><td>"+format_value(dataset[t][1])+"</td></tr></table>" |
615
|
|
|
); |
616
|
|
|
} else if (t === "restarts") { |
617
|
|
|
d3.select("#stats").html( |
618
|
|
|
"<table><tr><th style='background:#B41F1F;'>Memory</th><td>"+dataset[t][0]+"</td></tr>"+ |
619
|
|
|
"<tr><th style='background:#1FB437;'>Manual</th><td>"+dataset[t][1]+"</td></tr>"+ |
620
|
|
|
"<tr><th style='background:#ff7f0e;'>Keys</th><td>"+dataset[t][2]+"</td></tr></table>" |
621
|
|
|
); |
622
|
|
|
} |
623
|
|
|
} |
624
|
|
|
|
625
|
|
|
function change() { |
626
|
|
|
// Filter out any zero values to see if there is anything left |
627
|
|
|
var remove_zero_values = dataset[this.value].filter(function(value) { |
628
|
|
|
return value > 0; |
629
|
|
|
}); |
630
|
|
|
|
631
|
|
|
// Skip if the value is undefined for some reason |
632
|
|
|
if (typeof dataset[this.value] !== 'undefined' && remove_zero_values.length > 0) { |
633
|
|
|
$('#graph').find('> svg').show(); |
634
|
|
|
path = path.data(pie(dataset[this.value])); // update the data |
635
|
|
|
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs |
636
|
|
|
// Hide the graph if we can't draw it correctly, not ideal but this works |
637
|
|
|
} else { |
638
|
|
|
$('#graph').find('> svg').hide(); |
639
|
|
|
} |
640
|
|
|
|
641
|
|
|
set_text(this.value); |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
function arcTween(a) { |
645
|
|
|
var i = d3.interpolate(this._current, a); |
646
|
|
|
this._current = i(0); |
647
|
|
|
return function(t) { |
648
|
|
|
return arc(i(t)); |
649
|
|
|
}; |
650
|
|
|
} |
651
|
|
|
|
652
|
|
|
function size_for_humans(bytes) { |
653
|
|
|
if (bytes > 1048576) { |
654
|
|
|
return (bytes/1048576).toFixed(2) + ' MB'; |
655
|
|
|
} else if (bytes > 1024) { |
656
|
|
|
return (bytes/1024).toFixed(2) + ' KB'; |
657
|
|
|
} else return bytes + ' bytes'; |
658
|
|
|
} |
659
|
|
|
|
660
|
|
|
function format_value(value) { |
661
|
|
|
if (dataset["TSEP"] == 1) { |
662
|
|
|
return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
663
|
|
|
} else { |
664
|
|
|
return value; |
665
|
|
|
} |
666
|
|
|
} |
667
|
|
|
|
668
|
|
|
var w = window.innerWidth, |
669
|
|
|
h = window.innerHeight, |
670
|
|
|
x = d3.scale.linear().range([0, w]), |
671
|
|
|
y = d3.scale.linear().range([0, h]); |
672
|
|
|
|
673
|
|
|
var vis = d3.select("#partition") |
674
|
|
|
.style("width", w + "px") |
675
|
|
|
.style("height", h + "px") |
676
|
|
|
.append("svg:svg") |
677
|
|
|
.attr("width", w) |
678
|
|
|
.attr("height", h); |
679
|
|
|
|
680
|
|
|
var partition = d3.layout.partition() |
681
|
|
|
.value(function(d) { return d.size; }); |
682
|
|
|
|
683
|
|
|
root = JSON.parse('<?php echo json_encode($dataModel->getD3Scripts()); ?>'); |
684
|
|
|
|
685
|
|
|
var g = vis.selectAll("g") |
686
|
|
|
.data(partition.nodes(root)) |
687
|
|
|
.enter().append("svg:g") |
688
|
|
|
.attr("transform", function(d) { return "translate(" + x(d.y) + "," + y(d.x) + ")"; }) |
689
|
|
|
.on("click", click); |
690
|
|
|
|
691
|
|
|
var kx = w / root.dx, |
692
|
|
|
ky = h / 1; |
693
|
|
|
|
694
|
|
|
g.append("svg:rect") |
695
|
|
|
.attr("width", root.dy * kx) |
696
|
|
|
.attr("height", function(d) { return d.dx * ky; }) |
697
|
|
|
.attr("class", function(d) { return d.children ? "parent" : "child"; }); |
698
|
|
|
|
699
|
|
|
g.append("svg:text") |
700
|
|
|
.attr("transform", transform) |
701
|
|
|
.attr("dy", ".35em") |
702
|
|
|
.style("opacity", function(d) { return d.dx * ky > 12 ? 1 : 0; }) |
703
|
|
|
.text(function(d) { return d.name; }) |
704
|
|
|
|
705
|
|
|
d3.select(window) |
706
|
|
|
.on("click", function() { click(root); }) |
707
|
|
|
|
708
|
|
|
function click(d) { |
709
|
|
|
if (!d.children) return; |
710
|
|
|
|
711
|
|
|
kx = (d.y ? w - 40 : w) / (1 - d.y); |
712
|
|
|
ky = h / d.dx; |
713
|
|
|
x.domain([d.y, 1]).range([d.y ? 40 : 0, w]); |
714
|
|
|
y.domain([d.x, d.x + d.dx]); |
715
|
|
|
|
716
|
|
|
var t = g.transition() |
717
|
|
|
.duration(d3.event.altKey ? 7500 : 750) |
718
|
|
|
.attr("transform", function(d) { return "translate(" + x(d.y) + "," + y(d.x) + ")"; }); |
719
|
|
|
|
720
|
|
|
t.select("rect") |
721
|
|
|
.attr("width", d.dy * kx) |
722
|
|
|
.attr("height", function(d) { return d.dx * ky; }); |
723
|
|
|
|
724
|
|
|
t.select("text") |
725
|
|
|
.attr("transform", transform) |
726
|
|
|
.style("opacity", function(d) { return d.dx * ky > 12 ? 1 : 0; }); |
727
|
|
|
|
728
|
|
|
d3.event.stopPropagation(); |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
function transform(d) { |
732
|
|
|
return "translate(8," + d.dx * ky / 2 + ")"; |
733
|
|
|
} |
734
|
|
|
|
735
|
|
|
$(document).ready(function() { |
736
|
|
|
|
737
|
|
|
function handleVisualisationToggle(close) { |
738
|
|
|
|
739
|
|
|
$('#partition, #close-partition').fadeToggle(); |
740
|
|
|
|
741
|
|
|
// Is the visualisation being closed? If so show the status tab again |
742
|
|
|
if (close) { |
743
|
|
|
|
744
|
|
|
$('#tab-visualise').removeAttr('checked'); |
745
|
|
|
$('#tab-status').trigger('click'); |
746
|
|
|
|
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
} |
750
|
|
|
|
751
|
|
|
$('label[for="tab-visualise"], #close-partition').on('click', function() { |
752
|
|
|
|
753
|
|
|
handleVisualisationToggle(($(this).attr('id') === 'close-partition')); |
754
|
|
|
|
755
|
|
|
}); |
756
|
|
|
|
757
|
|
|
$(document).keyup(function(e) { |
758
|
|
|
|
759
|
|
|
if (e.keyCode == 27) handleVisualisationToggle(true); |
760
|
|
|
|
761
|
|
|
}); |
762
|
|
|
|
763
|
|
|
}); |
764
|
|
|
</script> |
765
|
|
|
</body> |
766
|
|
|
</html> |
767
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.