/** * Письмо админу с текущим сообщение лога. * * @return void */ private function mailToAdmin() { $logIt = $this->logIt; if (!($mailTo = $this->conf['_mail'])) { $this->addTyped('Не задан email админа, не могу отправить сообщение от логера.', ILogger::ENGINE); return; } $domain = Env::domainName(); $date = $logIt['ts']->format('Y/m/d H:i:s P'); $rn = PHP_EOL; $letters['text'] = 'Сообщение от логера' . $rn . $rn . $logIt['message'] . $rn . $rn . "Тип: {$logIt['type']}{$rn}" . "Источник: {$logIt['source']}" . $rn . $rn . "URL запроса:{$logIt['request']}" . $rn . "IP юзера: {$logIt['userIP']}" . $rn . $rn . "{$date} (c) {$domain}"; $vars = ['message' => nl2br($logIt['message']), 'type' => $logIt['type'], 'source' => $logIt['source'], 'request' => $logIt['request'], 'userIP' => $logIt['userIP'], 'date' => $date, 'homeURL' => Env::indexPage(), 'domain' => $domain]; $letters['html'] = Render::fetch('log_letter.htm', $vars); $from = App::conf('noreply_mail') ?: "noreply@{$domain}"; if (!Mailer::complex($from, $mailTo, "Сообщение от логера сайта {$domain}", $letters)) { $this->addTyped('Не удалось отправить сообщение от логера.', ILogger::ENGINE); } }
/** * Перехват ошибок PHP * * Свое оформление, трассировка и логирование. Если текущий тип ошибки отключен в error_reporting, то пишем * ее в temp-каталог приложения, в kira_php_error.log. Иначе - выдаем в браузер. * * Согласно мануала, такая функция вызывается независимо от настройки error_reporting. Поэтому проверяем, требуется * ли сообщать о полученной ошибке. Если да - рисуем ответ, иначе скидываем сообщение в лог. Обычно бывает так: * на локалке/деве всё включено - будут валиться сообщения в браузер. На проде все отключено - логируем. * * Опять же по мануалу, если этот обработчик не прервет выполнение вызвав die(), то программа продолжится. Если * вернет FALSE - ошибку получит стандартный обработчик и мы увидим еще и его сообщение. Поэтому делаем так: * если код соответствует какой-то ERROR - выходим; иначе возвращаем TRUE, чтоб стандартный обработчик * не дублировал сообщение в своем стиле. * * В случае фатальных ошибок трассировка вызовов уже неважна. К тому же я не могу ее получить почему-то. Поэтому * в таких сообщениях не будет стека вызовов. * * По константам кодов см. {@see http://php.net/manual/ru/errorfunc.constants.php} * Ликбез: {@link https://habrahabr.ru/post/134499/} * * @param int $code уровень ошибки в виде целого числа * @param string $msg сообщение об ошибке в виде строки * @param string $file имя файла, в котором произошла ошибка * @param int $line номер строки, в которой произошла ошибка * @return bool */ public static function errorHandler($code, $msg, $file, $line) { $codes = [1 => 'FATAL ERROR', 2 => 'WARNING', 4 => 'PARSE ERROR', 8 => 'NOTICE', 16 => 'CORE ERROR', 32 => 'CORE WARNING', 64 => 'COMPILE ERROR', 128 => 'COMPILE WARNING', 256 => 'USER ERROR', 512 => 'USER WARNING', 1024 => 'USER NOTICE', 2048 => 'STRICT', 4096 => 'RECOVERABLE ERROR', 8192 => 'DEPRECATED', 16384 => 'USER DEPRECATED']; $codeTxt = $codes[$code]; $file = str_replace(ROOT_PATH, '', $file); $stack_html = $stack_console = ''; if (($code & (E_ERROR | E_PARSE | E_COMPILE_ERROR)) == 0) { $trace = array_reverse(debug_backtrace()); array_pop($trace); foreach ($trace as $step) { $where = isset($step['file']) ? str_replace(ROOT_PATH, '', $step['file']) . ':' . $step['line'] : ''; $func = isset($step['class']) ? $step['class'] . $step['type'] . $step['function'] : $step['function']; if (isset($step['args'])) { $args = $step['args']; array_walk($args, function (&$i) { if (is_string($i)) { $i = "'{$i}'"; } elseif (is_array($i)) { $i = '[array]'; } elseif (is_bool($i)) { $i = $i ? 'true' : 'false'; } elseif (is_object($i)) { $i = get_class($i); } }); $args = implode(', ', $args); } else { $args = ''; } $stack_html .= "<tr><td class='php-err-txtright'>{$where}</td><td>{$func}({$args})</td></tr>" . PHP_EOL; $stack_console .= "{$where} > {$func}({$args})" . PHP_EOL; } $stack_html = "\n <p>Стек вызова в хронологическом порядке:</p>\n <table class = 'php-err-stack'>\n {$stack_html}\n </table>\n "; } $rn = PHP_EOL; if (error_reporting() & $code) { if (isConsoleInterface()) { echo "{$rn}{$codeTxt}{$rn}{$rn}\t{$msg}{$rn}{$rn}" . "Стек вызовов:{$rn}{$rn}" . $stack_console . "{$rn}"; } else { if (!headers_sent()) { header('500 Internal Server Error'); header('Content-Type: text/html; charset=UTF-8'); } $msg = nl2br(htmlspecialchars($msg, ENT_QUOTES, 'UTF-8')); echo Render::fetch('error_handler.htm', compact('codeTxt', 'msg', 'file', 'line', 'stack_html')); } } else { $info = "{$codeTxt}{$rn}{$rn}\t{$msg}{$rn}{$rn}" . 'в ' . date('Y.m.d. H:i:s') . "{$rn}{$rn}" . "Стек вызовов:{$rn}{$rn}" . $stack_console . "{$rn}---{$rn}{$rn}"; file_put_contents(TEMP_PATH . 'kira_php_error.log', $info, FILE_APPEND); return false; } if ($code & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR)) { exit; } else { return true; } }