| Total Complexity | 57 |
| Total Lines | 492 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Complex classes like ExceptionHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ExceptionHandler, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 35 | class ExceptionHandler |
||
| 36 | { |
||
| 37 | /** |
||
| 38 | * Gets caught buffer of memory. |
||
| 39 | * |
||
| 40 | * @var mixed $caughtBuffer |
||
| 41 | */ |
||
| 42 | protected $caughtBuffer; |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Gets caught lenght of buffer. |
||
| 46 | * |
||
| 47 | * @var int $caughtLength |
||
| 48 | */ |
||
| 49 | protected $caughtLength; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * Gets the charset. By default UTF-8. |
||
| 53 | * |
||
| 54 | * @var string $charset |
||
| 55 | */ |
||
| 56 | protected $charset; |
||
| 57 | |||
| 58 | /** |
||
| 59 | * Gets activation of debugging. |
||
| 60 | * |
||
| 61 | * @var bool $debug |
||
| 62 | */ |
||
| 63 | protected $debug; |
||
| 64 | |||
| 65 | /** |
||
| 66 | * Gets the file link format. |
||
| 67 | * |
||
| 68 | * @var string $fileLinkFormat |
||
| 69 | */ |
||
| 70 | protected $fileLinkFormat; |
||
| 71 | |||
| 72 | /** |
||
| 73 | * Gets an error handler. |
||
| 74 | * |
||
| 75 | * @var string $handler |
||
| 76 | */ |
||
| 77 | protected $handler; |
||
| 78 | |||
| 79 | /** |
||
| 80 | * Register the exception handler. |
||
| 81 | * |
||
| 82 | * @param bool $debug |
||
| 83 | * @param string|null $charset |
||
| 84 | * @param string|null $fileLinkformat |
||
| 85 | * |
||
| 86 | * @return static |
||
| 87 | */ |
||
| 88 | public static function register($debug = true, $charset = null, $fileLinkFormat = null) |
||
| 89 | { |
||
| 90 | $handler = new static($debug, $charset, $fileLinkFormat); |
||
| 91 | |||
| 92 | set_exception_handler([$handler, 'handle']); |
||
| 93 | |||
| 94 | return $handler; |
||
| 95 | } |
||
| 96 | |||
| 97 | /** |
||
| 98 | * Constructor. Initialize the ExceptionHandler instance. |
||
| 99 | * |
||
| 100 | * @param bool $debug |
||
| 101 | * @param string|null $charset |
||
| 102 | * @param string|null $fileLinkformat |
||
| 103 | * |
||
| 104 | * @return void |
||
| 105 | */ |
||
| 106 | public function __construct(bool $debug = true, string $charset = null, $fileLinkFormat = null) |
||
| 107 | { |
||
| 108 | $this->debug = $debug; |
||
| 109 | $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; |
||
| 110 | $this->fileLinkFormat = $fileLinkFormat; |
||
| 111 | } |
||
| 112 | |||
| 113 | /** |
||
| 114 | * Sets a user exception handler. |
||
| 115 | * |
||
| 116 | * @param \Callable $handler |
||
|
|
|||
| 117 | * |
||
| 118 | * @return \Callable|null |
||
| 119 | */ |
||
| 120 | public function setHandler(Callable $handler) |
||
| 121 | { |
||
| 122 | $oldHandler = $this->handler; |
||
| 123 | $this->handler = $handler; |
||
| 124 | |||
| 125 | return $oldHandler; |
||
| 126 | } |
||
| 127 | |||
| 128 | /** |
||
| 129 | * Sets the format for links to source files. |
||
| 130 | * |
||
| 131 | * @param string|FileLinkFormatter $fileLinkFormat |
||
| 132 | * |
||
| 133 | * @return string |
||
| 134 | */ |
||
| 135 | public function setFileLinkFormat($fileLinkFormat) |
||
| 141 | } |
||
| 142 | |||
| 143 | /** |
||
| 144 | * Sends a response for the given Exception. |
||
| 145 | * |
||
| 146 | * How does it work: |
||
| 147 | * First, the exception is handled by system exception handler, then by the user exception handler. |
||
| 148 | * The latter has priority and any exit from the first one is canceled. |
||
| 149 | * |
||
| 150 | * @param \Exception $exception |
||
| 151 | * |
||
| 152 | * @return void |
||
| 153 | */ |
||
| 154 | public function handler(Exception $exception) |
||
| 155 | { |
||
| 156 | if ($this->handler === null && $exception instanceof OutOfMemoryException) { |
||
| 157 | $this->sendPhpResponse($exception); |
||
| 158 | } |
||
| 159 | |||
| 160 | $caughtLength = $this->caughtLength = 0; |
||
| 161 | |||
| 162 | ob_start(function ($buffer) { |
||
| 163 | $this->caughtBuffer = $buffer; |
||
| 164 | |||
| 165 | return ''; |
||
| 166 | }); |
||
| 167 | |||
| 168 | $this->sendPhpResponse($exception); |
||
| 169 | |||
| 170 | if (isset($this->caughtBuffer[0])) { |
||
| 171 | ob_start(function ($buffer) { |
||
| 172 | if ($this->caughtLength) { |
||
| 173 | $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength); |
||
| 174 | |||
| 175 | if (isset($cleanBuffer[0])) { |
||
| 176 | $buffer = $cleanBuffer; |
||
| 177 | } |
||
| 178 | } |
||
| 179 | |||
| 180 | return $buffer; |
||
| 181 | |||
| 182 | }); |
||
| 183 | |||
| 184 | echo $this->caughtBuffer; |
||
| 185 | // Return the length of the output buffer. |
||
| 186 | $caughtLength = ob_get_length(); |
||
| 187 | } |
||
| 188 | |||
| 189 | $this->caughtBuffer = null; |
||
| 190 | |||
| 191 | try { |
||
| 192 | ($this->handler)($exception); |
||
| 193 | $caughtLength = $this->caughtLength; |
||
| 194 | } catch (Exception $e) { |
||
| 195 | if ( ! $caughtLength) { |
||
| 196 | throw $e; |
||
| 197 | } |
||
| 198 | } |
||
| 199 | } |
||
| 200 | |||
| 201 | /** |
||
| 202 | * Sends the error associated with the given Exception as a plain PHP response. |
||
| 203 | * |
||
| 204 | * This method uses plain php functions as header and echo to generate the output |
||
| 205 | * response. |
||
| 206 | * |
||
| 207 | * @param \Exception|\Syscodes\Debug\FlattenExceptions\FlattenException $exception An \Exception or \FlattenException instance |
||
| 208 | * |
||
| 209 | * @return string The HTML content as a string |
||
| 210 | */ |
||
| 211 | public function sendPhpResponse($exception) |
||
| 212 | { |
||
| 213 | if ( ! $exception instanceof FlattenException) { |
||
| 214 | $exception = FlattenException::make($exception); |
||
| 215 | } |
||
| 216 | |||
| 217 | if ( ! headers_sent()) { |
||
| 218 | header(sprintf('HTTP/1.0 %s', $exception->getStatusCode())); |
||
| 219 | |||
| 220 | foreach ($exception->getHeaders() as $name => $value) { |
||
| 221 | header($name.':'.$value, false); |
||
| 222 | } |
||
| 223 | |||
| 224 | header('Content-Type: text/html; charset='.$this->charset); |
||
| 225 | } |
||
| 226 | |||
| 227 | echo $this->design($this->getContent($exception), $this->getStylesheet()); |
||
| 228 | } |
||
| 229 | |||
| 230 | /** |
||
| 231 | * Gets the full HTML content associated with the given exception. |
||
| 232 | * |
||
| 233 | * @param \Exception|\Syscodes\Debug\FlattenExceptions\FlattenException $exception An \Exception or \FlattenException instance |
||
| 234 | * |
||
| 235 | * @return string The HTML content as a string |
||
| 236 | */ |
||
| 237 | public function getHtmlResponse($exception) |
||
| 238 | { |
||
| 239 | if ( ! $exception instanceof FlattenException) { |
||
| 240 | $exception = FlattenException::make($exception); |
||
| 241 | } |
||
| 242 | |||
| 243 | echo $this->design($this->getContent($exception), $this->getStylesheet()); |
||
| 244 | } |
||
| 245 | |||
| 246 | /** |
||
| 247 | * Layout HTML for gets the content and style css. |
||
| 248 | * |
||
| 249 | * @param string $content |
||
| 250 | * @param string $styleCss |
||
| 251 | * |
||
| 252 | * @return string |
||
| 253 | */ |
||
| 254 | private function design($content, $styleCss) |
||
| 269 | </body> |
||
| 270 | </html> |
||
| 271 | EOF; |
||
| 272 | } |
||
| 273 | |||
| 274 | /** |
||
| 275 | * Gets the HTML content associated with the given exception. |
||
| 276 | * |
||
| 277 | * @param \Syscodes\Debug\FlattenExceptions\FlattenException $exception |
||
| 278 | * |
||
| 279 | * @return string The HTML content as a string |
||
| 280 | */ |
||
| 281 | public function getContent(FlattenException $exception) |
||
| 282 | { |
||
| 283 | switch ($exception->getStatusCode()) { |
||
| 284 | case 404: |
||
| 285 | $title = 'Sorry, the page you are looking to could not be found'; |
||
| 286 | break; |
||
| 287 | default: |
||
| 288 | $title = 'Whoops, looks like something went wrong'; |
||
| 289 | } |
||
| 290 | |||
| 291 | if ( ! $this->debug) { |
||
| 292 | return <<<EOF |
||
| 293 | <div class="container"> |
||
| 294 | <h1>$title</h1> |
||
| 295 | </div> |
||
| 296 | EOF; |
||
| 297 | } |
||
| 298 | |||
| 299 | $content = ''; |
||
| 300 | |||
| 301 | try { |
||
| 302 | $count = count($exception->getAllPrevious()); |
||
| 303 | $total = $count + 1; |
||
| 304 | |||
| 305 | foreach ($exception->toArray() as $position => $e) { |
||
| 306 | $index = $count - $position + 1; |
||
| 307 | $class = $this->formatClass($e['class']); |
||
| 308 | $message = nl2br($this->escapeHtml($e['message'])); |
||
| 309 | $content .= sprintf(<<<'EOF' |
||
| 310 | <div class="trace"> |
||
| 311 | <table> |
||
| 312 | <tr class="trace-head"><th> |
||
| 313 | <h3 class="trace-class"> |
||
| 314 | <span class="text-muted">(%d/%d)</span> |
||
| 315 | <span class="exception_title">%s</span> |
||
| 316 | </h3> |
||
| 317 | <p class="break-long-words trace-message">%s</p> |
||
| 318 | </th></tr> |
||
| 319 | EOF |
||
| 320 | , $index, $total, $class, $message); |
||
| 321 | |||
| 322 | foreach ($e['trace'] as $trace) { |
||
| 323 | $content .= '<tr><td>'; |
||
| 324 | |||
| 325 | if ($trace['function']) { |
||
| 326 | $content .= sprintf('from <span class="trace-class">%s</span><span class="trace-type">%s</span><span class="trace-method">%s</span>(<span class="trace-arguments">%s</span>)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); |
||
| 327 | } |
||
| 328 | |||
| 329 | if (isset($trace['file']) && isset($trace['line'])) { |
||
| 330 | $content .= $this->formatPath($trace['file'], $trace['line']); |
||
| 331 | } |
||
| 332 | |||
| 333 | $content .= "</td></tr>\n"; |
||
| 334 | } |
||
| 335 | |||
| 336 | $content .= "</table>\n</div>\n"; |
||
| 337 | } |
||
| 338 | } catch (Exception $e) { |
||
| 339 | if ($this->debug) { |
||
| 340 | $e = FlattenException::make($e); |
||
| 341 | $title = sprintf('Exception thrown when handling an exception: (%s: %s)', |
||
| 342 | $e->getClass(), |
||
| 343 | $this->escapeHtml($e->getMessage()) |
||
| 344 | ); |
||
| 345 | } else { |
||
| 346 | $title = 'Whoops, looks like something went wrong'; |
||
| 347 | } |
||
| 348 | } |
||
| 349 | |||
| 350 | return <<<EOF |
||
| 351 | <div class="exception"> |
||
| 352 | <div class="container"> |
||
| 353 | <div class="exception-wrapper"> |
||
| 354 | <h1 class="break-long-words exception-message">$title</h1> |
||
| 355 | </div> |
||
| 356 | </div> |
||
| 357 | </div> |
||
| 358 | |||
| 359 | <div class="container"> |
||
| 360 | $content |
||
| 361 | </div> |
||
| 362 | EOF; |
||
| 363 | } |
||
| 364 | |||
| 365 | public function getStylesheet() |
||
| 366 | { |
||
| 367 | if ( ! $this->debug) { |
||
| 368 | return <<<'EOF' |
||
| 369 | body { background-color: #fff; color: #222; font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; overflow: hidden; } |
||
| 370 | .container { display: flex; align-items: center; justify-content: center; height: 100vh; width: 100vw; } |
||
| 371 | h1 { color: #586d7e; font-size: 2em; text-shadow: none; word-break: break-all; word-break: break-word; } |
||
| 372 | EOF; |
||
| 373 | } |
||
| 374 | |||
| 375 | return <<<'EOF' |
||
| 376 | body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; margin: 0; padding-bottom: 45px; } |
||
| 377 | |||
| 378 | a { cursor: pointer; text-decoration: none; } |
||
| 379 | a:hover { text-decoration: underline; } |
||
| 380 | abbr[title] { border-bottom: none; cursor: help; text-decoration: none; } |
||
| 381 | |||
| 382 | code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; } |
||
| 383 | |||
| 384 | table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; } |
||
| 385 | table { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; } |
||
| 386 | table th, table td { border: solid #E0E0E0; border-width: 1px 0; padding: 8px 10px; } |
||
| 387 | table th { background-color: #E0E0E0; font-weight: bold; text-align: left; } |
||
| 388 | |||
| 389 | .hidden-xs-down { display: none; } |
||
| 390 | .block { display: block; } |
||
| 391 | .break-long-words { -ms-word-break: break-all; word-break: break-all; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; } |
||
| 392 | .text-muted { color: #999; } |
||
| 393 | |||
| 394 | .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; } |
||
| 395 | .container::after { content: ""; display: table; clear: both; } |
||
| 396 | |||
| 397 | .exception { background: #B0413E; border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 30px; } |
||
| 398 | |||
| 399 | .exception-wrapper { display: flex; align-items: center; min-height: 70px; } |
||
| 400 | .exception-message { flex-grow: 1; padding: 30px 0; text-shadow: none; } |
||
| 401 | .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; } |
||
| 402 | .exception-message.long { font-size: 18px; } |
||
| 403 | .exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; } |
||
| 404 | .exception-message a:hover { border-bottom-color: #ffffff; } |
||
| 405 | |||
| 406 | .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; } |
||
| 407 | |||
| 408 | .trace + .trace { margin-top: 30px; } |
||
| 409 | .trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; } |
||
| 410 | |||
| 411 | .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; } |
||
| 412 | |||
| 413 | .trace-file, .trace-file a { color: #222; margin-top: 3px; font-size: 13px; } |
||
| 414 | .trace-class { color: #B0413E; } |
||
| 415 | .trace-type { padding: 0 2px; } |
||
| 416 | .trace-method { color: #B0413E; font-weight: bold; } |
||
| 417 | .trace-args { color: #777; font-weight: normal; padding-left: 2px; } |
||
| 418 | |||
| 419 | @media (min-width: 575px) { |
||
| 420 | .hidden-xs-down { display: initial; } |
||
| 421 | } |
||
| 422 | EOF; |
||
| 423 | } |
||
| 424 | |||
| 425 | /** |
||
| 426 | * Gets the format class where the exception. |
||
| 427 | * |
||
| 428 | * @param string $class |
||
| 429 | * |
||
| 430 | * @return string |
||
| 431 | */ |
||
| 432 | private function formatClass($class) |
||
| 433 | { |
||
| 434 | $parts = explode('\\', $class); |
||
| 435 | |||
| 436 | return sprintf('<abbr title="%s">%s</abbr>', $class, array_pop($parts)); |
||
| 437 | } |
||
| 438 | |||
| 439 | /** |
||
| 440 | * Gets the path file with you line code. |
||
| 441 | * |
||
| 442 | * @param string $path |
||
| 443 | * @param int $line |
||
| 444 | * |
||
| 445 | * @return string |
||
| 446 | */ |
||
| 447 | private function formatPath($path, $line) |
||
| 448 | { |
||
| 449 | $file = $this->escapeHtml(preg_match('#[^/\\\\]*+$#', $path, $file) ? $file[0] : $path); |
||
| 450 | $frmt = $this->fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); |
||
| 451 | |||
| 452 | if ( ! $frmt) { |
||
| 453 | return sprintf('<span class="block trace-file">in <span title="%s%3$s"><strong>%s</strong>%s</span></span>', |
||
| 454 | $this->escapeHtml($path), |
||
| 455 | $file, |
||
| 456 | 0 < $line ? ' line '.$line : '' |
||
| 457 | ); |
||
| 458 | } |
||
| 459 | |||
| 460 | if (is_string($frmt)) { |
||
| 461 | $index = strpos($f = $frmt, '&', max(strrpos($f, '%f'), strrpos($f, '%l')) ?: strlen($f)); |
||
| 462 | $frmt = [substr($f, 0, $index)] + preg_split('/&([^>]++)>/', substr($f, $index), -1, PREG_SPLIT_DELIM_CAPTURE); |
||
| 463 | |||
| 464 | for ($index = 1; isset($frmt[$index]); ++$index) { |
||
| 465 | if (strpos($path, $k = $frmt[$index++])) { |
||
| 466 | $path = substr_replace($path, $frmt[$index], 0, strlen($k)); |
||
| 467 | break; |
||
| 468 | } |
||
| 469 | } |
||
| 470 | |||
| 471 | $data = strstr($frmt[0], ['%f' => $file, '%l' => $line]); |
||
| 472 | } else { |
||
| 473 | try { |
||
| 474 | $data = $frmt->format($file, $line); |
||
| 475 | } catch (Exception $e) { |
||
| 476 | return sprintf('<span class="block trace-file-path">in <span title="%s%3$s"><strong>%s</strong>%s</span></span>', |
||
| 477 | $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : ''); |
||
| 478 | } |
||
| 479 | } |
||
| 480 | |||
| 481 | return sprintf('<span class="block trace-file">in <a href="%s" title="Go to source"><b>%s</b>%s</a></span>', |
||
| 482 | $this->escapeHtml($data), $file, $line > 0 ? ' line '.$line : ''); |
||
| 483 | } |
||
| 484 | |||
| 485 | /** |
||
| 486 | * Formats an array as a string. |
||
| 487 | * |
||
| 488 | * @param array $args |
||
| 489 | * |
||
| 490 | * @return string |
||
| 491 | */ |
||
| 492 | private function formatArgs(array $args) |
||
| 515 | } |
||
| 516 | |||
| 517 | /** |
||
| 518 | * Gets HTML-encode as a string. |
||
| 519 | * |
||
| 520 | * @param string $string |
||
| 521 | * |
||
| 522 | * @return string |
||
| 523 | */ |
||
| 524 | private function escapeHtml($string) |
||
| 527 | } |
||
| 528 | } |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths