. * * @copyright (c) Visman * @license The MIT License (MIT) */ declare(strict_types=1); namespace ForkBB; use ForkBB\Core\Container; use DateTime; use DateTimeZone; use InvalidArgumentException; /** * Инициализирует другие функции (передача контейнера) */ function _init(Container $c): void { __([$c]); dt(0, true, '', '', true, true, $c); } /** * Переводит строку с подстановкой аргументов * Защита от дурака отсутствует, ловим ошибки/исключения */ function __(string|array $arg): string { static $c, $lang; if (null === $lang) { if (null === $c) { $c = \reset($arg); if (! $c instanceof Container) { throw new InvalidArgumentException('Container expected'); } return ''; } else { $lang = $c->Lang; } } if (\is_array($arg)) { $str = \array_shift($arg); $tr = $lang->get($str); if (null === $tr) { $tr = e($str); } elseif (\is_array($tr)) { $num = \array_shift($arg); $tr = $lang->getForm($tr, $num); } if (empty($arg)) { return $tr; } elseif (\is_array(\reset($arg))) { return \strtr($tr, \array_map('\\ForkBB\\e', \reset($arg))); } else { $arg = \array_map('\\ForkBB\\e', $arg); return \sprintf($tr, ...$arg); } } else { return $lang->get($arg) ?? e($arg); } } /** * Экранирует спецсимволов HTML-сущностями */ function e(string $arg): string { return \htmlspecialchars($arg, \ENT_HTML5 | \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8'); } /** * Возвращает число в формате текущего пользователя */ function num(mixed $number, int $decimals = 0): string { return \is_numeric($number) ? \number_format((float) $number, $decimals, __('lang_decimal_point'), __('lang_thousands_sep')) : '-'; } /** * Возвращает дату/время в формате текущего пользователя */ function dt(int $arg, bool $dateOnly = false, string $dateFormat = null, string $timeFormat = null, bool $timeOnly = false, bool $noText = false, Container $container = null): string { static $c, $offset; if (null !== $container) { $c = $container; return ''; } if (empty($arg)) { return __('Never'); } if (null === $offset) { if (\in_array($c->user->timezone, DateTimeZone::listIdentifiers(), true)) { $dateTimeZone = new DateTimeZone($c->user->timezone); $dateTime = new DateTime('now', $dateTimeZone); $offset = $dateTime->getOffset(); } else { $offset = 0; } } $arg += $offset; if (null === $dateFormat) { $dateFormat = $c->DATE_FORMATS[$c->user->date_format]; } if (null === $timeFormat) { $timeFormat = $c->TIME_FORMATS[$c->user->time_format]; } $date = \gmdate($dateFormat, $arg); if (! $noText) { $now = \time() + $offset; if ($date == \gmdate($dateFormat, $now)) { $date = __('Today'); } elseif ($date == \gmdate($dateFormat, $now - 86400)) { $date = __('Yesterday'); } } if ($dateOnly) { return $date; } elseif ($timeOnly) { return \gmdate($timeFormat, $arg); } else { return $date . ' ' . \gmdate($timeFormat, $arg); } } /** * Возвращает размер в байтах, Кбайтах, ... */ function size(int $size): string { $units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']; for ($i = 0; $size >= 1024; ++$i) { $size /= 1024; } $decimals = $size - (int) $size < 0.005 ? 0 : 2; return __(['%s ' . $units[$i], num($size, $decimals)]); } /** * Возвращает нормализованный (частично) url или пустую строку */ function url(string $url): string { if ( ! isset($url[1]) || \preg_match('%^\s%u', $url) ) { return ''; } switch ($url[0]) { case '/': if ('/' === $url[1]) { $schemeOn = false; $hostOn = true; $url = 'http:' . $url; break; } case '?': case '#': $schemeOn = false; $hostOn = false; $url = 'http://a.a' . $url; break; default: $hostOn = true; if (\preg_match('%^([a-z][a-z0-9+.-]*):(\S)?%i', $url, $m)) { if ( ! isset($m[2]) || isset($m[1][10]) ) { return ''; } $schemeOn = true; } else { $schemeOn = false; $url = 'http://' . $url; } break; } $p = \parse_url($url); if (! \is_array($p)) { return ''; } $scheme = \strtolower($p['scheme'] ?? ''); $result = $schemeOn && $scheme ? $scheme . ':' : ''; switch ($scheme) { case 'javascript': return ''; case 'mailto': if ( isset($p['host']) || ! isset($p['path']) || ! \preg_match('%^([^\x00-\x1F]+)@([^\x00-\x1F\s@]++)$%Du', $p['path'], $m) ) { return ''; } $result .= \rawurlencode(\rawurldecode($m[1])) . '@' . \rawurlencode(\rawurldecode($m[2])); break; case 'tel': if ( isset($p['host']) || ! isset($p['path']) || ! \preg_match('%^\+?[0-9.()-]+$%D', $p['path']) ) { return ''; } $result .= $p['path']; break; default: if ($hostOn && isset($p['host'])) { $result .= '//'; if (isset($p['user'])) { $result .= \rawurlencode(\rawurldecode($p['user'])); if (isset($p['pass'])) { $result .= ':' . \rawurlencode(\rawurldecode($p['pass'])); } $result .= '@'; } if (\preg_match('%[\x80-\xFF]%', $p['host'])) { $p['host'] = \idn_to_ascii($p['host'], 0, \INTL_IDNA_VARIANT_UTS46); } $host = \filter_var($p['host'], \FILTER_VALIDATE_DOMAIN, \FILTER_FLAG_HOSTNAME); if (false !== $host) { $result .= $host; } elseif ( '[' === $p['host'][0] && ']' === $p['host'][-1] && \filter_var(\substr($p['host'], 1, -1), \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6) ) { $result .= $p['host']; } else { return ''; } if (isset($p['port'])) { $result .= ':' . $p['port']; } } if (isset($p['path'])) { $result .= \preg_replace_callback( '%[^/\%\w.~-]|\%(?![0-9a-fA-F]{2})%', function($m) { return \rawurlencode($m[0]); }, $p['path'] ); } break; } if (isset($p['query'])) { $result .= '?' . \preg_replace_callback( '%[^=&\%\w.~-]|\%(?![0-9a-fA-F]{2})%', function($m) { return \rawurlencode($m[0]); }, $p['query'] ); } if (isset($p['fragment'])) { $result .= '#' . \preg_replace_callback( '%[^\%\w.~-]|\%(?![0-9a-fA-F]{2})%', function($m) { return \rawurlencode($m[0]); }, $p['fragment'] ); } return $result; }