'OTHER_ERROR', \E_ERROR => 'E_ERROR', \E_WARNING => 'E_WARNING', \E_PARSE => 'E_PARSE', \E_NOTICE => 'E_NOTICE', \E_CORE_ERROR => 'E_CORE_ERROR', \E_CORE_WARNING => 'E_CORE_WARNING', \E_COMPILE_ERROR => 'E_COMPILE_ERROR', \E_COMPILE_WARNING => 'E_COMPILE_WARNING', \E_USER_ERROR => 'E_USER_ERROR', \E_USER_WARNING => 'E_USER_WARNING', \E_USER_NOTICE => 'E_USER_NOTICE', \E_STRICT => 'E_STRICT', \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', \E_DEPRECATED => 'E_DEPRECATED', \E_USER_DEPRECATED => 'E_USER_DEPRECATED', ]; public function __construct() { $this->hidePath = \realpath(__DIR__ . '/../../'); \set_error_handler([$this, 'errorHandler']); \set_exception_handler([$this, 'exceptionHandler']); \register_shutdown_function([$this, 'shutdownHandler']); \ob_start(); $this->obLevel = \ob_get_level(); } public function __destruct() { \restore_error_handler(); \restore_exception_handler(); //???? } /** * Обрабатыет перехватываемые ошибки */ public function errorHandler(int $type, string $message, string $file, string $line): bool { $error = [ 'type' => $type, 'message' => $message, 'file' => $file, 'line' => $line, 'trace' => \debug_backtrace(0), ]; $this->log($error); if ($type & \error_reporting()) { $this->error = $error; exit(1); } $this->logged = false; return true; } /** * Обрабатывает не перехваченные исключения */ public function exceptionHandler(Throwable $e): void { $this->error = [ 'type' => 0, //???? 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => $e->getTrace(), ]; } /** * Окончательно обрабатывает ошибки (в том числе фатальные) и исключения */ public function shutdownHandler(): void { if (isset($this->error['type'])) { $show = true; } else { $show = false; $this->error = \error_get_last(); if (isset($this->error['type'])) { switch ($this->error['type']) { case \E_ERROR: case \E_PARSE: case \E_CORE_ERROR: case \E_CORE_WARNING: case \E_COMPILE_ERROR: case \E_COMPILE_WARNING: $show = true; break; } } } if ( isset($this->error['type']) && ! $this->logged ) { $this->log($this->error); } while (\ob_get_level() > $this->obLevel) { \ob_end_clean(); } if (\ob_get_level() === $this->obLevel) { if ($show) { \ob_end_clean(); $this->show($this->error); } else { \ob_end_flush(); } } } /** * Отправляет сообщение в лог */ protected function log(array $error): void { $this->logged = true; $message = \preg_replace('%[\x00-\x1F]%', ' ', $this->message($error)); \error_log($message); } /** * Выводит сообщение об ошибке * * @param array $error */ protected function show(array $error): void { \header('HTTP/1.1 500 Internal Server Error'); echo <<<'EOT' 500 Internal Server Error EOT; if (1 == \ini_get('display_errors')) { echo '

' . $this->e($this->message($error)) . '

'; if ( isset($error['trace']) && \is_array($error['trace']) ) { echo '

Trace:

    '; foreach ($error['trace'] as $cur) { if ( isset($cur['file'], $cur['line'], $error['file'], $error['line']) && $error['line'] === $cur['line'] && $error['file'] === $cur['file'] ) { continue; } $line = $cur['file'] ?? '-'; $line .= '(' . ($cur['line'] ?? '-') . '): '; if (isset($cur['class'])) { $line .= $cur['class'] . $cur['type']; } $line .= ($cur['function'] ?? 'unknown') . '('; if ( ! empty($cur['args']) && \is_array($cur['args']) ) { $comma = ''; foreach($cur['args'] as $arg) { $type = \gettype($arg); switch ($type) { case 'boolean': $type = $arg ? 'true' : 'false'; break; case 'array': $type .= '(' . \count($arg) . ')'; break; case 'resource': $type = \get_resource_type($arg); break; case 'object': $type .= '{' . \get_class($arg) . '}'; break; } $line .= $comma . $type; $comma = ', '; } } $line .= ')'; $line = $this->e(\str_replace($this->hidePath, '...', $line)); echo "
  1. {$line}
  2. "; } echo '
'; } } else { echo '

Oops

'; } echo <<<'EOT' EOT; } /** * Формирует сообщение */ protected function message(array $error): string { $type = $this->type[$error['type']] ?? $this->type[0]; $file = \str_replace($this->hidePath, '...', $error['file']); return "PHP {$type}: \"{$error['message']}\" in {$file}:[{$error['line']}]"; } /** * Экранирует спецсимволов HTML-сущностями */ protected function e(string $arg): string { return \htmlspecialchars($arg, \ENT_HTML5 | \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8'); } }