LocaleUrlResponseListener::getCurrentLocale()   B
last analyzed

Complexity

Conditions 8
Paths 24

Size

Total Lines 26
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 26
rs 8.4444
c 0
b 0
f 0
cc 8
nc 24
nop 1
1
<?php
2
3
namespace Application\Bundle\DefaultBundle\EventListener;
4
5
use JMS\I18nRoutingBundle\Router\I18nRouter;
6
use Maxmind\Bundle\GeoipBundle\Service\GeoipManager;
7
use Monolog\Logger;
8
use Symfony\Component\HttpFoundation\RedirectResponse;
9
use Symfony\Component\HttpFoundation\Request;
10
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
11
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
12
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
13
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
14
use Symfony\Component\Routing\Router;
15
16
/**
17
 * Class LocaleUrlRequestListener.
18
 */
19
class LocaleUrlResponseListener
20
{
21
    const UKRAINE_COUNTRY_CODE = 'UA';
22
23
    private $defaultLocale;
24
25
    private $locales;
26
27
    private $cookieName;
28
29
    /** @var I18nRouter */
30
    private $routerService;
31
32
    private $geoIpService;
33
34
    /** @var Logger */
35
    private $logger;
36
37
    /**
38
     * LocaleUrlResponseListener constructor.
39
     *
40
     * @param string       $defaultLocale
41
     * @param array        $locales
42
     * @param string       $cookieName
43
     * @param Router       $routerService
44
     * @param GeoipManager $geoIpService
45
     * @param Logger       $logger
46
     */
47
    public function __construct($defaultLocale, array $locales, $cookieName, $routerService, $geoIpService, $logger)
48
    {
49
        $this->defaultLocale = $defaultLocale;
50
        $this->locales = $locales;
51
        $this->cookieName = $cookieName;
52
        $this->routerService = $routerService;
0 ignored issues
show
Documentation Bug introduced by
$routerService is of type Symfony\Component\Routing\Router, but the property $routerService was declared to be of type JMS\I18nRoutingBundle\Router\I18nRouter. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
53
        $this->geoIpService = $geoIpService;
54
        $this->logger = $logger;
55
    }
56
57
    /**
58
     * @param GetResponseEvent $event
59
     */
60
    public function onKernelRequest(GetResponseEvent $event)
61
    {
62
        if (!$event->isMasterRequest()) {
63
            return;
64
        }
65
66
        $request = $event->getRequest();
67
        $locale = $this->getCurrentLocale($request);
68
69
        if ($locale === $this->defaultLocale) {
70
            $request->setLocale($locale);
71
72
            return;
73
        }
74
75
        $path = $request->getPathInfo();
76
        $currentLocal = $this->getInnerSubstring($path, '/');
77
        if ('' === rtrim($path, '/')) {
78
            $params = $request->query->all();
79
            $event->setResponse(new RedirectResponse($request->getBaseUrl().'/'.$locale.($params ? '?'.http_build_query($params) : '/'), 301));
80
        } elseif ('admin' === $currentLocal && $locale !== $this->defaultLocale) {
81
            $params = $request->query->all();
82
            unset($params[$this->cookieName]);
83
            $request->setLocale($this->defaultLocale);
84
            $event->setResponse(new RedirectResponse($request->getBaseUrl().$path.($params ? '?'.http_build_query($params) : '/'), 301));
85
        } elseif (!in_array($currentLocal, $this->locales, true)) {
86
            try {
87
                $matched = $this->routerService->match('/'.$locale.$path);
88
            } catch (ResourceNotFoundException $e) {
89
                $matched = false;
90
            }
91
            if (false !== $matched) {
92
                $params = $request->query->all();
93
                $event->setResponse(new RedirectResponse($request->getBaseUrl().'/'.$locale.$path.($params ? '?'.http_build_query($params) : ''), 301));
94
            }
95
        }
96
    }
97
98
    /**
99
     * @param GetResponseForExceptionEvent $event
100
     */
101
    public function onKernelException(GetResponseForExceptionEvent $event)
102
    {
103
        if (!$event->isMasterRequest()) {
104
            return;
105
        }
106
107
        $ex = $event->getException();
108
        if (!$ex instanceof NotFoundHttpException || !$ex->getPrevious() instanceof ResourceNotFoundException) {
109
            return;
110
        }
111
112
        $request = $event->getRequest();
113
114
        $path = $request->getPathInfo();
115
        $currentLocal = $this->getInnerSubstring($path, '/');
116
117
        if (in_array($currentLocal, $this->locales, true)) {
118
            $request->setLocale($currentLocal);
119
        }
120
121
        if ($currentLocal === $this->defaultLocale) {
122
            $params = $request->query->all();
123
            unset($params[$this->cookieName]);
124
            $path = ltrim($path, '/'.$currentLocal);
125
            $event->setResponse(new RedirectResponse($request->getBaseUrl().'/'.$path.($params ? '?'.http_build_query($params) : ''), 301));
126
        }
127
    }
128
129
    /**
130
     * @param Request $request
131
     *
132
     * @return mixed
133
     */
134
    private function getCurrentLocale($request)
135
    {
136
        $local = null;
137
138
        // get local from cookie
139
        if ($request instanceof Request) {
0 ignored issues
show
introduced by
$request is always a sub-type of Symfony\Component\HttpFoundation\Request.
Loading history...
140
            if ($request->cookies->has($this->cookieName)
141
                && in_array($request->cookies->get($this->cookieName), $this->locales, true)) {
142
                $local = $request->cookies->get($this->cookieName);
143
            }
144
        }
145
146
        if (!$local) {
147
            if (false !== $this->geoIpService->lookup($this->getRealIpAddr($request))) {
148
                if (self::UKRAINE_COUNTRY_CODE === $this->geoIpService->getCountryCode()) {
149
                    $local = $this->defaultLocale;
150
                }
151
            }
152
        }
153
154
        // get locale from preferred languages
155
        if (!$local) {
156
            $local = $request->getPreferredLanguage($this->locales);
157
        }
158
159
        return $local;
160
    }
161
162
    /**
163
     * @param string $string
164
     * @param string $delim
165
     * @param int    $keyNumber
166
     *
167
     * @return string
168
     */
169
    private function getInnerSubstring($string, $delim, $keyNumber = 1)
170
    {
171
        $string = explode($delim, $string, 3);
172
173
        return isset($string[$keyNumber]) ? $string[$keyNumber] : '';
174
    }
175
176
    /**
177
     * @param Request $request
178
     *
179
     * @return null|string
180
     */
181
    private function getRealIpAddr($request)
182
    {
183
        $server = $request->server;
184
        if (!$server) {
0 ignored issues
show
introduced by
$server is of type Symfony\Component\HttpFoundation\ServerBag, thus it always evaluated to true.
Loading history...
185
            return null;
186
        }
187
        $ip = null;
188
        if ($server->has('HTTP_CLIENT_IP')) {
189
            $ip = filter_var($server->get('HTTP_CLIENT_IP'), FILTER_VALIDATE_IP);
190
        }
191
192
        if (!$ip && $server->has('HTTP_X_FORWARDED_FOR')) {
193
            $ip = filter_var($server->get('HTTP_X_FORWARDED_FOR'), FILTER_VALIDATE_IP);
194
        }
195
196
        if (!$ip) {
197
            $ip = $server->get('REMOTE_ADDR');
198
        }
199
200
        return $ip;
201
    }
202
}
203