<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RequestContextAwareInterface;
use App\Admin\Entity\StdLanguages;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\Cookie;
use Doctrine\ORM\EntityManagerInterface;
/**
* When visiting the homepage, this listener redirects the user to the most
* appropriate localized version according to the browser settings.
*
* See https://symfony.com/doc/current/components/http_kernel.html#the-kernel-request-event
*
* @author Oleg Voronkovich <oleg-voronkovich@yandex.ru>
*/
class DefaultLocaleSubscriber implements EventSubscriberInterface
{
private $availablelocales = array();
private $defaultlocale = null;
private $router;
private $requeststack;
private $mainrequest;
/**
* @var EntityManagerInterface
*/
private $entitymanager;
public function __construct(EntityManagerInterface $entityManager, RequestStack $requestStack, RequestContextAwareInterface $router = null)
{
$this->requeststack = $requestStack;
$this->mainrequest = $requestStack->getMainRequest();
$this->router = $router;
$this->entitymanager = $entityManager;
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 17],
KernelEvents::FINISH_REQUEST => ['onKernelFinishRequest', 1],
KernelEvents::RESPONSE => ['onKernelResponse'],
];
}
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
if (in_array($request->attributes->get('_route'),['liip_imagine_filter', '_profiler', '_wdt']))
return; // Ignore liip imagine and profiler requests
$this->loadLanguages();
$request->setDefaultLocale($this->defaultlocale);
// Find the first non-null locale from various sources
$locale = $this->urlLocale($request);
// Check if session exists before using it
if ($locale === null && $request->hasSession()) {
$session = $request->getSession();
if ($session->isStarted()) {
$locale = $this->sessionLocale($session);
}
}
if ($locale === null) {
$locale = $this->cookieLocale($request);
}
if ($locale === null) {
$locale = $this->browserLocale($request);
}
if ($locale === null) {
$locale = $this->defaultlocale;
}
// Only set the session if it exists and is started
if ($request->hasSession()) {
$session = $request->getSession();
if ($session->isStarted()) {
$session->set("locale", $locale);
}
}
$request->setLocale($locale);
$this->setRouterContext($request);
$this->locale = $locale;
if (!($this->mainrequest->attributes->get('webpackEntries'))) {
$this->mainrequest->attributes->set('webpackEntries', []);
}
}
public function onKernelFinishRequest(FinishRequestEvent $event)
{
if (null !== $parentRequest = $this->requeststack->getParentRequest()) {
$this->setRouterContext($parentRequest);
}
}
public function onKernelResponse(ResponseEvent $event)
{
// Use a single condition to check for profiler routes
if (in_array($event->getRequest()->attributes->get('_route'), ['liip_imagine_filter', '_profiler', '_wdt']) ||
strpos($event->getRequest()->getRequestURI(), '_wdt') !== false ||
strpos($event->getRequest()->getRequestURI(), '_profiler') !== false) {
return; // Ignore profiler and liip imagine requests
}
if (sizeof($this->availablelocales) > 1 && \in_array($event->getRequest()->getLocale(), $this->availablelocales))
$event->getResponse()->headers->setCookie(Cookie::create('locale', $event->getRequest()->getLocale(), time() + (365 * 24 * 60 * 60),getenv("COOKIE_PATH"),getenv("COOKIE_DOMAIN")));
}
private function setRouterContext(Request $request)
{
if (null !== $this->router) {
$this->router->getContext()->setParameter('_locale', $request->getLocale());
}
}
private function urlLocale($request)
{
if (\in_array(\strtolower($request->query->get('_locale','')),$this->availablelocales))
{
return \strtolower($request->query->get('_locale'));
}
list($null, $segment) = \explode('/', $request->getRequestURI());
if (\in_array(\strtolower($segment),$this->availablelocales))
{
return \strtolower($segment);
}
return null;
}
private function sessionLocale(SessionInterface $session)
{
if($session->get("locale")){
return $session->get("locale");
}
return null;
}
private function cookieLocale($request)
{
return \in_array(\strtolower($request->cookies->get('locale','')),$this->availablelocales) ? $request->cookies->get('locale','') : null;
}
private function browserLocale($request)
{
$http_accept = $request->server->get('HTTP_ACCEPT_LANGUAGE','');
$locale = null;
$locales = []; // Initialize $locales array to avoid undefined variable
if(\strlen($http_accept) > 1)
{
# Split possible languages into array
$x = \explode(",",$http_accept);
foreach ($x as $val)
{
#check for q-value and create associative array. No q-value means 1 by rule
if(\preg_match("/(.*);q=([0-1]{0,1}\.\d{0,4})/i",$val,$matches))
$locales[$matches[1]] = (float)$matches[2];
else
$locales[$val] = 1.0;
}
#return default language (highest q-value)
$qval = 0.0;
foreach ($locales as $langAndCountry => $q)
{
$langAndCountry = \str_replace( "_", "-", $langAndCountry );
$arr = \explode("-", $langAndCountry);
$lang = $arr[0];
if ($q > $qval)
{
if (\in_array(\strtolower($langAndCountry),$this->availablelocales))
{
$qval = (float)$q;
$locale = \strtolower($langAndCountry);
}
else if (\in_array(\strtolower($lang),$this->availablelocales))
{
$qval = (float)$q;
$locale = \strtolower($lang);
}
}
}
}
return $locale;
}
private function loadLanguages()
{
if (is_null($this->defaultlocale))
{
if ($languages = $this->entitymanager->getRepository(StdLanguages::class)->findBy(['isActive' => 1,'isPublic' => 1]))
{
foreach ($languages as $language)
{
$this->availablelocales[] = $language->getLanguageCode();
$language->getIsDefault() && $this->defaultlocale = $language->getLanguageCode();
}
}
if (is_null($this->defaultlocale)) {
throw new \UnexpectedValueException('The default locale is not defined in the database.');
}
if (empty($this->availablelocales)) {
throw new \UnexpectedValueException('The list of supported locales must not be empty.');
}
$this->defaultlocale = $this->defaultlocale ?: $this->availablelocales[0];
if (!\in_array($this->defaultlocale, $this->availablelocales, true)) {
throw new \UnexpectedValueException(sprintf('The default locale ("%s") must be one of "%s".', $this->defaultlocale, implode(', ', $this->availablelocales)));
}
}
}
}