Ubiquity 2.5.2
php rapid development framework
Loading...
Searching...
No Matches
Debugger.php
Go to the documentation of this file.
1<?php
2namespace Ubiquity\debug;
3
5use Ubiquity\controllers\admin\popo\ComposerDependency;
14use Ubiquity\utils\base\UArray;
15
25class Debugger {
26
27 const CONTEXT_VARIABLES = [
28 'globals' => [
29 '_SESSION',
30 '_POST',
31 '_GET',
32 '_REQUEST',
33 '_SERVER',
34 '_COOKIE',
35 '_FILES'
36 ],
37 'toRemove' => [
38 'e',
39 'config',
40 'sConfig'
41 ]
42 ];
43
44 private static $variables = [];
45
49 public static function start(&$config, int $level = E_ALL) {
50 self::setErrorLevel($level);
51 if ($level > 0) {
52 $config['onError'] = function ($code, $message = null, $controllerInstance = null) {
53 switch ($code) {
54 case 404:
55 case 500:
56 throw new \Exception($message);
57 break;
58 }
59 };
60 \ob_start(array(
61 __class__,
62 '_error_handler'
63 ));
64 \set_exception_handler(function (\Throwable $ex) {
65 self::showException($ex);
66 });
67 }
68 }
69
75 public static function setErrorLevel(int $level = E_ALL) {
76 \error_reporting($level);
77 if ($level > 0) {
78 \ini_set('display_errors', '1');
79 }
80 }
81
82 public static function _error_handler($buffer) {
83 $e = \error_get_last();
84 if ($e) {
85 if ($e['file'] != 'xdebug://debug-eval' && ! UResponse::isJSON()) {
86 $file = $e['file'];
87 $code = self::getFileContent($file);
88 $error = $e['message'];
89 $type = TypeError::asString($e['type']);
90 $line = $e['line'];
91 $message = self::loadView('error', [
92 'file' => $file,
93 'error' => $error,
94 'code' => $code,
95 'line' => $line,
96 'type' => $type
97 ]);
98 switch ($e['type']) {
99 case E_ERROR:
100 case E_PARSE:
101 case E_COMPILE_ERROR:
102 case E_WARNING:
103 return self::getErrorFromValues($file, $line, $error, $type, '', false);
104
105 default:
106 return self::wrapResponse(\str_replace($e['message'], "", $buffer) . $message);
107 }
108 } else {
109 return self::wrapResponse(\str_replace($e['message'] ?? '', "", $buffer));
110 }
111 }
112 return $buffer;
113 }
114
115 public static function showException(\Throwable $e) {
116 $file = $e->getFile();
117 $line = $e->getLine();
118 $traces = $e->getTrace();
119 echo self::getErrorFromValues($file, $line, $e->getMessage(), $e, self::showTraces($traces), true);
120 }
121
122 private static function showTraces($traces) {
123 self::$variables = [];
124 $tracesContent = '';
125 foreach ($traces as $index => $trace) {
126 $tracesContent .= self::showTrace($trace, $index);
127 }
128 if ($tracesContent != null) {
129 return self::loadView('traces', [
130 'content' => $tracesContent,
131 'variables' => json_encode(self::$variables),
132 'count' => \count($traces)
133 ]);
134 }
135 return '';
136 }
137
138 private static function getGlobales($variables) {
139 $result = [];
140 foreach (self::CONTEXT_VARIABLES['globals'] as $k) {
141 $result[$k] = $variables[$k];
142 }
143 return $result;
144 }
145
146 private static function getLocales() {
147 $variables = [];
148 $errors = [];
149 $ctrl = Startup::getController();
150 $variables['Request']['controller'] = $ctrl;
151 if (isset($ctrl)) {
152 if (! \class_exists($ctrl)) {
153 $errors['Request']['controller'] = self::errorFlag("Controller $ctrl does not exist!");
154 }
155 $action = Startup::getAction();
156 if (isset($action)) {
157 $variables['Request']['action'] = $action;
158 if (! \method_exists($ctrl, $action)) {
159 $errors['Request']['action'] = self::errorFlag("Method $action does not exist on Controller $ctrl!");
160 }
161 $variables['Request']['params'] = Startup::getActionParams();
162 $variables['Request']['method'] = URequest::getMethod();
163 $path = Framework::getUrl();
164 $variables['Request']['url'] = $path;
165 $route = Router::getRouteInfo($path);
166 if ($route === false) {
167 $errors['Route'] = self::errorFlag("No route found for the url $path", 'exclamation circle orange');
168 }
169 $variables['Route'] = $route;
170 }
171 }
172 $variables['Application']['cacheSystem'] = Framework::getCacheSystem();
173 $variables['Application']['AnnotationsEngine'] = Framework::getAnnotationsEngine();
174 $variables['Application']['applicationDir'] = Startup::getApplicationDir();
175 $variables['Application']['Ubiquity-version'] = Framework::getVersion();
176 $variables['Config'] = Startup::$config;
177 $variables['system']['php'] = \phpversion();
178 $variables['system']['os'] = \php_uname();
179 $variables['system']['extensions'] = \implode(', ', \get_loaded_extensions());
180 return [
181 'variables' => $variables,
182 'errors' => $errors
183 ];
184 }
185
186 private static function displayVar($variable) {
187 if (! \is_string($variable) && ! \is_array($variable) && \is_callable($variable)) {
188 return UIntrospection::closure_dump($variable);
189 }
190 if (\is_object($variable)) {
191 return \get_class($variable) . '@' . \spl_object_hash($variable);
192 }
193 if (! self::isRecursive($variable)) {
194 return \var_export($variable, true);
195 }
196 return 'Recursive array!';
197 }
198
199 private static function isRecursive($array) {
200 $dump = \print_r($array, true);
201 return \strpos($dump, '*RECURSION*') !== false;
202 }
203
204 private static function errorFlag($message, $type = 'exclamation triangle red') {
205 return "&nbsp;<i class='ui $type icon var_error' data-content='$message'></i>";
206 }
207
208 private static function showAllVariables() {
209 $l = self::getLocales();
210 $g = self::getGlobales($GLOBALS);
211 $locales = self::showVariables($l['variables'], $l['errors']);
212 $globales = self::showVariables($g);
213 return self::loadView('all_variables', compact('locales', 'globales'));
214 }
215
216 private static function showVariables($variables, $errors = []) {
217 $keys = \array_keys($variables);
218 $names = '';
219 $variables_elements = '';
220 foreach ($keys as $i => $k) {
221 $active = '';
222 $v = $variables[$k] ?? '';
223 if (\is_array($v)) {
224 $error = $errors[$k] ?? [];
225 $ve = self::showVariable($k, $v, $error, 1);
226 } else {
227 $error = $errors[$k] ?? '';
228 $ve = "<span class='ui label'><pre>" . self::displayVar($v) . "</pre>$error</span>";
229 }
230 if ($i === 0) {
231 $first_var = "<div class='variable'>$ve</div>";
232 $active = 'active';
233 }
234 $names .= "<a class='item display_var $active' data-id='$k'>$k</a>";
235 $variables_elements .= "<div id='ve-$k' class='variable'>$ve</div>";
236 }
237 return self::loadView('menu_variables', compact('names', 'variables_elements', 'first_var'));
238 }
239
240 private static function showVariable($name, array $variables, array $errors = [], $level = 1) {
241 $values = '';
242 $count = \count($variables);
243 if ($count > 0) {
244 foreach ($variables as $k => $v) {
245 if ($v != $variables) {
246
247 if (\is_array($v)) {
248 $error = $errors[$k] ?? [];
249 $v = self::showVariable($k, $v, $error, $level + 1);
250 } else {
251 $error = $errors[$k] ?? '';
252 $v = '<span class="ui label"><pre>' . self::displayVar($v) . "</pre>$error</span>";
253 }
254 $values .= "<tr><td><b>$k</b></td><td>" . $v . "</td></tr>";
255 }
256 }
257 return self::loadView('variable', [
258 'level' => $level,
259 'variables' => $values,
260 'name' => $name,
261 'count' => $count
262 ]);
263 }
264 return '<span class="ui label">empty</span>';
265 }
266
267 private static function showTrace($trace, $index) {
268 $callFunction = $trace['function'] ?? '';
269 $callMethod = null;
270 $line = $trace['line'] ?? 0;
271 $callClass = $trace['class'] ?? 'no class';
272 $args = $trace['args'] ?? [];
273 $file = $trace['file'] ?? 'no file';
274 $attr = UString::cleanAttribute($callClass . "." . $callFunction);
275 self::$variables[$attr] = [];
276 if ($file != null && \file_exists($file)) {
277 $class = ClassUtils::getClassFullNameFromFile($file);
278 if ($class != null && $class != '\\' && (\class_exists($class) || \trait_exists($class))) {
279 $method = UIntrospection::getMethodAtLine($class, $line);
280 if ($callClass !== 'no class' && \method_exists($callClass, $callFunction)) {
281 $callMethod = new \ReflectionMethod($callClass, $callFunction);
282 }
283 if ($callMethod != null) {
284 $code = UIntrospection::getMethodCode($method, file($trace['file']));
285
286 $attributes = $callMethod->getParameters();
287 $effectiveArguments = self::getCallbackArguments($file, $line, $callFunction);
288 foreach ($attributes as $i => $param) {
289 $argValue = $args[$i] ?? '';
290 $arg = $effectiveArguments[$i] ?? ('$' . $param->getName());
291 self::$variables[$attr]['$' . $param->getName()] = [
292 'name' => $effectiveArguments[$i] ?? '',
293 'value' => self::displayVar($argValue)
294 ];
295 $code = \preg_replace('#(\W*)(' . \preg_quote($arg) . ')(\W+)#', '$1<mark>$2</mark>$3', $code);
296 }
297 $start = $method->getStartLine();
298 $countParams = count(self::$variables[$attr] ?? []);
299 $parameters = '';
300 if ($countParams > 0) {
301 $parameters = self::loadView('parameters', [
302 'count' => $countParams,
303 'variables' => self::getMethodParametersTable($attr)
304 ]);
305 }
306 return self::loadView('trace', [
307 'in' => $method->getName(),
308 'function' => $callFunction,
309 'line' => $line,
310 'class' => $class,
311 'code' => $code,
312 'start' => $start,
313 'active' => '',
314 'attr' => $attr,
315 'parameters' => $parameters,
316 'index' => $index
317 ]);
318 }
319 }
320 $code = self::getFileContent($file);
321 $start = 1;
322 return self::loadView('trace', [
323 'in' => $file,
324 'function' => $callFunction,
325 'line' => $line,
326 'class' => '',
327 'code' => $code,
328 'start' => $start,
329 'active' => '',
330 'attr' => $attr,
331 'parameters' => '',
332 'index' => $index
333 ]);
334 }
335 return '';
336 }
337
338 private static function getCallbackArguments($file, $lineNumber, $callbackName) {
339 $fileContent = \file($file);
340 return UIntrospection::getMethodEffectiveParameters('<?php ' . $fileContent[$lineNumber - 1], $callbackName);
341 }
342
343 private static function getMethodParametersTable($functionName) {
344 $res = '';
345 if (isset(self::$variables[$functionName]) && \is_array(self::$variables[$functionName])) {
346 foreach (self::$variables[$functionName] as $name => $value) {
347 $res .= "<tr><td><b>$name</b></td><td><pre>" . $value['value'] . "</pre></td></tr>";
348 }
349 }
350 return $res;
351 }
352
353 private static function getErrorFromValues($file, $line, $errorMessage, $errorType, $traces = '', $introspect = true) {
354 \restore_error_handler();
355 \restore_exception_handler();
356 $code = null;
357 $vars = self::showAllVariables();
358 $controller_action = \basename($file);
359 if ($introspect) {
360 $class = ClassUtils::getClassFullNameFromFile($file, true);
361 if ($class !== null) {
362 $controller_action = $class;
363 $method = UIntrospection::getMethodAtLine($class, $line);
364 if ($method != null) {
365 $start = $method->getStartLine();
366 $code = UIntrospection::getMethodCode($method, file($file));
367 $controller_action .= '::' . $method->getName();
368 }
369 }
370 }
371 if ($code == null) {
372 $start = 1;
373 $code = self::getFileContent($file);
374 }
375 $type = TypeError::asString($errorType);
376 $message = self::loadView('error', [
377 'file' => $file,
378 'error' => $errorMessage,
379 'code' => $code,
380 'line' => $line,
381 'type' => $type,
382 'start' => $start,
383 'traces' => $traces,
384 'vars' => $vars,
385 'controller_action' => $controller_action
386 ]);
387 return self::wrapResponse($message);
388 }
389
390 private static function wrapResponse($content) {
391 return self::getHeader() . $content . self::getFooter();
392 }
393
394 private static function getHeader() {
395 return '<div class="ui container">';
396 }
397
398 private static function getFooter() {
399 return '</div>' . '<script type="text/javascript">' . \file_get_contents(__DIR__ . '/js/loader.js') . '</script>';
400 }
401
402 private static function getFileContent($file) {
403 if (\file_exists($file)) {
404 return \htmlentities(\file_get_contents($file));
405 }
406 return "$file not found!";
407 }
408
409 private static function loadView($name, $data) {
410 $content = \file_get_contents(__DIR__ . '/views/' . $name . '.html');
411 foreach ($data as $key => $value) {
412 $content = str_replace('{{' . $key . '}}', $value, $content);
413 }
414 return $content;
415 }
416}
Manipulates class and namespace names Ubiquity\cache$ClassUtils This class is part of Ubiquity.
Starts the framework.
Definition Startup.php:19
Ubiquity\utils\base$UIntrospection This class is part of Ubiquity.
String utilities.
Definition UString.php:15
Http Request utilities, wrapper for accessing to $_GET, $_POST and php://input.
Definition URequest.php:18
Http Response utilities.
Definition UResponse.php:17