Display::setQueryData()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 0 Features 1
Metric Value
c 7
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
/*****************************************
4
 * Title : Php Quick Profiler Display Class
5
 * Author : Created by Ryan Campbell
6
 * URL : http://particletree.com/features/php-quick-profiler/
7
 * Description : This is a hacky way of pushing profiling logic to the
8
 *  PQP HTML. This is great because it will just work in your project,
9
 *  but it is hard to maintain and read.
10
*****************************************/
11
12
namespace Particletree\Pqp;
13
14
class Display
15
{
16
17
    /** @var  array */
18
    protected $defaults = array(
19
        'relative_path' => true,
20
        'script_path' => 'asset/script.js',
21
        'style_path' => 'asset/style.css'
22
    );
23
24
    /** @var  array */
25
    protected $options;
26
27
    /** @var  double */
28
    protected $startTime;
29
30
    /** @var  Console */
31
    protected $console;
32
33
    /** @var  array */
34
    protected $speedData;
35
36
    /** @var  array */
37
    protected $queryData;
38
39
    /** @var  array */
40
    protected $memoryData;
41
42
    /** @var  array */
43
    protected $fileData;
44
45
    /** @var  integer */
46
    protected $pathTrimStart = 0;
47
48
    /**
49
     * @param array $options
50
     */
51
    public function __construct(array $options = array())
52
    {
53
        $options = array_intersect_key($options, $this->defaults);
54
        $this->options = array_replace($this->defaults, $options);
55
56
        if ($this->options['relative_path']) {
57
            $this->pathTrimStart = $this->getPathTrimStart(getcwd(), __DIR__);
58
        }
59
    }
60
61
    /**
62
     * @param double $startTime
63
     */
64
    public function setStartTime($startTime)
65
    {
66
        $this->startTime = $startTime;
67
    }
68
69
    /**
70
     * @param Console $console
71
     */
72
    public function setConsole(Console $console)
73
    {
74
        $this->console = $console;
75
    }
76
77
    /**
78
     * Sets memory data
79
     *
80
     * @param array $data
81
     */
82
    public function setMemoryData(array $data)
83
    {
84
        $this->memoryData = $data;
85
    }
86
87
    /**
88
     * Sets query data
89
     *
90
     * @param array $data
91
     */
92
    public function setQueryData(array $data)
93
    {
94
        $this->queryData = $data;
95
    }
96
97
    /**
98
     * Sets speed data
99
     *
100
     * @param array $data
101
     */
102
    public function setSpeedData(array $data)
103
    {
104
        $this->speedData = $data;
105
    }
106
107
    /**
108
     * Sets file data
109
     *
110
     * @param array $data
111
     */
112
    public function setFileData(array $data)
113
    {
114
        $this->fileData = $data;
115
    }
116
117
    /**
118
     * @return integer
119
     */
120
    protected function getPathTrimStart($cwd, $dir)
121
    {
122
        for ($pos = 0; $pos <= strlen($cwd); $pos++) {
123
            if (strncmp($cwd, $dir, $pos + 1) !== 0) {
124
                break;
125
            }
126
        }
127
128
        return $pos;
129
    }
130
131
    /**
132
     * @return array
133
     */
134
    protected function getConsoleMeta()
135
    {
136
        $consoleMeta = array(
137
            'log' => 0,
138
            'memory' => 0,
139
            'error' => 0,
140
            'speed' => 0
141
        );
142
        foreach ($this->console->getLogs() as $log) {
143
            if (array_key_exists($log['type'], $consoleMeta)) {
144
                $consoleMeta[$log['type']]++;
145
                continue;
146
            }
147
            $consoleMeta['error']++;
148
        }
149
150
        return $consoleMeta;
151
    }
152
153
    /**
154
     * @return array
155
     */
156
    protected function getConsoleMessages()
157
    {
158
        $messages = array();
159
        foreach ($this->console->getLogs() as $log) {
160
            switch ($log['type']) {
161
                case 'log':
162
                    $message = array(
163
                        'message' => print_r($log['data'], true),
164
                        'type'    => 'log'
165
                    );
166
                    break;
167
                case 'memory':
168
                    $message = array(
169
                        'message' => (!empty($log['data_type']) ? "{$log['data_type']}: " : '') . $log['name'],
170
                        'data'    => $this->getReadableMemory($log['data']),
171
                        'type'    => 'memory'
172
                    );
173
                    break;
174
                case 'error':
175
                    $message = array(
176
                        'message' => "Line {$log['line']}: {$log['data']} in {$this->getFilePath($log['file'])}",
177
                        'type'    => 'error'
178
                    );
179
                    break;
180
                case 'speed':
181
                    $elapsedTime = $log['data'] - $this->startTime;
182
                    $message = array(
183
                        'message' => $log['name'],
184
                        'data'    => $this->getReadableTime($elapsedTime),
185
                        'type'    => 'speed'
186
                    );
187
                    break;
188
                default:
189
                    $message = array(
190
                        'message' => "Unrecognized console log type: {$log['type']}",
191
                        'type'    => 'error'
192
                    );
193
                    break;
194
            }
195
            array_push($messages, $message);
196
        }
197
        return $messages;
198
    }
199
200
    /**
201
     * @return array
202
     */
203
    protected function getSpeedMeta()
204
    {
205
        $elapsedTime = $this->getReadableTime($this->speedData['elapsed']);
206
        $allowedTime = $this->getReadableTime($this->speedData['allowed'], 0);
207
208
        return array(
209
            'elapsed' => $elapsedTime,
210
            'allowed' => $allowedTime,
211
        );
212
    }
213
214
    /**
215
     * @return array
216
     */
217
    public function getQueryMeta()
218
    {
219
        $queryCount = count($this->queryData);
220
        $queryTotalTime = array_reduce($this->queryData, function ($sum, $row) {
221
            return $sum + $row['time'];
222
        }, 0);
223
        $queryTotalTime = $this->getReadableTime($queryTotalTime);
224
        $querySlowestTime = array_reduce($this->queryData, function ($slowest, $row) {
225
            return ($slowest < $row['time']) ? $row['time'] : $slowest;
226
        }, 0);
227
        $querySlowestTime = $this->getReadableTime($querySlowestTime);
228
229
        return array(
230
            'count'   => $queryCount,
231
            'time'    => $queryTotalTime,
232
            'slowest' => $querySlowestTime
233
        );
234
    }
235
236
    /**
237
     * @return array
238
     */
239
    public function getQueryList()
240
    {
241
        $queryList = array();
242
        foreach ($this->queryData as $query) {
243
            array_push($queryList, array(
244
                'message'  => $query['sql'],
245
                'sub_data' => array_filter($query['explain']),
246
                'data'     => $this->getReadableTime($query['time'])
247
            ));
248
        }
249
        return $queryList;
250
    }
251
252
    /**
253
     * @return array
254
     */
255
    public function getMemoryMeta()
256
    {
257
        $usedMemory = $this->getReadableMemory($this->memoryData['used']);
258
        $allowedMemory = $this->memoryData['allowed']; // todo parse this, maybe?
259
260
        return array(
261
            'used'    => $usedMemory,
262
            'allowed' => $allowedMemory
263
        );
264
    }
265
266
    /**
267
     * @return array
268
     */
269
    protected function getFileMeta()
270
    {
271
        $fileCount = count($this->fileData);
272
        $fileTotalSize = array_reduce($this->fileData, function ($sum, $row) {
273
            return $sum + $row['size'];
274
        }, 0);
275
        $fileTotalSize = $this->getReadableMemory($fileTotalSize);
276
        $fileLargestSize = array_reduce($this->fileData, function ($largest, $row) {
277
            return ($largest < $row['size']) ? $row['size'] : $largest;
278
        }, 0);
279
        $fileLargestSize = $this->getReadableMemory($fileLargestSize);
280
281
        return array(
282
            'count' => $fileCount,
283
            'size' => $fileTotalSize,
284
            'largest' => $fileLargestSize
285
        );
286
    }
287
288
    /**
289
     * @return array
290
     */
291
    protected function getFileList()
292
    {
293
        $fileList = array();
294
        foreach ($this->fileData as $file) {
295
            array_push($fileList, array(
296
                'message' => $this->getFilePath($file['name']),
297
                'data'    => $this->getReadableMemory($file['size'])
298
            ));
299
        }
300
        return $fileList;
301
    }
302
303
    /**
304
     * Formatter for human-readable time
305
     * Only handles time up to 60 minutes gracefully
306
     *
307
     * @param double  $time
308
     * @param integer $percision
309
     * @return string
310
     */
311
    protected function getReadableTime($time, $percision = 3)
312
    {
313
        $unit = 's';
314
        if ($time < 1) {
315
            $time *= 1000;
316
            $percision = 0;
317
            $unit = 'ms';
318
        } elseif ($time > 60) {
319
            $time /= 60;
320
            $unit = 'm';
321
        }
322
        $time = number_format($time, $percision);
323
        return "{$time} {$unit}";
324
    }
325
326
    /**
327
     * Formatter for human-readable memory
328
     * Only handles time up to a few gigs gracefully
329
     *
330
     * @param double  $size
331
     * @param integer $percision
332
     */
333
    protected function getReadableMemory($size, $percision = 2)
334
    {
335
        $unitOptions = array('b', 'k', 'M', 'G');
336
337
        $base = log($size, 1024);
338
339
        $memory = round(pow(1024, $base - floor($base)), $percision);
340
        $unit = $unitOptions[floor($base)];
341
        return "{$memory} {$unit}";
342
    }
343
344
    /**
345
     * @param string $path
346
     * @return string
347
     */
348
    protected function getFilePath($path)
349
    {
350
        if (!$this->options['relative_path']) {
351
            return $path;
352
        }
353
354
        return substr($path, $this->pathTrimStart);
355
    }
356
357
    /**
358
     * @param array  $messages
359
     * @param string $type
360
     * @return array
361
     */
362
    protected function filterMessages($messages, $type)
363
    {
364
        return array_filter($messages, function ($message) use ($type) {
365
            return $message['type'] == $type;
366
        });
367
    }
368
369
    /**
370
     * @returns array
371
     */
372
    protected function gatherTemplateData()
373
    {
374
        $consoleMeta = $this->getConsoleMeta();
375
        $speedMeta = $this->getSpeedMeta();
376
        $queryMeta = $this->getQueryMeta();
377
        $memoryMeta = $this->getMemoryMeta();
378
        $fileMeta = $this->getFileMeta();
379
380
        $consoleMessages = $this->getConsoleMessages();
381
        $queryList = $this->getQueryList();
382
        $fileList = $this->getFileList();
383
384
        return array(
385
            'header' => array(
386
                'console' => array_sum($consoleMeta),
387
                'speed'   => $speedMeta['elapsed'],
388
                'query'   => $queryMeta['count'],
389
                'memory'  => $memoryMeta['used'],
390
                'files'   => $fileMeta['count']
391
            ),
392
            'console' => array(
393
                'meta' => $consoleMeta,
394
                'messages' => $consoleMessages
395
            ),
396
            'speed' => array(
397
                'meta' => $speedMeta,
398
                'messages' => $this->filterMessages($consoleMessages, 'speed')
399
            ),
400
            'query' => array(
401
                'meta' => $queryMeta,
402
                'messages' => $queryList
403
            ),
404
            'memory' => array(
405
                'meta' => $memoryMeta,
406
                'messages' => $this->filterMessages($consoleMessages, 'memory')
407
            ),
408
            'files' => array(
409
                'meta' => $fileMeta,
410
                'messages' => $fileList
411
            )
412
        );
413
    }
414
415
    public function __invoke()
416
    {
417
        $templateData = $this->gatherTemplateData();
418
419
        // todo is this really the best way to load these?
420
        $styles = file_get_contents(__DIR__ . "/../{$this->options['style_path']}");
421
        $script = file_get_contents(__DIR__ . "/../{$this->options['script_path']}");
422
423
        call_user_func(function () use ($templateData, $styles, $script) {
424
            extract($templateData);
425
            require_once __DIR__ . '/../asset/display.html';
426
        });
427
    }
428
}
429