/** * Перехватчик исключений. * * Ловит исключения, которые не были пойманы ранее. Последний шанс обработать ошибку. Например, записать в лог или * намылить админу. Можно так же вежливо откланяться юзеру. * * После выполнения этого обработчика программа остановится, обеспечено PHP. * * Если указано предыдущее исключение, отсюда не пишем в лог. Сей факт указывает на то, что реальное исключение * уже было поймано и обработано. Считаем, что необходимость логирования была решена в предыдущих обработчиках. * * Прим: для поддержки PHP 7.0 тип ожидаемого параметра расширен, * см. {@see http://php.net/manual/ru/function.set-exception-handler.php PHP::set_exception_handler()} * * @param \Throwable $ex */ public static function exceptionHandler($ex) { $class = get_class($ex); $message = nl2br($ex->getMessage()); $file = str_replace(ROOT_PATH, '/', $ex->getFile()); $line = $ex->getLine(); $trace = $ex->getTraceAsString(); if (isConsoleInterface()) { echo 'Исключение: ' . $class . PHP_EOL . PHP_EOL . $message . PHP_EOL . PHP_EOL . 'Стек вызовов:' . PHP_EOL . $trace . PHP_EOL; return; } if (!headers_sent()) { header('500 Internal Server Error'); header('Content-Type: text/html; charset=UTF-8'); } if (DEBUG) { echo Render::fetch('exception.htm', compact('class', 'message', 'file', 'line', 'trace')); } else { echo Render::fetch('exception_prod.htm', ['domain' => Env::domainName()]); if ($ex->getPrevious() === null) { $logger = App::logger(); $logger->addTyped("Class: {$class}" . PHP_EOL . "Message: {$message}" . PHP_EOL . "Source: {$file}:{$line}" . PHP_EOL . PHP_EOL . "Trace: {$trace}", $logger::EXCEPTION); } } }
/** * Конструктор * * Логируем ошибку. В зависимости от кода пишем лог в БД или файлы: если исключение проброшено из попытки соединения, * сразу же пишем лог в файлы, не пытаясь еще раз ткнуться в базу. * * @param string $message * @param int $code * @param \Exception $previous */ public function __construct($message, $code = self::QUERY, $previous = null) { $logger = App::logger(); if ($code === self::CONNECT) { $logger->add(['message' => $message, 'type' => $logger::DB_CONNECT, 'file_force' => true]); } else { $logger->addTyped($message, $logger::DB_QUERY); } parent::__construct($message, $code, $previous); }
/** * Редирект. Только для нужд роутера. * Если задана настройка "router.log_redirects", логируем такие редиректы. * @param string $url новый относительный адрес. Всегда без слеша слева, таков тут мой код. * @return void */ private function redirect($url) { if ($_SERVER['QUERY_STRING']) { $url .= '?' . $_SERVER['QUERY_STRING']; } if (App::conf('router.log_redirects', false)) { App::logger()->addTyped('pедирект на /' . $url, 'router redirect'); } Response::redirect('/' . $url, 301); }
/** * Проверка права запускать скрипты через Консоль * * В режиме отладки - разрешено без вопросов, иначе - при указании правильного ключа. Количество ошибок считается. * Ключ и допустимое количество прописываем в конфиге приложения: * <pre> * 'console => [ * 'ties' => int|0 * 'key' => string|'' * ] * </pre> * * <b>Внимание!</b> При отсутствии этого конфига, а так же со значениями по умолчанию, доступ будет разрешен. * * В дальнейшем можно добавить контроль на запрещенные/разрешенные IP и т.п. * * Неудавшиеся попытки логируются, после NN попыток консоль вообще блокируется через файл [TEMP_PATH/console.lock], * при блокировке админу пойдет письмо. * * @param string $key ключ доступа, спарсенный из запроса * @return null если нет права запуска, прямо тут все и закончится. Поэтому возвращаемое значение не важно. */ private function checkAccess(string $key) { if (DEBUG) { return; } $lockFile = TEMP_PATH . 'convisor.lock'; $count = file_exists($lockFile) ? file_get_contents($lockFile) : 0; $tries = (int) App::conf('convisor.tries', false); $critical = $tries && $count >= $tries; if ($critical) { exit('Консоль заблокирована' . PHP_EOL); } $allow = (string) App::conf('convisor.key', false) == $key; if (!$allow) { $count++; $msg = "{$count} неудачная попытка запуска скрипта в консоли: {$this->script} " . implode(' ', $this->params); if ($critical) { App::logger()->add(['message' => $msg, 'type' => 'deny console', 'notify' => true]); } else { App::logger()->addTyped($msg, 'deny console'); } file_put_contents($lockFile, $count); exit('Запуск запрещен' . PHP_EOL); } FS::deleteFile($lockFile); }