Просмотр файла app/classes/App.php

Размер файла: 22.58Kb
  1. <?php
  2.  
  3. class App
  4. {
  5. /**
  6. * Возвращает текущую страницу
  7. * @param null $url
  8. * @return string текущая страница
  9. */
  10. public static function returnUrl($url = null)
  11. {
  12. if (Request::is('/', 'login', 'register', 'lostpassword', 'ban', 'closed')) {
  13. return false;
  14. }
  15. $query = Request::has('return') ? Request::input('return') : Request::path();
  16. return '?return='.urlencode(is_null($url) ? $query : $url);
  17. }
  18.  
  19. /**
  20. * Возвращает подключенный шаблон
  21. * @param $template
  22. * @param array $params массив параметров
  23. * @param boolean $return выводить или возвращать код
  24. * @return string сформированный код
  25. * @internal param string $view имя шаблона
  26. */
  27. public static function view($template, $params = [], $return = false)
  28. {
  29. $log = static::user('login');
  30. $config = self::setting();
  31.  
  32. $params +=compact('config', 'log');
  33.  
  34. $blade = new Jenssegers\Blade\Blade([APP.'/views', HOME.'/themes'], STORAGE.'/cache');
  35.  
  36. if ($return) {
  37. return $blade->render($template, $params);
  38. } else {
  39. echo $blade->render($template, $params);
  40. }
  41. }
  42.  
  43. /**
  44. * Сохраняет страницы с ошибками
  45. * @param integer $code код ошибки
  46. * @param string $message текст ошибки
  47. * @return string сформированная страница с ошибкой
  48. */
  49. public static function abort($code, $message = null)
  50. {
  51. if ($code == 403) {
  52. header($_SERVER['SERVER_PROTOCOL'].' 403 Forbidden');
  53. }
  54.  
  55. if ($code == 404) {
  56. header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
  57. }
  58.  
  59. if (App::setting('errorlog') && in_array($code, [403, 404])) {
  60.  
  61. DBM::run()->insert('error', [
  62. 'num' => $code,
  63. 'request' => utf_substr(App::server('REQUEST_URI'), 0, 200),
  64. 'referer' => utf_substr(App::server('HTTP_REFERER'), 0, 200),
  65. 'username' => App::getUsername(),
  66. 'ip' => App::getClientIp(),
  67. 'brow' => App::getUserAgent(),
  68. 'time' => SITETIME,
  69. ]);
  70.  
  71. DBM::run()->delete('error', [
  72. 'num' => $code,
  73. 'time' => ['<', SITETIME - 3600 * 24 * App::setting('maxlogdat')]]
  74. );
  75. }
  76.  
  77. exit(self::view('errors.'.$code, compact('message')));
  78. }
  79.  
  80.  
  81.  
  82. /**
  83. * Переадресовывает пользователя
  84. * @param string $url адрес переадресации
  85. * @param boolean $permanent постоянное перенаправление
  86. */
  87. public static function redirect($url, $permanent = false)
  88. {
  89. if (isset($_SESSION['captcha'])) $_SESSION['captcha'] = null;
  90.  
  91. if ($permanent){
  92. header($_SERVER['SERVER_PROTOCOL'].' 301 Moved Permanently');
  93. }
  94.  
  95. exit(header('Location: '.$url));
  96. }
  97.  
  98. /**
  99. * Сохраняет flash уведомления
  100. * @param string $status статус уведомления
  101. * @param mixed $message массив или текст с уведомлениями
  102. */
  103. public static function setFlash($status, $message)
  104. {
  105. $_SESSION['flash'][$status] = $message;
  106. }
  107.  
  108. /**
  109. * Возвращает flash уведомления
  110. * @return string сформированный блок с уведомлениями
  111. * @internal param array $errors массив уведомлений
  112. */
  113. public static function getFlash()
  114. {
  115. self::view('app/_flash');
  116. }
  117.  
  118. /**
  119. * Сохраняет POST данные введенных пользователем
  120. * @param array $data массив полей
  121. */
  122. public static function setInput(array $data)
  123. {
  124. $prepareData = [];
  125. foreach($data as $key => $value) {
  126.  
  127. if (is_object($value)) {
  128. continue;
  129. }
  130.  
  131. $prepareData[$key] = $value;
  132. }
  133.  
  134. $_SESSION['input'] = $prepareData;
  135. }
  136.  
  137. /**
  138. * Возвращает значение из POST данных
  139. * @param string $name имя поля
  140. * @param string $default
  141. * @return string сохраненный текст
  142. */
  143. public static function getInput($name, $default = '')
  144. {
  145. return isset($_SESSION['input'][$name]) ? $_SESSION['input'][$name] : $default;
  146. }
  147.  
  148. /**
  149. * Подсвечивает блок с полем для ввода сообщения
  150. * @param string $field имя поля
  151. * @return string CSS класс ошибки
  152. */
  153. public static function hasError($field)
  154. {
  155. return isset($_SESSION['flash']['danger'][$field]) ? ' has-error' : '';
  156. }
  157.  
  158. /**
  159. * Возвращает блок с текстом ошибки
  160. * @param string $field имя поля
  161. * @return string блоки ошибки
  162. */
  163. public static function textError($field)
  164. {
  165. if (isset($_SESSION['flash']['danger'][$field])) {
  166. $error = $_SESSION['flash']['danger'][$field];
  167. return '<div class="text-danger">'.$error.'</div>';
  168. }
  169. }
  170.  
  171. /**
  172. * Проверяет является ли email валидным
  173. * @param string $email адрес email
  174. * @return boolean результат проверки
  175. */
  176. public static function isMail($email)
  177. {
  178. return preg_match('#^([a-z0-9_\-\.])+\@([a-z0-9_\-\.])+(\.([a-z0-9])+)+$#', $email);
  179. }
  180.  
  181. /**
  182. * Отправка уведомления на email
  183. * @param mixed $to Получатель
  184. * @param string $subject Тема письма
  185. * @param string $body Текст сообщения
  186. * @param array $headers Дополнительные параметры
  187. * @return boolean Результат отправки
  188. */
  189. /* public static function sendMail($to, $subject, $body, $headers = [])
  190. {
  191. if (empty($headers['from'])) $headers['from'] = [env('SITE_EMAIL') => env('SITE_ADMIN')];
  192.  
  193. $message = Swift_Message::newInstance()
  194. ->setTo($to)
  195. ->setSubject($subject)
  196. ->setBody($body, 'text/html')
  197. ->setFrom($headers['from'])
  198. ->setReturnPath(env('SITE_EMAIL'));
  199.  
  200. if (env('MAIL_DRIVER') == 'smtp') {
  201. $transport = Swift_SmtpTransport::newInstance(env('MAIL_HOST'), env('MAIL_PORT'), 'ssl')
  202. ->setUsername(env('MAIL_USERNAME'))
  203. ->setPassword(env('MAIL_PASSWORD'));
  204. } else {
  205. $transport = new Swift_MailTransport();
  206. }
  207.  
  208. $mailer = new Swift_Mailer($transport);
  209. return $mailer->send($message);
  210. }*/
  211.  
  212. /**
  213. * Возвращает форматированную дату
  214. * @param string $format отформатированная дата
  215. * @param mixed $date временная метки или дата
  216. * @return string отформатированная дата
  217. */
  218. public static function date($format, $date = null)
  219. {
  220. $date = (is_null($date)) ? time() : strtotime($date);
  221.  
  222. $eng = ['January','February','March','April','May','June','July','August','September','October','November','December'];
  223. $rus = ['января','февраля','марта','апреля','мая','июня','июля','августа','сентября','октября','ноября','декабря'];
  224. return str_replace($eng, $rus, date($format, $date));
  225. }
  226.  
  227. /**
  228. * Возвращает расширение файла
  229. * @param string $filename имя файла
  230. * @return string расширение
  231. */
  232. public static function getExtension($filename)
  233. {
  234. return pathinfo($filename, PATHINFO_EXTENSION);
  235. }
  236.  
  237. /**
  238. * Возвращает размер файла
  239. * @param string $filename путь к файлу
  240. * @param integer $decimals кол. чисел после запятой
  241. * @return string форматированный вывод размера
  242. */
  243. public static function filesize($filename, $decimals = 1)
  244. {
  245. if (!file_exists($filename)) return 0;
  246.  
  247. $bytes = filesize($filename);
  248. $size = ['B','kB','MB','GB','TB'];
  249. $factor = floor((strlen($bytes) - 1) / 3);
  250. $unit = isset($size[$factor]) ? $size[$factor] : '';
  251. return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)).$unit;
  252. }
  253.  
  254. /**
  255. * Склоняет числа
  256. * @param integer $num число
  257. * @param array $forms массив склоняемых слов (один, два, много)
  258. * @return string форматированная строка
  259. */
  260. public static function plural($num, array $forms)
  261. {
  262. if ($num % 100 > 10 && $num % 100 < 15) return $num.' '.$forms[2];
  263. if ($num % 10 == 1) return $num.' '.$forms[0];
  264. if ($num % 10 > 1 && $num %10 < 5) return $num.' '.$forms[1];
  265. return $num.' '.$forms[2];
  266. }
  267.  
  268. /**
  269. * Валидирует даты
  270. * @param string $date дата
  271. * @param string $format формат даты
  272. * @return boolean результат валидации
  273. */
  274. public static function validateDate($date, $format = 'Y-m-d H:i:s')
  275. {
  276. $d = DateTime::createFromFormat($format, $date);
  277. return $d && $d->format($format) == $date;
  278. }
  279.  
  280. /**
  281. * Обрабатывает BB-код
  282. * @param string $text Необработанный текст
  283. * @param boolean $parse Обрабатывать или вырезать код
  284. * @return string Обработанный текст
  285. */
  286. public static function bbCode($text, $parse = true)
  287. {
  288. $bbcode = new BBCodeParser(self::setting());
  289.  
  290. if ( ! $parse) return $bbcode->clear($text);
  291.  
  292. $text = $bbcode->parse($text);
  293. $text = $bbcode->parseSmiles($text);
  294.  
  295. return $text;
  296. }
  297.  
  298. /**
  299. * Определяет браузер
  300. * @param string|null $userAgent
  301. * @return string браузер и версия браузера
  302. */
  303. public static function getUserAgent($userAgent = null)
  304. {
  305. $browser = new Browser();
  306. if ($userAgent) {
  307. $browser->setUserAgent($userAgent);
  308. }
  309.  
  310. $brow = $browser->getBrowser();
  311. $version = implode('.', array_slice(explode('.', $browser->getVersion()), 0, 2));
  312. return mb_substr($version == 'unknown' ? $brow : $brow.' '.$version, 0, 25, 'utf-8');
  313. }
  314.  
  315. /**
  316. * Определяет IP пользователя
  317. * @return string IP пользователя
  318. */
  319. public static function getClientIp()
  320. {
  321. $ip = Request::ip();
  322. return $ip == '::1' ? '127.0.0.1' : $ip;
  323. }
  324.  
  325. /**
  326. * Возвращает серверные переменные
  327. * @param string|null $key ключ массива
  328. * @param string|null $default значение по умолчанию
  329. * @return mixed данные
  330. */
  331. public static function server($key = null, $default = null)
  332. {
  333. $server = Request::server($key, $default);
  334. if ($key == 'REQUEST_URI') $server = urldecode($server);
  335. if ($key == 'PHP_SELF') $server = current(explode('?', static::server('REQUEST_URI')));
  336.  
  337. return check($server);
  338. }
  339.  
  340. public static function getUsername()
  341. {
  342. return isset($_SESSION['login']) ? check($_SESSION['login']) : self::setting('guestsuser');
  343. }
  344.  
  345. /**
  346. * Возвращает данные пользователя по ключу
  347. * @param string $key ключ массива
  348. * @return string данные
  349. */
  350. public static function user($key = null)
  351. {
  352. if (Registry::has('user')) {
  353. if (empty($key)) return Registry::get('user');
  354.  
  355. return isset(Registry::get('user')[$key]) ? Registry::get('user')[$key] : '';
  356. }
  357. }
  358.  
  359. /**
  360. * Возвращает настройки сайта по ключу
  361. * @param string $key ключ массива
  362. * @return string данные
  363. */
  364. public static function setting($key = null)
  365. {
  366. if (Registry::has('config')) {
  367. if (empty($key)) return Registry::get('config');
  368.  
  369. return isset(Registry::get('config')[$key]) ? Registry::get('config')[$key] : '';
  370. }
  371. }
  372.  
  373. /**
  374. * Авторизует пользователя
  375. * @param string $login Логин или никнэйм
  376. * @param string $password Пароль пользователя
  377. * @param boolean $remember Запомнить пароль
  378. * @return boolean Результат авторизации
  379. */
  380. public static function login($login, $password, $remember = true)
  381. {
  382. $domain = check_string(self::setting('home'));
  383.  
  384. if (!empty($login) && !empty($password)) {
  385.  
  386. $user = DB::run()->queryFetch("SELECT `login`, `password` FROM `users` WHERE LOWER(`login`)=? OR LOWER(`nickname`)=? LIMIT 1;", [$login, $login]);
  387.  
  388.  
  389. /* Миграция старых паролей */
  390. if (preg_match('/^[a-f0-9]{32}$/', $user['password']))
  391. {
  392. if (md5(md5($password)) == $user['password']) {
  393. $user['password'] = password_hash($password, PASSWORD_BCRYPT);
  394. DBM::run()->update('users', [
  395. 'password' => $user['password'],
  396. ], ['login' => $user['login']]);
  397. }
  398. }
  399.  
  400. if ($user && password_verify($password, $user['password'])) {
  401.  
  402. if ($remember) {
  403. setcookie('login', $user['login'], time() + 3600 * 24 * 365, '/', $domain);
  404. setcookie('password', md5($user['password'].env('APP_KEY')), time() + 3600 * 24 * 365, '/', $domain, null, true);
  405. }
  406.  
  407. $_SESSION['ip'] = self::getClientIp();
  408. $_SESSION['login'] = $user['login'];
  409. $_SESSION['password'] = md5(env('APP_KEY').$user['password']);
  410.  
  411. // Сохранение привязки к соц. сетям
  412. if (!empty($_SESSION['social'])) {
  413. DBM::run()->insert('socials', [
  414. 'user' => $user['login'],
  415. 'network' => $_SESSION['social']->network,
  416. 'uid' => $_SESSION['social']->uid,
  417. ]);
  418. }
  419.  
  420. DB::run()->query("UPDATE `users` SET `visits`=`visits`+1, `timelastlogin`=? WHERE `login`=?", [SITETIME, $user['login']]);
  421.  
  422. $authorization = DB::run()->querySingle("SELECT `id` FROM `login` WHERE `user`=? AND `time`>? LIMIT 1;", [$user['login'], SITETIME - 30]);
  423.  
  424. if (empty($authorization)) {
  425. DB::run()->query("INSERT INTO `login` (`user`, `ip`, `brow`, `time`, `type`) VALUES (?, ?, ?, ?, ?);", [$user['login'], self::getClientIp(), self::getUserAgent(), SITETIME, 1]);
  426. DB::run()->query("DELETE FROM `login` WHERE `user`=? AND `time` < (SELECT MIN(`time`) FROM (SELECT `time` FROM `login` WHERE `user`=? ORDER BY `time` DESC LIMIT 50) AS del);", [$user['login'], $user['login']]);
  427. }
  428.  
  429. return $user;
  430. }
  431. }
  432.  
  433. return false;
  434. }
  435.  
  436. /**
  437. * Авторизует через социальные сети
  438. * @param string $token идентификатор Ulogin
  439. */
  440. public static function socialLogin($token)
  441. {
  442. $domain = check_string(self::setting('home'));
  443.  
  444. $curl = new Curl\Curl();
  445. $network = $curl->get('http://ulogin.ru/token.php', [
  446. 'token' => $token,
  447. 'host' => $_SERVER['HTTP_HOST']
  448. ]);
  449.  
  450. if ($network && empty($network->error)) {
  451. $_SESSION['social'] = $network;
  452.  
  453. $social = DBM::run()->selectFirst('socials', ['network' => $network->network, 'uid' => $network->uid]);
  454.  
  455. if ($social && $user = user($social['user'])) {
  456.  
  457. setcookie('login', $user['login'], time() + 3600 * 24 * 365, '/', $domain);
  458. setcookie('password', md5($user['password'].env('APP_KEY')), time() + 3600 * 24 * 365, '/', $domain, null, true);
  459.  
  460. $_SESSION['login'] = $user['login'];
  461. $_SESSION['password'] = md5(env('APP_KEY').$user['password']);
  462. $_SESSION['ip'] = App::getClientIp();
  463.  
  464. self::setFlash('success', 'Добро пожаловать, '.$user['login'].'!');
  465. self::redirect('/');
  466. }
  467. }
  468. }
  469.  
  470. /**
  471. * Генерирует постраничную навигация
  472. * @param array $page массив данных
  473. * @return string сформированный блок
  474. */
  475. public static function pagination($page)
  476. {
  477. if ($page['total'] > 0) {
  478.  
  479. if (empty($page['crumbs'])) $page['crumbs'] = 3;
  480.  
  481. $url = array_except($_GET, 'page');
  482. $request = $url ? '&'.http_build_query($url) : null;
  483.  
  484. $pages = [];
  485. $pg_cnt = ceil($page['total'] / $page['limit']);
  486. $idx_fst = max($page['current'] - $page['crumbs'], 1);
  487. $idx_lst = min($page['current'] + $page['crumbs'], $pg_cnt);
  488.  
  489. if ($page['current'] != 1) {
  490. $pages[] = [
  491. 'page' => $page['current'] - 1,
  492. 'title' => 'Предыдущая',
  493. 'name' => '«',
  494. ];
  495. }
  496.  
  497. if ($page['current'] > $page['crumbs'] + 1) {
  498. $pages[] = [
  499. 'page' => 1,
  500. 'title' => '1 страница',
  501. 'name' => 1,
  502. ];
  503. if ($page['current'] != $page['crumbs'] + 2) {
  504. $pages[] = [
  505. 'separator' => true,
  506. 'name' => ' ... ',
  507. ];
  508. }
  509. }
  510.  
  511. for ($i = $idx_fst; $i <= $idx_lst; $i++) {
  512. if ($i == $page['current']) {
  513. $pages[] = [
  514. 'current' => true,
  515. 'name' => $i,
  516. ];
  517. } else {
  518. $pages[] = [
  519. 'page' => $i,
  520. 'title' => $i.' страница',
  521. 'name' => $i,
  522. ];
  523. }
  524. }
  525.  
  526. if ($page['current'] < $pg_cnt - $page['crumbs']) {
  527. if ($page['current'] != $pg_cnt - $page['crumbs'] - 1) {
  528. $pages[] = [
  529. 'separator' => true,
  530. 'name' => ' ... ',
  531. ];
  532. }
  533. $pages[] = [
  534. 'page' => $pg_cnt,
  535. 'title' => $pg_cnt . ' страница',
  536. 'name' => $pg_cnt,
  537. ];
  538. }
  539.  
  540. if ($page['current'] != $pg_cnt) {
  541. $pages[] = [
  542. 'page' => $page['current'] + 1,
  543. 'title' => 'Следующая',
  544. 'name' => '»',
  545. ];
  546. }
  547.  
  548. self::view('app._pagination', compact('pages', 'request'));
  549. }
  550. }
  551.  
  552. /**
  553. * Генерирует постраничную навигация для форума
  554. * @param array $topic массив данных
  555. * @return string сформированный блок
  556. */
  557. public static function forumPagination($topic) {
  558.  
  559. if ($topic['posts']) {
  560.  
  561. $pages = [];
  562. $link = '/topic/'.$topic['id'];
  563.  
  564. $pg_cnt = ceil($topic['posts'] / App::setting('forumpost'));
  565.  
  566. for ($i = 1; $i <= 5; $i++) {
  567. if ($i <= $pg_cnt) {
  568. $pages[] = [
  569. 'page' => $i,
  570. 'title' => $i.' страница',
  571. 'name' => $i,
  572. ];
  573. }
  574. }
  575.  
  576. if (5 < $pg_cnt) {
  577.  
  578. if (6 < $pg_cnt) {
  579. $pages[] = array(
  580. 'separator' => true,
  581. 'name' => ' ... ',
  582. );
  583. }
  584.  
  585. $pages[] = array(
  586. 'page' => $pg_cnt,
  587. 'title' => $pg_cnt.' страница',
  588. 'name' => $pg_cnt,
  589. );
  590. }
  591.  
  592. self::view('forum._pagination', compact('pages', 'link'));
  593. }
  594. }
  595.  
  596. /**
  597. * Обрабатывает постраничную навигацию
  598. * @param integer $limit элементов на страницу
  599. * @param integer $total всего элементов
  600. * @return array массив подготовленных данных
  601. */
  602. public static function paginate($limit, $total)
  603. {
  604. $current = Request::input('page');
  605. if ($current < 1) $current = 1;
  606.  
  607. if ($total && $current * $limit >= $total) {
  608. $current = ceil($total / $limit);
  609. }
  610.  
  611. $offset = intval(($current * $limit) - $limit);
  612.  
  613. return compact('current', 'offset', 'limit', 'total');
  614. }
  615.  
  616. /**
  617. * Устанавливает права доступа на папки
  618. * @return void
  619. */
  620. public static function install()
  621. {
  622. $storage = glob(dirname(__DIR__).'/storage/*', GLOB_ONLYDIR);
  623. $uploads = glob(dirname(dirname(__DIR__)).'/public/uploads/*', GLOB_ONLYDIR);
  624.  
  625. $dirs = array_merge($storage, $uploads);
  626.  
  627. foreach ($dirs as $dir) {
  628. $old = umask(0);
  629. chmod ($dir, 0777);
  630. umask($old);
  631. }
  632. }
  633.  
  634. /**
  635. * Возвращает сформированный код base64 картинки
  636. * @param string $path путь к картинке
  637. * @param array $params параметры
  638. * @return string сформированный код
  639. */
  640. public static function imageBase64($path, array $params = [])
  641. {
  642. $type = pathinfo($path, PATHINFO_EXTENSION);
  643. $data = file_get_contents($path);
  644.  
  645. if (! isset($params['class'])) {
  646. $params['class'] = 'img-responsive';
  647. }
  648.  
  649. $strParams = [];
  650. foreach ($params as $key => $param) {
  651. $strParams[] = $key.'="'.$param.'"';
  652. }
  653.  
  654. $strParams = implode(' ', $strParams);
  655.  
  656. return '<img src="data:image/'.$type.';base64,'.base64_encode($data).'"'.$strParams.'>';
  657. }
  658. }