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 * Instantiates the class
163 *
164 * @author Art <a.molcanovas@gmail.com>
165 *
166 * @return Profiler
167 */
168 static function Profiler() {
169 return new Profiler();
170 }
171
172 /**
173 * Sets a profiler mark
174 *
175 * @author Art <a.molcanovas@gmail.com>
176 *
177 * @param string $identifier How to identify this mark
178 *
179 * @return Profiler
180 */
181 function mark($identifier) {
182 $m = &$this->marks[$identifier];
183 $r = &\Alo::$router;
184
185 $m = [
186 self::P_MICROTIME => microtime(true),
187 self::P_SESSION_DATA => \Alo::$session ? \Alo::$session->getAll() : false,
188 self::P_GET => $_GET,
189 self::P_POST => $_POST,
190 self::P_FILES => $_FILES,
191 self::P_CONTROLLER => $r->getController(),
192 self::P_CONTROLLER_METHOD => $r->getMethod(),
193 self::P_PORT => $r->getPort(),
194 self::P_REMOTE_ADDR => $r->getRemoteAddr(),
195 self::P_REQUEST_METHOD => $r->getRequestMethod(),
196 self::P_REQUEST_SCHEME => $r->getRequestScheme(),
197 self::P_SERVER_ADDR => $r->getServerAddr(),
198 self::P_SERVER_NAME => $r->getServerName(),
199 self::P_HEADERS => \getallheaders(),
200 self::P_REQUEST_PATH => $r->getPath(),
201 self::P_MEMORY_USAGE => memory_get_usage(false),
202 self::P_REAL_MEMORY_USAGE => memory_get_usage(true)
203 ];
204
205 return $this;
206 }
207
208 /**
209 * Returns absolute microtime difference between the two marks
210 *
211 * @author Art <a.molcanovas@gmail.com>
212 *
213 * @param string $first_mark The first mark identifier
214 * @param string $second_mark The second mark identifier
215 *
216 * @throws PE When one of the marks cannot be found
217 * @return float
218 */
219 function timeBetween($first_mark, $second_mark) {
220 if(!isset($this->marks[$first_mark])) {
221 throw new PE('The first mark could not be found.', PE::E_MARK_NOT_SET);
222 } elseif(!isset($this->marks[$second_mark])) {
223 throw new PE('The second mark could not be found.', PE::E_MARK_NOT_SET);
224 } else {
225 return abs($this->marks[$first_mark][self::P_MICROTIME] - $this->marks[$second_mark][self::P_MICROTIME]);
226 }
227 }
228
229 /**
230 * Returns the difference between the two marks, i.e. all key/value pairs in $second_mark that differ from those
231 * of $first_mark
232 *
233 * @author Art <a.molcanovas@gmail.com>
234 *
235 * @param string $first_mark The first mark identifier
236 * @param string $second_mark The second mark identifier
237 *
238 * @throws PE When one of the marks cannot be found
239 * @return array
240 */
241 function diff($first_mark, $second_mark) {
242 if(!isset($this->marks[$first_mark])) {
243 throw new PE('The first mark could not be found.', PE::E_MARK_NOT_SET);
244 } elseif(!isset($this->marks[$second_mark])) {
245 throw new PE('The second mark could not be found.', PE::E_MARK_NOT_SET);
246 } else {
247 //Hide illogical array to string conversion notices
248 ob_start();
249 $diff = array_diff_assoc($this->marks[$second_mark], $this->marks[$first_mark]);
250 ob_end_clean();
251
252 return $diff;
253 }
254 }
255
256 /**
257 * Shows the diff on the specified key
258 *
259 * @author Art <a.molcanovas@gmail.com>
260 *
261 * @param string $key The key
262 * @param string $first_mark The first mark
263 * @param string $second_mark The second mark
264 *
265 * @return array
266 * @throws PE If the key isn't found in one or both marks
267 */
268 function diff_on_key($key, $first_mark, $second_mark) {
269 if(!isset($this->marks[$first_mark])) {
270 throw new PE('The first mark could not be found.', PE::E_MARK_NOT_SET);
271 } elseif(!isset($this->marks[$second_mark])) {
272 throw new PE('The second mark could not be found.', PE::E_MARK_NOT_SET);
273 } elseif(!isset($this->marks[$first_mark][$key])) {
274 throw new PE('Invalid $key.', PE::E_KEY_INVALID);
275 } else {
276 $fm = $this->marks[$first_mark][$key];
277 $sm = $this->marks[$second_mark][$key];
278
279 $ret = [$first_mark => $fm,
280 $second_mark => $sm
281 ];
282
283 if(is_numeric($fm)) {
284 $ret[self::P_DIFF] = abs($fm - $sm);
285 } elseif(is_array($fm)) {
286 $ret[self::P_DIFF] = array_diff_assoc($sm, $fm);
287 } else {
288 $ret[self::P_DIFF] = '[values not numeric or arrays]';
289 }
290
291 return $ret;
292 }
293 }
294
295 /**
296 * Returns the marks set, as well as their data
297 *
298 * @return array
299 */
300 function getMarks() {
301 return $this->marks;
302 }
303 }