1 <?php
2
3 namespace Alo;
4
5 use Alo\Exception\ProfilerException as PE;
6
7 if (!defined('GEN_START')) {
8 http_response_code(404);
9 die();
10 }
11
12 /**
13 * A code profiling class
14 *
15 * @author Art <a.molcanovas@gmail.com>
16 */
17 class Profiler {
18
19 /**
20 * Defines a parameter as "microtime"
21 *
22 * @var string
23 */
24 const P_MICROTIME = 'microtime';
25
26 /**
27 * Defines a parameter as "session data"
28 *
29 * @var string
30 */
31 const P_SESSION_DATA = 'session_data';
32
33 /**
34 * Defines a parameter as "$_GET data set"
35 *
36 * @var string
37 */
38 const P_GET = '$_GET';
39
40 /**
41 * Defines a parameter as "$_POST data set"
42 *
43 * @var string
44 */
45 const P_POST = '$_POST';
46
47 /**
48 * Defines a parameter as "$_FILES data set"
49 *
50 * @var string
51 */
52 const P_FILES = '$_FILES';
53
54 /**
55 * Defines a parameter as "controller in use"
56 *
57 * @var string
58 */
59 const P_CONTROLLER = 'controller';
60
61 /**
62 * Defines a parameter as "controller method in use"
63 *
64 * @var string
65 */
66 const P_CONTROLLER_METHOD = 'controller_method';
67
68 /**
69 * Defines a parameter as "port in use"
70 *
71 * @var string
72 */
73 const P_PORT = 'port';
74
75 /**
76 * Defines a parameter as "request IP"
77 *
78 * @var string
79 */
80 const P_REMOTE_ADDR = 'remote_addr';
81
82 /**
83 * Defines a parameter as "request method"
84 *
85 * @var string
86 */
87 const P_REQUEST_METHOD = 'request_method';
88
89 /**
90 * Defines a parameter as "request scheme"
91 *
92 * @var string
93 */
94 const P_REQUEST_SCHEME = 'request_scheme';
95
96 /**
97 * Defines a parameter as "server internal IP"
98 *
99 * @var string
100 */
101 const P_SERVER_ADDR = 'server_addr';
102
103 /**
104 * Defines a parameter as "server name"
105 *
106 * @var string
107 */
108 const P_SERVER_NAME = 'server_name';
109
110 /**
111 * Defines a parameter as "Request headers set"
112 *
113 * @var string
114 */
115 const P_HEADERS = 'headers';
116
117 /**
118 * Defines a parameter as "request path"
119 *
120 * @var string
121 */
122 const P_REQUEST_PATH = 'request_path';
123
124 /**
125 * Defines a parameter as "memory allocated to PHP script via emalloc()"
126 *
127 * @var string
128 */
129 const P_MEMORY_USAGE = 'memory_usage';
130
131 /**
132 * Defines a parameter as "real memory allocated to PHP script"
133 *
134 * @var string
135 */
136 const P_REAL_MEMORY_USAGE = 'real_memory_usage';
137
138 /**
139 * Defines a parameter as "diff"
140 *
141 * @var string
142 */
143 const P_DIFF = '_diff';
144
145 /**
146 * Marks set
147 *
148 * @var array
149 */
150 protected $marks;
151
152 /**
153 * Instantiates the class
154 *
155 * @author Art <a.molcanovas@gmail.com>
156 */
157 function __construct() {
158 $this->marks = [];
159 }
160
161 /**
162 * Sets a profiler mark
163 *
164 * @author Art <a.molcanovas@gmail.com>
165 * @param string $identifier How to identify this mark
166 * @return Profiler
167 */
168 function mark($identifier) {
169 $m = &$this->marks[$identifier];
170 $r = &\Alo::$router;
171
172 $m = [
173 self::P_MICROTIME => microtime(true),
174 self::P_SESSION_DATA => \Alo::$session ? \Alo::$session->getAll() : false,
175 self::P_GET => $_GET,
176 self::P_POST => $_POST,
177 self::P_FILES => $_FILES,
178 self::P_CONTROLLER => $r->getController(),
179 self::P_CONTROLLER_METHOD => $r->getMethod(),
180 self::P_PORT => $r->getPort(),
181 self::P_REMOTE_ADDR => $r->getRemoteAddr(),
182 self::P_REQUEST_METHOD => $r->getRequestMethod(),
183 self::P_REQUEST_SCHEME => $r->getRequestScheme(),
184 self::P_SERVER_ADDR => $r->getServerAddr(),
185 self::P_SERVER_NAME => $r->getServerName(),
186 self::P_HEADERS => \getallheaders(),
187 self::P_REQUEST_PATH => $r->getPath(),
188 self::P_MEMORY_USAGE => memory_get_usage(false),
189 self::P_REAL_MEMORY_USAGE => memory_get_usage(true)
190 ];
191
192 return $this;
193 }
194
195 /**
196 * Returns absolute microtime difference between the two marks
197 *
198 * @author Art <a.molcanovas@gmail.com>
199 * @param string $first_mark The first mark identifier
200 * @param string $second_mark The second mark identifier
201 * @throws PE When one of the marks cannot be found
202 * @return float
203 */
204 function timeBetween($first_mark, $second_mark) {
205 if (!isset($this->marks[$first_mark])) {
206 throw new PE('The first mark could not be found.', PE::E_MARK_NOT_SET);
207 } elseif (!isset($this->marks[$second_mark])) {
208 throw new PE('The second mark could not be found.', PE::E_MARK_NOT_SET);
209 } else {
210 return abs($this->marks[$first_mark][self::P_MICROTIME] - $this->marks[$second_mark][self::P_MICROTIME]);
211 }
212 }
213
214 /**
215 * Returns the difference between the two marks, i.e. all key/value pairs in $second_mark that differ from those
216 * of $first_mark
217 *
218 * @author Art <a.molcanovas@gmail.com>
219 * @param string $first_mark The first mark identifier
220 * @param string $second_mark The second mark identifier
221 * @throws PE When one of the marks cannot be found
222 * @return array
223 */
224 function diff($first_mark, $second_mark) {
225 if (!isset($this->marks[$first_mark])) {
226 throw new PE('The first mark could not be found.', PE::E_MARK_NOT_SET);
227 } elseif (!isset($this->marks[$second_mark])) {
228 throw new PE('The second mark could not be found.', PE::E_MARK_NOT_SET);
229 } else {
230 //Hide illogical array to string conversion notices
231 ob_start();
232 $diff = array_diff_assoc($this->marks[$second_mark], $this->marks[$first_mark]);
233 ob_end_clean();
234
235 return $diff;
236 }
237 }
238
239 /**
240 * Shows the diff on the specified key
241 *
242 * @author Art <a.molcanovas@gmail.com>
243 * @param string $key The key
244 * @param string $first_mark The first mark
245 * @param string $second_mark The second mark
246 * @return array
247 * @throws PE If the key isn't found in one or both marks
248 */
249 function diff_on_key($key, $first_mark, $second_mark) {
250 if (!isset($this->marks[$first_mark])) {
251 throw new PE('The first mark could not be found.', PE::E_MARK_NOT_SET);
252 } elseif (!isset($this->marks[$second_mark])) {
253 throw new PE('The second mark could not be found.', PE::E_MARK_NOT_SET);
254 } elseif (!isset($this->marks[$first_mark][$key])) {
255 throw new PE('Invalid $key.', PE::E_KEY_INVALID);
256 } else {
257 $fm = $this->marks[$first_mark][$key];
258 $sm = $this->marks[$second_mark][$key];
259
260 $ret = [$first_mark => $fm,
261 $second_mark => $sm
262 ];
263
264 if (is_numeric($fm)) {
265 $ret[self::P_DIFF] = abs($fm - $sm);
266 } elseif (is_array($fm)) {
267 $ret[self::P_DIFF] = array_diff_assoc($sm, $fm);
268 } else {
269 $ret[self::P_DIFF] = '[values not numeric or arrays]';
270 }
271
272 return $ret;
273 }
274 }
275
276 /**
277 * Returns the marks set, as well as their data
278 *
279 * @return array
280 */
281 function getMarks() {
282 return $this->marks;
283 }
284 }