| @copyright 2007-2026 (c) AVE.cms | @link https://ave-cms.ru | @version 3.3 */ namespace App\Common; defined("BASEPATH") || die ('Direct access to this location is not allowed.'); use App\Helpers\Debug; use App\Helpers\File; class Errors { /** @var bool Защита от рекурсии (ошибка внутри обработчика) */ protected static $handling = false; public function __construct () { if (PHP_DEBUGGING_FILE) { ini_set('display_errors', 'Off'); set_error_handler([$this, 'scriptError'], E_ALL); register_shutdown_function([$this, 'shutDown']); } } /** * Обработчик не-фатальных ошибок. * Уважает @-подавление и error_reporting; всегда пишет в лог; * «красивый» блок рендерит только в dev и не в AJAX/JSON. */ public function scriptError ($errno, $errstr, $errfile, $errline) { // Уважать @ и текущий уровень error_reporting if (! (error_reporting() & $errno)) { return false; } $severity = self::severityName($errno); $this->logError($severity, $errstr, $errfile, $errline); // HTML-блок — только в режиме отладки и не в AJAX/JSON // (иначе разметка попадёт в JSON-ответ и сломает его) if (defined('PHP_DEBUGGING') && PHP_DEBUGGING && ! self::isAjax()) { echo $this->renderBox($severity, $errno, $errstr, $errfile, $errline); } return true; } /** * Обработчик фатальных ошибок при завершении скрипта. */ public function shutDown () { if (! $error = error_get_last()) { return; } $fatalTypes = [ E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, E_USER_ERROR, E_RECOVERABLE_ERROR, ]; if (! in_array($error['type'], $fatalTypes, true)) { return; } $severity = self::severityName($error['type']); $this->logError($severity, $error['message'], $error['file'], $error['line']); if (PHP_SAPI === 'cli') { fwrite(STDERR, 'Fatal: ' . $error['message'] . ' (' . $error['file'] . ':' . $error['line'] . ")\n"); return; } if (! headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } if (self::isAjax()) { if (! headers_sent()) { header('Content-Type: application/json; charset=UTF-8'); } echo json_encode(['success' => false, 'error' => 'Внутренняя ошибка сервера']); return; } if (defined('PHP_DEBUGGING') && PHP_DEBUGGING) { echo $this->renderBox($severity, $error['type'], $error['message'], $error['file'], $error['line']); return; } echo '' . '
' . 'Внутренняя ошибка. Попробуйте позже.
'; } /** * Записать ошибку в лог-файл (с защитой от рекурсии). */ protected function logError ($severity, $message, $file, $line) { if (self::$handling) { return; } self::$handling = true; $context = ['message' => $message, 'file' => $file, 'line' => $line]; if (class_exists('Logger')) { try { (new \Logger())->error('PHP ' . $severity, $context); } catch (\Throwable $e) { error_log('PHP ' . $severity . ': ' . $message . ' in ' . $file . ':' . $line); } } else { error_log('PHP ' . $severity . ': ' . $message . ' in ' . $file . ':' . $line); } self::$handling = false; } /** * AJAX/JSON-запрос? (нельзя печатать HTML в такой ответ) */ protected static function isAjax () { if (PHP_SAPI === 'cli') { return false; } if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') { return true; } return isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false; } /** * Имя и цвет уровня ошибки. */ protected static function severityName ($errno) { $map = [ E_ERROR => 'Error', E_WARNING => 'Warning', E_NOTICE => 'Notice', E_CORE_ERROR => 'Core Error', E_CORE_WARNING => 'Core Warning', E_COMPILE_ERROR => 'Compile Error', E_COMPILE_WARNING => 'Compile Warning', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_STRICT => 'Strict Standards', E_PARSE => 'Parse Error', E_RECOVERABLE_ERROR => 'Recoverable Error', E_DEPRECATED => 'Deprecated', E_USER_DEPRECATED => 'User Deprecated', ]; return isset($map[$errno]) ? $map[$errno] : 'Error'; } /** * Отрисовать HTML-блок ошибки (только для dev). */ protected function renderBox ($severity, $errno, $errstr, $errfile, $errline) { $red = ['Error', 'Core Error', 'Compile Error', 'User Error', 'Parse Error', 'Recoverable Error']; $blue = ['Notice', 'User Notice']; $color = in_array($severity, $red, true) ? '#f05050' : (in_array($severity, $blue, true) ? '#23b7e5' : '#fad733'); $out = '
'; $out .= '
'; $out .= '' . $severity . ' Line ' . $errline . ': ' . File::cutRootPath($errfile); $out .= '
'; $out .= '
'; $out .= '
'; $out .= Debug::fileLines($errfile, $errline); $out .= '
'; $out .= '
';
			$out .= '[' . $errno . '] ' . File::cutRootPathAll($errstr);
			$out .= '
'; $out .= '
'; $out .= '
'; return $out; } }