'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'
' . $this->e($this->message($error)) . '
'; if ( isset($error['trace']) && \is_array($error['trace']) ) { echo 'Trace:
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'); } }