Example #1
0
 /**
  * Соединяемся с базой или получаем дескриптор подключения.
  *
  * Запоминаем подключения по $confKey. Полагаем, что в настройках приложения для каждого ключа описано <i>уникальное
  * сочетание</i> базы/пользователя, поскольку идентичный конфиг не имеет смысла.
  *
  * Опционально ('mysql_timezone'): после установки соединения отправляется запрос на установку часового пояса сессии.
  * Подробнее см. в доке "DB.md"
  *
  * Единственный источник исключения PDOException тут - это сам конструктор PDO. В трейсе он будет занимать первую
  * позицию, потом будет модель, вызвавшая этот метод и только потом возможно одна/две позиции по клиентскому коду.
  * Поэтому такая глубина разбора трассировки.
  *
  * @param string $confKey ключ в настройках, по которому хранится массив с кофигурацией подключения к БД
  * @return PDO объект подключения к БД
  * @throws DbException
  */
 public static function connect($confKey)
 {
     if (isset(self::$cons[$confKey])) {
         return self::$cons[$confKey];
     }
     $conf = array_merge(['options' => null, 'mysql_timezone' => '+00:00'], App::conf($confKey));
     try {
         $dbh = new PDO($conf['dsn'], $conf['user'], $conf['password'], $conf['options']);
         if ($tz = $conf['mysql_timezone']) {
             $sql = 'SET time_zone = ?';
             if (false === $dbh->prepare($sql)->execute([$tz])) {
                 throw new DbException('Ошибка установки часового пояса MySQL-сессии.' . PHP_EOL . 'Запрос: ' . str_replace('?', "'{$tz}'", $sql));
             }
         }
         self::$cons[$confKey] = $dbh;
     } catch (\PDOException $e) {
         $msg = $e->getMessage();
         $trace = $e->getTrace();
         if (isset($trace[2])) {
             $msg .= PHP_EOL . 'Инициатор подключения ' . str_replace(ROOT_PATH, '', $trace[2]['file']) . '(' . $trace[2]['line'] . ')';
         }
         if (isset($trace[3])) {
             $msg .= ' функция ' . $trace[3]['function'] . '(...)';
         }
         throw new DbException($msg, DbException::CONNECT, $e);
     }
     return self::$cons[$confKey];
 }
Example #2
0
 /**
  * Письмо админу с текущим сообщение лога.
  *
  * @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);
     }
 }
Example #3
0
 /**
  * Построение URL по описанию, используя карту роутов.
  *
  * Перебираем роуты в заданном пространстве имен. Сравниваем указанные контроллер/действие с правой частью роута.
  * Если нашли совпадение справа (будет массив), анализируем правило слева, выбирая необходимые подстановки -
  * "required params". Если сможем их обеспечить из массива совпадения и/или переданных параметров - роут найден.
  * Иначе продолжаем поиск.
  *
  * Все параметры, которые не пойдут в подстановки, допишутся в URL в качестве строки запроса. Если у элемента
  * не задано значение, а есть только ключ, типа ['p' => null], он попадет в URL без значения.
  *
  * Прим: поиск роута не зависит от регистра. Это немного упрощает требования к описанию параметров функции.
  *
  * Если ничего не найдем, проброс ошибки. Вообще ситуация некритичная, но иначе можно прозевать исчезновение роута
  * и получить битую ссылку.
  *
  * @param mixed $route  <b>неассоциативный</b> массив 2-х элементов ["пространство имен", "контроллер/действие"]
  * @param array $params доп.параметры для передачи в адрес. Ассоциативный массив ['имя параметра' => 'значение']
  * @return string готовый <b>относительный</b> URL
  * @throws RouteException
  */
 public function url($route, array $params = [])
 {
     list($ns, $ctrlAct) = $route;
     $ctrlAct = trim($ctrlAct, '/');
     $routes = App::conf("router.routes.{$ns}", false);
     if (!$routes) {
         throw new RouteException("Нет карты роутов для пространства имен '{$ns}'");
     }
     $match = null;
     $paramsBackup = $params;
     foreach ($routes as $left => $right) {
         $left = ltrim($left, '/');
         $right = ltrim($right, '/');
         $pattern = preg_replace('~(<(.+)>)~U', '(?P<$2>[^/]+)', $right);
         $pattern = "~^{$pattern}\$~Ui";
         if (preg_match($pattern, $ctrlAct, $matchCtrlAct)) {
             if (preg_match_all('~<([a-z0-9_]+):.+>~U', $left, $requiredParams)) {
                 $requiredParams = array_flip($requiredParams[1]);
                 foreach ($requiredParams as $k => &$v) {
                     if (isset($matchCtrlAct[$k])) {
                         $v = $matchCtrlAct[$k];
                     } else {
                         if (isset($params[$k])) {
                             $v = $params[$k];
                             // удаляем из параметров то, что будет использовано в подстановке
                             unset($params[$k]);
                         } else {
                             $params = $paramsBackup;
                             continue 2;
                         }
                     }
                 }
                 unset($v);
             } else {
                 $requiredParams = [];
             }
             break;
         }
     }
     //dd($left, $match);//DBG
     if ($matchCtrlAct) {
         if ($requiredParams) {
             $placeholders = [];
             foreach ($requiredParams as $key => $v) {
                 $placeholders[] = "~(<{$key}:.+>)~U";
             }
             $left = preg_replace($placeholders, $requiredParams, $left);
         }
         if (preg_match('~[^/a-z0-9_-]~i', $left)) {
             $arr = explode('/', $left);
             $arr = array_map('urlencode', $arr);
             $left = implode('/', $arr);
         }
         $url = '/' . $left;
         if ($params) {
             foreach ($params as $k => &$v) {
                 $v = urlencode($k) . ($v ? '=' . urlencode($v) : '');
             }
             $url .= '?' . implode('&', $params);
         }
         return $url;
     } else {
         $strParams = [];
         foreach ($params as $k => $v) {
             $strParams[] = "{$k} => {$v}";
         }
         $strParams = count($strParams) ? 'параметры [' . implode(', ', $strParams) . ']' : 'без параметров';
         throw new RouteException("не могу построить URL по заданным значениям: ['{$ns}', '{$ctrlAct}'], {$strParams}");
     }
 }
Example #4
0
 /**
  * Имя домена, типа "my.site.com".
  *
  * Пытаемся его получить из $_SERVER['HTTP_HOST']. Иначе ищем в настройках приложения ('domain').
  *
  * @return string
  */
 public static function domainName()
 {
     return isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (App::conf('domain', false) ?: '');
 }
Example #5
0
<?php

/**
 * Главный скрипт приложения 'app'
 */
use kira\core\App;
mb_internal_encoding('UTF-8');
define('APP_NAMESPACE', 'app');
define('ROOT_PATH', str_replace('\\', '/', rtrim(__DIR__, '/')) . '/');
define('APP_PATH', ROOT_PATH . 'application/');
define('VIEWS_PATH', APP_PATH . 'views/');
define('TEMP_PATH', APP_PATH . 'temp/');
define('MAIN_CONFIG', APP_PATH . 'conf/main.php');
define('DEBUG', true);
ini_set('display_errors', (int) DEBUG);
ini_set('display_startup_errors', (int) DEBUG);
error_reporting(DEBUG ? E_ALL : 0);
$composer = (require ROOT_PATH . 'vendor/autoload.php');
App::setComposer($composer);
unset($composer);
date_default_timezone_set(App::conf('timezone'));
App::router()->callAction();
Example #6
0
 /**
  * Проверка права запускать скрипты через Консоль
  *
  * В режиме отладки - разрешено без вопросов, иначе - при указании правильного ключа. Количество ошибок считается.
  * Ключ и допустимое количество прописываем в конфиге приложения:
  * <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);
 }