Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
DeveloperToolbar
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
8 / 8
12
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 interpretData
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
1
 view
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 json
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 dumpDataToHtml
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 bugicon
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 calculateRusageTime
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 formatBytes
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace Miniframe\Toolbar\Controller;
4
5use Miniframe\Core\AbstractController;
6use Miniframe\Core\Config;
7use Miniframe\Core\Request;
8use Miniframe\Core\Response;
9use Miniframe\Response\JsonResponse;
10use Miniframe\Response\PhpResponse;
11
12class DeveloperToolbar extends AbstractController
13{
14    /**
15     * All logged data from the request
16     *
17     * @var array
18     */
19    private $requestData;
20
21    /**
22     * Initiates the Developer Toolbar Controller
23     *
24     * @param Request $request     Reference to the Request object.
25     * @param Config  $config      Reference to the Config object.
26     * @param array   $requestData Data that's linked to the debugging request.
27     */
28    public function __construct(Request $request, Config $config, array $requestData = array())
29    {
30        $this->requestData = $requestData;
31        parent::__construct($request, $config);
32    }
33
34    /**
35     * Interprets all data and returns it to an associative array
36     *
37     * @return array
38     */
39    private function interpretData(): array
40    {
41        $originalRequest = $this->requestData['request'];
42        /* @var $originalRequest Request */
43
44        $data = array();
45
46        $data['requestMethod'] = $originalRequest->getServer('REQUEST_METHOD');
47        $data['requestVersion'] = $originalRequest->getServer('SERVER_PROTOCOL');
48        $data['requestUri'] = $originalRequest->getServer('REQUEST_URI');
49        $data['requestHeaders'] = $this->requestData['requestHeaders'];
50        $data['userTimeMs'] = $this->calculateRusageTime($this->requestData['rusage'], 'utime');
51        $data['systemTimeMs'] = $this->calculateRusageTime($this->requestData['rusage'], 'stime');
52
53        $data['responseHeaders'] = $this->requestData['responseHeaders'];
54        $data['responseType'] = $this->requestData['responseType'];
55        $data['responseCode'] = $this->requestData['responseCode'];
56        $data['exitCode'] = $this->requestData['exitCode'];
57
58        $data['errors'] = $this->requestData['errors'];
59        $data['memoryPeakReal'] = $this->formatBytes($this->requestData['memoryPeak']['real']);
60        $data['memoryPeakEmalloc'] = $this->formatBytes($this->requestData['memoryPeak']['emalloc']);
61
62        $data['exceptionThrown'] = $this->requestData['exceptionThrown'];
63        $data['exceptionType'] = $this->requestData['exceptionType'];
64        $data['exceptionCode'] = $this->requestData['exceptionCode'];
65        $data['exceptionMessage'] = $this->requestData['exceptionMessage'];
66        $data['exceptionFile'] = $this->requestData['exceptionFile'];
67        $data['exceptionLine'] = $this->requestData['exceptionLine'];
68        $data['exceptionTrace'] = $this->requestData['exceptionTrace'];
69
70        $data['dumps'] = $this->requestData['dumps'];
71
72        return $data;
73    }
74
75    /**
76     * /_DEBUG/[hash] page; shows the debugging output in HTML
77     *
78     * @return Response
79     */
80    public function view(): Response
81    {
82        return new PhpResponse(__DIR__ . '/../../templates/toolbar_view.html.php', $this->interpretData());
83    }
84
85    /**
86     * /_DEBUG/[hash]/json page; outputs the debugging data in json format.
87     *
88     * @return Response
89     */
90    public function json(): Response
91    {
92        $data = $this->interpretData();
93        // Convert dumps
94        foreach ($data['dumps'] as &$dump) {
95            $dump['data'] = static::dumpDataToHtml($dump['data']);
96        }
97        return new JsonResponse($data);
98    }
99
100    /**
101     * Converts dump data to nicely formatted HTML
102     *
103     * @param mixed $dumpData The dump data.
104     *
105     * @return string
106     */
107    public static function dumpDataToHtml($dumpData): string
108    {
109        $varExport = var_export($dumpData, true);
110        $highlighted = highlight_string('<?php ' . $varExport, true);
111
112        $phpStripped = preg_replace('/<span[^>]+>&lt;\?php&nbsp;<\/span>/', '', $highlighted);
113        $firstNewlineStripped = preg_replace("/\n/", '', $phpStripped, 1);
114        $lastNewlineStripped = str_replace("\n</code>", '', $firstNewlineStripped);
115
116        return $lastNewlineStripped;
117    }
118
119    /**
120     * Returns the bugicon.svg asset.
121     *
122     * @return Response
123     */
124    public function bugicon(): Response
125    {
126        $return = new PhpResponse(__DIR__ . '/../../templates/bugicon.svg.php');
127        $return->addHeader('Content-Type: image/svg+xml');
128        return $return;
129    }
130
131    /**
132     * Calculates the difference between start and end.
133     *
134     * @param array  $rusageData Rusage data array.
135     * @param string $key        Key for which we want the difference.
136     *
137     * @return float
138     */
139    private function calculateRusageTime(array $rusageData, string $key): float
140    {
141        $start = $rusageData['start'];
142        $end = $rusageData['end'];
143
144        $startTimestamp = $start['ru_' . $key . '.tv_sec'] . '.' . $start['ru_' . $key . '.tv_usec'];
145        $endTimestamp = $end['ru_' . $key . '.tv_sec'] . '.' . $end['ru_' . $key . '.tv_usec'];
146
147        return (float)$endTimestamp - (float)$startTimestamp;
148    }
149
150    /**
151     * Formats an amount of bytes in a human-readable format
152     *
153     * @param integer $bytes The amount of bytes.
154     *
155     * @return string
156     */
157    private function formatBytes(int $bytes): string
158    {
159        if ($bytes > 1073741824) {
160            return number_format($bytes / 1073741824, 2) . 'GB';
161        }
162        if ($bytes > 1048576) {
163            return number_format($bytes / 1048576, 2) . 'MB';
164        }
165        if ($bytes > 1024) {
166            return number_format($bytes / 1024, 2) . 'kB';
167        }
168        return $bytes . ' bytes';
169    }
170}