Issues (152)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

code/tests/opcache.php (11 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 5 and the first side effect is on line 2.

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.

Loading history...
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']) ||
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
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) {
0 ignored issues
show
The expression $value of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
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;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$dirs was never initialized. Although not strictly required by PHP, it is generally a good practice to add $dirs = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
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()
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
229
    {
230
        return $this->_status['memory_usage']['used_memory'];
231
    }
232
233
    public function getFreeMemory()
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
234
    {
235
        return $this->_status['memory_usage']['free_memory'];
236
    }
237
238
    public function getWastedMemory()
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
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)
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
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)
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
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&nbsp;MB', $bytes / 1048576);
281
        } else {
282
            if ($bytes > 1024) {
283
                return sprintf('%.2f&nbsp;kB', $bytes / 1024);
284
            } else {
285
                return sprintf('%d&nbsp;bytes', $bytes);
286
            }
287
        }
288
    }
289
290
    // Borrowed from Laravel
291
    private function _arrayPset(&$array, $key, $value)
0 ignored issues
show
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
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">&#10006; 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