<?php
namespace WebBundle\Helper;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\ORMException;
use Doctrine\Persistence\ObjectRepository;
use Exception;
use FlexApp\Constant\TimeConstant;
use FlexApp\Service\BBCodeManager;
use FlexApp\ValueObject\LocaleVo;
use LogicException;
use Memcache;
use Monolog\Logger;
use Pheanstalk\Pheanstalk;
use RuntimeException;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Process;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Translation\IdentityTranslator;
use Symfony\Component\VarDumper\VarDumper;
use Twig_Environment;
use WebBundle\Constant\CookieKeysConstant;
use WebBundle\Entity\ListCountry;
use WebBundle\Entity\User;
use WebBundle\Repository\ListCountryRepository;
use WebBundle\Service\LocationService;
use WebBundle\Service\SphinxSearcherService;
use WebBundle\WebBundle;
/*
* class: Help
* -----------------------------------------------------
* Класс хелпера, для упрошенного обращения в методам и функциям.
* @package WebBundle\Helper
*/
class App
{
private static $isGeneral;
private static $oContainer;
private static $sCountryList;
private static $sCurCountryData;
private static $oRouter;
private static $oPortal;
private static $oSphinxSearcher;
private static $oRequest;
private static $oLogger;
private static $oKernel;
private static $oSession;
private static $oTwig;
private static $oTranslator;
private static $oDoctrine;
private static $oEntityManager;
private static $microtime;
private static array $langsAvailable = [];
/**
* Хелпер Дамепра от Symfony, передавать можно любое количество парметров
* @param $params
* @return mixed
*/
public static function dump(...$params)
{
if ($params) {
$params = (count($params) <= 1) ? $params[0] : $params;
} else {
$params = '';
}
return VarDumper::dump($params);
}
/**
* Хелпер Дамепра от Symfony, передавать можно любое количество парметров
* Обернут в exit , для остановки дальнейшего вывода кода
* @param $params
*/
public static function dumpExit(...$params)
{
if ($params) {
$params = (count($params) <= 1) ? $params[0] : $params;
} else {
$params = '';
}
exit(VarDumper::dump($params));
}
/**
* Иногда удобнее вывести не через дамепр symfony
* @param $params
*/
public static function debug(...$params)
{
if ($params) {
$params = (count($params) <= 1) ? $params[0] : $params;
} else {
$params = '';
}
echo '<pre>';
print_r($params);
echo '</pre>';
}
/**
* Иногда удобнее вывести не через дамепр symfony
* Обернут в exit , для остановки дальнейшего вывода кода
* @param $params
*/
public static function debugExit(...$params)
{
if ($params) {
$params = (count($params) <= 1) ? $params[0] : $params;
} else {
$params = '';
}
echo '<pre>';
print_r($params);
exit('</pre>');
}
/**
* @return ContainerInterface|Container
*/
public static function getContainer()
{
if (!static::$oContainer) {
static::$oContainer = WebBundle::getContainer();
}
return static::$oContainer;
}
/**
* @return string
* @throws Exception
*/
public static function csrfToken()
{
/** @var CsrfToken $securityCsrf */
$securityCsrf = self::getContainer()->get('security.csrf.token_manager')->getToken('authenticate');
return $securityCsrf->getValue();
}
/**
* @param string $id
* @param ?string $locale
* @param ?array $parameters
* @param ?string $domain
* @return string
*/
public static function trans(string $id, ?string $locale = null, ?array $parameters = [], ?string $domain = null): string
{
try {
return static::getTranslator()->trans($id, $parameters, $domain, $locale);
} catch (Exception $e) {
return $id;
}
}
/**
* @param $id
* @param null $locale
* @param array $parameters
* @param null $domain
* @return string
* @throws Exception
*/
public static function transReact($id, $locale = null, array $parameters = [], $domain = null)
{
$localeValue = new LocaleVo($locale, App::getRequest()->request->get('countryCode', null));
$bbCodeManager = static::getContainer()->get(BBCodeManager::class)->init($localeValue);
return $bbCodeManager->parse(static::getTranslator()->trans($id, $parameters, $domain, $locale));
}
/**
* Текущая локаль юзера
* @param bool $full
* @return string
*/
public static function getCurLocale($full = false)
{
return LocaleHelper::getCurLocale($full);
}
/**
* Текущая страна пользователя
* @return string
* @throws Exception
*/
public static function getCurCountry(): string
{
return LocaleHelper::getCurCountry();
}
public static function getCurCountryEntity(): ?ListCountry
{
$countryRepository = static::getRepository(ListCountry::class);
return $countryRepository->find(static::getCurCountryId());
}
/**
* Текущая страна пользователя
* @return int
* @throws Exception
*/
public static function getCurCountryId()
{
$id = self::getCountryList()[LocaleHelper::getCurCountry()]['id'] ?? null;
return empty($id) ? 380 : $id;
}
/**
* Получение кода страны.
*
* @param bool $forIp
* @return null|string
* @throws NonUniqueResultException
* @throws ORMException
* @throws Exception
*/
public static function getCountryCode($forIp = false): ?string
{
$locationCode = CookieHelper::get(CookieKeysConstant::COUNTRY);
// проверяем правильность кода страны
if (!empty($locationCode) && empty(static::getCountryList()[$locationCode])) {
unset($locationCode);
}
if (empty($locationCode)) {
$locationCode = LocaleHelper::getCurCountry();
if ($forIp || $locationCode == 'en') {
$locationCode = static::getCountryByIp();
}
// проверяем правильность кука
if (empty($locationCode) || empty(static::getCountryList()[$locationCode])) {
$locationCode = 'us';
}
CookieHelper::set(CookieKeysConstant::COUNTRY, $locationCode, ['httponly' => false]);
}
return $locationCode;
}
/**
* @param string|null $ip
* @return string
* @throws Exception
*/
public static function getCountryByIp($ip = null)
{
/** @var LocationService $locationService */
$locationService = static::getContainer()->get('app.service.location');
$location = $locationService->getCountryRecord($ip != null ? $ip : static::getRequest()->getClientIp());
return strtolower($location ? $location->country->isoCode : 'us');
}
public static function getLangsAvailable(): array
{
if (empty(static::$langsAvailable)) {
static::$langsAvailable = explode('|', App::getParameter('langs_available'));
}
return static::$langsAvailable;
}
/**
* Список стран, с которыми работаем
* @param bool $full
* @return array
* @throws Exception
*/
public static function getCountryList(bool $full = false): array
{
$translator = self::getTranslator();
$memcache = static::getMemcache();
$memcacheKey = 'APP_GET_COUNTRY_LIST_CACHE' . $translator->getLocale() . $full;
$sCountryList = $memcache->get($memcacheKey);
if (!empty($sCountryList)) {
return $sCountryList;
}
/** @var ListCountryRepository $countryRepo */
$countryRepo = self::getDoctrine()->getRepository(ListCountry::class);
$countries = $countryRepo->getListForLocalize();
$countryList = [];
/** @var ListCountry $country */
foreach ($countries as $country) {
$locales = explode('|', $country['locale']);
$name = $translator->trans($country['alias']);
$code = strtolower($country['code']);
$countryList[$code] = [
'id' => $country['id'],
'code' => $code,
'name' => $name,
'alias' => $country['alias'],
'locale' => $locales[0],
'locales' => $country['locale'],
'localesArr' => $locales,
'pvType' => $country['pvType'],
'phoneCode' => $country['phoneCode'],
];
}
// Сортировка стран по алфавиту
// Если сортировать просто asort($countryList) то он отсортирует по id
$cSort = array_column($countryList, 'name', 'code');
asort($cSort);
$sortedCountryList = [];
foreach ($cSort as $key => $value) {
$sortedCountryList[$key] = $countryList[$key];
}
// принято решение убрать локализацию "весь мир"
// убираем фиктивные страны
if (!$full) {
unset($sortedCountryList['es-cn']);
}
// ставим кеш на месяц
$memcache->add($memcacheKey, $sortedCountryList, MEMCACHE_COMPRESSED, (int) TimeConstant::MONTH);
return $sortedCountryList;
}
/**
* Текущая страна пользователя
* @return array
* @throws Exception
*/
public static function getCurCountryData()
{
if (!static::$sCurCountryData) {
$list = self::getCountryList();
if (self::getCurCountry() == 'en') {
$list[self::getCurCountry()]['pvType'] = 2;
}
static::$sCurCountryData = $list[self::getCurCountry()];
}
return static::$sCurCountryData;
}
/**
* @param $objectName
* @param null $managerName
* @return ObjectRepository
* @throws Exception
*/
public static function getRepository($objectName, $managerName = null)
{
return self::getDoctrine()->getRepository($objectName, $managerName);
}
/**
* @return mixed
* @throws Exception
*/
public static function getCountryListId()
{
$memcache = static::getMemcache();
$memcacheKey = 'APP_GET_COUNTRY_LIST_ID_CACHE';
$sCountryListId = $memcache->get($memcacheKey);
if (!empty($sCountryListId)) {
return $sCountryListId;
}
/** @var ListCountryRepository $countryRepo */
$countryRepo = static::getRepository('WebBundle:ListCountry');
$countries = $countryRepo->getListForLocalize();
$countryList = [];
/** @var ListCountry $country */
foreach ($countries as $country) {
$countryList[] = $country['id'];
}
// ставим кеш на месяц
$memcache->add($memcacheKey, $countryList, MEMCACHE_COMPRESSED, (int) TimeConstant::MONTH);
return $countryList;
}
/**
* @return RouterInterface
*/
public static function getRouter()
{
if (!static::$oRouter) {
try {
static::$oRouter = static::getContainer()->get('router');
} catch (Exception $e) {
}
}
return static::$oRouter;
}
/**
* @return PortalHelper
* @throws Exception
*/
public static function getPortal()
{
if (!static::$oPortal) {
static::$oPortal = static::getContainer()->get('portal');
}
return static::$oPortal;
}
/**
* Получение значений из параметров
* @param string $name
* @return mixed
* @throws InvalidArgumentException
*/
public static function getParameter($name)
{
return self::getContainer()->getParameter($name);
}
/**
* Получение имени своего домена без лишних данных из конфига.
* @return mixed|string
*/
public static function getDomain()
{
$domain = static::getParameter('domain');
$domain = trim($domain, '.');
$domain = trim($domain);
return $domain;
}
/**
* @return Memcache
* @throws Exception
*/
public static function getMemcache(): Memcache
{
return static::getContainer()->get('app.service.cache')->getMemcache();
}
/**
* Получаем чистое имя класса без неймспейсов
* @param $obj
* @return null|string
*/
public static function getClassName($obj)
{
$name = is_object($obj) ? get_class($obj) : $obj;
$name = str_replace('\\', '/', $name);
$name = explode('/', $name);
$name = array_pop($name);
return $name;
}
/**
* @return object|SphinxSearcherService
* @throws Exception
*/
public static function getSphinxSearcher()
{
if (!static::$oSphinxSearcher) {
static::$oSphinxSearcher = static::getContainer()->get('sphinx_searcher');
}
return static::$oSphinxSearcher;
}
/**
* @param array $data
* @param int $limit
* @param bool $withSchema
* @param bool $full
* @param bool $getCount
* @return array
* @throws Exception
*/
public static function searchSphinx($data = [], $limit = 0, $withSchema = false, $full = false, $getCount = false)
{
return static::getSphinxSearcher()->searchSphinx($data, $limit, $withSchema, $full, $getCount);
}
/**
* @return bool
*/
public static function getHash()
{
return md5(static::getContainer()->getParameter('salt') . date("Y.m.d"));
}
/**
* генератор MetaDescription из тектовой стороки
* @param $text
* @return string
*/
public static function genMetaDescription($text)
{
$limitChar = 150;
$desc = '';
$text = strip_tags($text);
$text = html_entity_decode($text);
$arr = explode(".", $text);
foreach ($arr as $str) {
$descTmp = '';
if (mb_strlen($str, 'UTF-8') > 3) {
$descTmp = $desc . $str . '. ';
}
if (mb_strlen($descTmp, 'UTF-8') > $limitChar && mb_strlen($desc, 'UTF-8') > 80) {
break;
}
if (mb_strlen($descTmp, 'UTF-8') > $limitChar) {
$descTmp = StrHelper::cutText($descTmp, $limitChar);
}
$desc = $descTmp;
}
$desc = trim($desc);
return $desc;
}
/**
* @param $route
* @param array $parameters
* @param bool|int $referenceType
* @return string
* @throws Exception
*/
public static function generateUrl($route, $parameters = [], $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH)
{
$url = static::getRouter()->generate($route, $parameters, $referenceType);
return urldecode($url);
}
/**
* Перевод строки ТП в массив. Только строки коротые под plural
* @param string $str
* @return string[]
*/
public static function transPluralStrToArray(string $str): array
{
$rows = explode('|', $str);
$pattern = '~\{(\d)}(.*)~';
foreach ($rows as $i => $r) {
preg_match($pattern, $r, $out);
$rows[$i] = [
'key' => intval($out[1]),
'val' => trim($out[2]),
];
}
return $rows;
}
/**
* Получаем склонение, в виде числа, что соответвует
* 0 = 0 комментариев
* 1 = 1 комментарий
* 2 = 2 комментария
* 3 = 5 комментариев
* @param $number
* @param null $lc
* @param null $allowLang
* @param int $allowKey включает доп ключь для варианта перевода
* @return int
*/
public static function plural($number, $lc = null, $allowLang = null, $allowKey = 0)
{
$lc = $lc ? $lc : App::getCurLocale();
$number = intval($number);
$cases = [2, 0, 1, 1, 1, 2];
if ($number > 0) {
$res = ($number % 100 > 4 && $number % 100 < 20) ? 2 : $cases[min($number % 10, 5)];
$res = $res + 1;
} else {
$res = $number;
}
// сделал что бы можно было включать желаемый вариант сколнения по по языку и добовлять нужный ключь склонения
if ($allowKey > 1 && $allowLang && $lc == $allowLang) {
if ($number > 1 && $res == $allowKey) {
$res = $allowKey;
} else {
$res = 3;
}
} else {
// фикс склонения для всех языков кроме RU , если число больше одного и имеет в окончании единицу, то ставим множественное склонение
if ($lc != 'ru') {
if ($number > 1) {
$res = 3;
}
}
}
// фикс склонения для всех языков кроме RU , если число больше одного и имеет в окончании единицу, то ставим множественное склонение
return $res;
}
/**
* @param string $controller
* @param array $path
* @param array $query
* @return mixed
* @throws Exception
*/
public static function forward($controller, array $path = [], array $query = [])
{
$request = self::getRequest();
$path['_forwarded'] = $request->attributes;
$path['_controller'] = $controller;
$subRequest = $request->duplicate($query, null, $path);
return self::getContainer()->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
/**
* Проверяем значение на число
* @param $val
* @return bool
*/
public static function isInt($val)
{
preg_match_all('/[0-9]+/', $val, $matchId);
$matchId = ArrHelper::get($matchId, '0.0');
if ($matchId) {
if ($matchId != $val) {
$res = false; // строка c числом
} else {
$res = true; // число
}
} else {
$res = false; // строка
}
return $res;
}
/**
* Получить юзера из его токена
* @return User|null
* @throws Exception
*/
public static function getCurUser(): ?User
{
if (!App::getContainer()->has('security.token_storage')) {
throw new LogicException('The SecurityBundle is not registered in your application.');
}
/** @var TokenInterface $securiToken */
if ($securiToken = App::getContainer()->get('security.token_storage')->getToken()) {
$user = $securiToken->getUser();
if ($user instanceof User) {
return $user;
}
}
return null;
}
/**
* Проверка на расположения кода: локально или на боевом сервере
*
* @return bool
*/
public static function isGeneral(): bool
{
if (self::$isGeneral == null) {
$isGeneral = false;
if (self::getContainer()->hasParameter('general')) {
$isGeneral = self::getContainer()->getParameter('general');
}
self::$isGeneral = boolval($isGeneral);
}
return self::$isGeneral;
}
/**
* Проверяем на наличие DEV окружения, не зависимо TE это или TR
* @return bool
* @throws Exception
*/
public static function isDev()
{
$env = static::getKernel()->getEnvironment();
return StrHelper::isInStr($env, 'dev');
}
/**
* Проверяем на наличие DEV окружения и подставляет немецкий ip в этом случае
* @param Request|null $request
* @return null|string
* @throws Exception
*/
public static function getIp(?Request $request = null): ?string
{
$request = $request ?: App::getRequest();
return App::isDev() ? '213.136.79.122' : $request->getClientIp();
}
/**
* Проверка на локальную копию сайта по хосту
* @return bool
*/
public static function isLocEnv()
{
$host = RequestHelper::getRequest()->getHost();
if ($host == '') {
return false;
}
if ($host == 'adm.tile.expert') {
return false;
}
if ($host == 'tile.expert') {
return false;
}
return true;
}
/**
* проверка окружения для вакансий - учетом локали ру
* @param bool $isDev
* @return bool
* @throws Exception
*/
public static function isVacancyTE($isDev = false)
{
$env = static::getKernel()->getEnvironment();
if ($isDev) {
return $env == 'te_dev';
} else {
return StrHelper::isInStr($env, 'te');
}
}
/**
* Запуск EXEC из PHP
* @param $command
* @return array|false|string|string[]
*/
public static function runConsole($command)
{
if (!is_array($command)) {
$command = explode(' ', $command);
}
$process = new Process($command);
$process->setTimeout((int) TimeConstant::MINUTE * 30);
try {
$process->run();
if (!$process->isSuccessful()) {
return ['error' => $process->getErrorOutput()];
}
$out = $process->getOutput();
$process->stop();
} catch (ProcessTimedOutException $e) {
return ['error' => 'TimedOut Error'];
}
return $out;
}
/**
* @return bool
* @throws Exception
* @todo убрать после отладки
* Проверка окружения TEST
*/
public static function isTest()
{
$env = static::getKernel()->getEnvironment();
return StrHelper::isInStr($env, 'test');
}
/**
* получить сервис очередей
* @return Pheanstalk
*/
public static function getPheanstalk()
{
if (static::getContainer()->hasParameter('pheanstalk_server')) {
$pheanstalkHost = static::getParameter('pheanstalk_server');
} else {
throw new RuntimeException('parameter not found: pheanstalk_server');
}
$pheanstalk = new Pheanstalk($pheanstalkHost);
if (null === $pheanstalk) {
throw new RuntimeException('Pheanstalk not found: ');
}
if (!$pheanstalk->getConnection()->isServiceListening()) {
throw new RuntimeException('Pheanstalk not connected');
}
return $pheanstalk;
}
/**
* @return object|Request
* @throws LogicException
*/
public static function getRequest()
{
if (!self::$oRequest) {
self::$oRequest = RequestHelper::getRequest();
}
return self::$oRequest;
}
/**
* @return Logger
* @throws Exception
*/
public static function getLogger()
{
if (!self::$oLogger) {
if (!self::getContainer()->has('logger')) {
throw new LogicException('The Monolog/Logger is not found');
}
/** @var Logger $oLogger */
$oLogger = self::getContainer()->get('logger');
self::$oLogger = $oLogger;
}
return self::$oLogger;
}
/**
* @return KernelInterface|Kernel
* @throws Exception
*/
public static function getKernel()
{
if (!self::$oKernel) {
if (!self::getContainer()->has('kernel')) {
throw new LogicException('The KernelInterface is not found');
}
self::$oKernel = self::getContainer()->get('kernel');
}
return self::$oKernel;
}
/**
* @return Session
* @throws Exception
*/
public static function getSession()
{
if (!self::$oSession) {
if (!self::getContainer()->has('session')) {
throw new LogicException('The Session is not found');
}
/** @var Session $oSession */
$oSession = self::getContainer()->get('session');
self::$oSession = $oSession;
}
return self::$oSession;
}
/**
* @return Twig_Environment
* @throws Exception
*/
public static function getTwig()
{
if (!self::getContainer()->has('twig')) {
throw new LogicException('The twig is not found');
}
if (!self::$oTwig) {
/** @var Twig_Environment $oTwig */
$oTwig = self::getContainer()->get('twig');
self::$oTwig = $oTwig;
}
return self::$oTwig;
}
/**
* @return IdentityTranslator
* @throws LogicException
* @throws Exception
*/
public static function getTranslator()
{
if (!self::$oTranslator) {
if (!self::getContainer()->has('translator')) {
throw new LogicException('The translator is not found');
}
self::$oTranslator = self::getContainer()->get('translator');
}
return self::$oTranslator;
}
/**
* @return Registry
* @throws LogicException
* @throws Exception
*/
public static function getDoctrine()
{
if (!self::$oDoctrine) {
if (!self::getContainer()->has('doctrine')) {
throw new LogicException('The DoctrineBundle is not registered');
}
self::$oDoctrine = self::getContainer()->get('doctrine');
}
return self::$oDoctrine;
}
/**
* @param null $name
* @return EntityManager
* @throws Exception
*/
public static function em($name = null)
{
$key = $name ? $name : 'default';
if (empty(self::$oEntityManager[$key])) {
self::$oEntityManager[$key] = self::getDoctrine()->getManager($name);
}
return self::$oEntityManager[$key];
}
/**
* @return EntityManager
* @throws Exception
*/
public static function emAdm()
{
return self::em('logs');
}
public static function microTime($text = '', $stop = false)
{
if (empty(self::$microtime)) {
self::$microtime = microtime(true);
} else {
$t = microtime(true);
$res = round((float) $t, 3) - round((float) self::$microtime, 3);
self::$microtime = $t;
if (!empty($text)) {
$res = $text . ': ' . $res;
}
if ($stop) {
App::dumpExit($res);
} else {
App::dump($res);
}
}
}
/**
* @param $name
* @return bool
* @throws Exception
*/
public static function isRole($name)
{
if (App::getContainer()->get('security.token_storage')->getToken() === null) {
return false;
} else {
return self::getContainer()->get('security.authorization_checker')->isGranted($name);
}
}
/**
* sizer - функция для красивого отображения размерности файлов("1024" -> "1 kb")
*
* @param $size integer bytes
* @return string
*/
public static function sizer($size)
{
$unit = ['b', 'kb', 'mb', 'gb', 'tb', 'pb'];
return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $unit[$i];
}
/**
* @param string $functionName
* @return bool
*/
public static function checkCallFunction(string $functionName)
{
$traces = debug_backtrace();
if ($traces) {
foreach ($traces as $trace) {
if ($trace['function'] == $functionName) {
return true;
}
}
}
return false;
}
}