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