Completed
Push — master ( 43338e...78bbf2 )
by ARCANEDEV
25s queued 11s
created

Localization   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 486
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 98.29%

Importance

Changes 0
Metric Value
dl 0
loc 486
ccs 115
cts 117
cp 0.9829
rs 8.4
c 0
b 0
f 0
wmc 50
lcom 1
cbo 8

28 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A request() 0 4 1
A getDefaultLocale() 0 4 1
A getSupportedLocales() 0 4 1
A setSupportedLocales() 0 6 1
A getSupportedLocalesKeys() 0 4 1
A getCurrentLocale() 0 4 1
A getCurrentLocaleEntity() 0 4 1
A getCurrentLocaleName() 0 4 1
A getCurrentLocaleScript() 0 4 1
A getCurrentLocaleDirection() 0 4 1
A getCurrentLocaleNative() 0 4 1
A getCurrentLocaleRegional() 0 4 1
A getAllLocales() 0 4 1
A setLocale() 0 4 1
A setBaseUrl() 0 8 2
A transRoute() 0 4 1
A localizeURL() 0 4 1
A getNonLocalizedURL() 0 4 1
A createUrlFromUri() 0 8 2
A localesNavbar() 0 8 1
A findTranslatedRouteByUrl() 0 15 4
A setRouteNameFromRequest() 0 8 1
A isDefaultLocaleHiddenInUrl() 0 4 1
A isLocaleSupported() 0 4 2
A isLocaleSupportedOrFail() 0 7 2
C getLocalizedURL() 0 60 15
A getUrlFromRouteName() 0 18 2

How to fix   Complexity   

Complex Class

Complex classes like Localization often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Localization, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arcanedev\Localization;
6
7
use Arcanedev\Localization\Contracts\LocalesManager as LocalesManagerContract;
8
use Arcanedev\Localization\Contracts\Localization as LocalizationContract;
9
use Arcanedev\Localization\Contracts\RouteTranslator as RouteTranslatorContract;
10
use Arcanedev\Localization\Exceptions\UnsupportedLocaleException;
11
use Arcanedev\Localization\Utilities\Url;
12
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
13
use Illuminate\Contracts\View\Factory as ViewFactoryContract;
14
use Illuminate\Http\Request;
15
16
/**
17
 * Class     Localization
18
 *
19
 * @author   ARCANEDEV <[email protected]>
20
 */
21
class Localization implements LocalizationContract
22
{
23
    /* -----------------------------------------------------------------
24
     |  Properties
25
     | -----------------------------------------------------------------
26
     */
27
28
    /**
29
     * Base url.
30
     *
31
     * @var string
32
     */
33
    protected $baseUrl;
34
35
    /**
36
     * Laravel application instance.
37
     *
38
     * @var \Illuminate\Contracts\Foundation\Application
39
     */
40
    private $app;
41
42
    /**
43
     * The RouteTranslator instance.
44
     *
45
     * @var \Arcanedev\Localization\Contracts\RouteTranslator
46
     */
47
    protected $routeTranslator;
48
49
    /**
50
     * The LocalesManager instance.
51
     *
52
     * @var \Arcanedev\Localization\Contracts\LocalesManager
53
     */
54
    private $localesManager;
55
56
    /* -----------------------------------------------------------------
57
     |  Constructor
58
     | -----------------------------------------------------------------
59
     */
60
61
    /**
62
     * Localization constructor.
63
     *
64
     * @param  \Illuminate\Contracts\Foundation\Application       $app
65
     * @param  \Arcanedev\Localization\Contracts\RouteTranslator  $routeTranslator
66
     * @param  \Arcanedev\Localization\Contracts\LocalesManager   $localesManager
67
     */
68 544
    public function __construct(
69
        ApplicationContract     $app,
70
        RouteTranslatorContract $routeTranslator,
71
        LocalesManagerContract  $localesManager
72
    ) {
73 544
        $this->app             = $app;
74 544
        $this->routeTranslator = $routeTranslator;
75 544
        $this->localesManager  = $localesManager;
76
77 544
        $this->localesManager->setDefaultLocale(
78 544
            $this->app['config']->get('app.locale')
79
        );
80 544
    }
81
82
    /* -----------------------------------------------------------------
83
     |  Getters & Setters
84
     | -----------------------------------------------------------------
85
     */
86
87
    /**
88
     * Get Request instance.
89
     *
90
     * @return \Illuminate\Http\Request
91
     */
92 212
    private function request()
93
    {
94 212
        return $this->app['request'];
95
    }
96
97
    /**
98
     * Returns default locale.
99
     *
100
     * @return string
101
     */
102 224
    public function getDefaultLocale()
103
    {
104 224
        return $this->localesManager->getDefaultLocale();
105
    }
106
107
    /**
108
     * Return an array of all supported Locales.
109
     *
110
     * @return \Arcanedev\Localization\Entities\LocaleCollection
111
     */
112 216
    public function getSupportedLocales()
113
    {
114 216
        return $this->localesManager->getSupportedLocales();
115
    }
116
117
    /**
118
     * Set the supported locales.
119
     *
120
     * @param  array  $supportedLocales
121
     *
122
     * @return self
123
     */
124 8
    public function setSupportedLocales(array $supportedLocales)
125
    {
126 8
        $this->localesManager->setSupportedLocales($supportedLocales);
127
128 4
        return $this;
129
    }
130
131
    /**
132
     * Get supported locales keys.
133
     *
134
     * @return array
135
     */
136 4
    public function getSupportedLocalesKeys()
137
    {
138 4
        return $this->localesManager->getSupportedLocalesKeys();
139
    }
140
141
    /**
142
     * Returns current language.
143
     *
144
     * @return string
145
     */
146 220
    public function getCurrentLocale()
147
    {
148 220
        return $this->localesManager->getCurrentLocale();
149
    }
150
151
    /**
152
     * Returns current language.
153
     *
154
     * @return \Arcanedev\Localization\Entities\Locale
155
     */
156 20
    public function getCurrentLocaleEntity()
157
    {
158 20
        return $this->localesManager->getCurrentLocaleEntity();
159
    }
160
161
    /**
162
     * Returns current locale name.
163
     *
164
     * @return string
165
     */
166 4
    public function getCurrentLocaleName()
167
    {
168 4
        return $this->getCurrentLocaleEntity()->name();
169
    }
170
171
    /**
172
     * Returns current locale script.
173
     *
174
     * @return string
175
     */
176 4
    public function getCurrentLocaleScript()
177
    {
178 4
        return $this->getCurrentLocaleEntity()->script();
179
    }
180
181
    /**
182
     * Returns current locale direction.
183
     *
184
     * @return string
185
     */
186 4
    public function getCurrentLocaleDirection()
187
    {
188 4
        return $this->getCurrentLocaleEntity()->direction();
189
    }
190
191
    /**
192
     * Returns current locale native name.
193
     *
194
     * @return string
195
     */
196 4
    public function getCurrentLocaleNative()
197
    {
198 4
        return $this->getCurrentLocaleEntity()->native();
199
    }
200
201
    /**
202
     * Returns current locale regional.
203
     *
204
     * @return string
205
     */
206 4
    public function getCurrentLocaleRegional()
207
    {
208 4
        return $this->getCurrentLocaleEntity()->regional();
209
    }
210
211
    /**
212
     * Get all locales.
213
     *
214
     * @return \Arcanedev\Localization\Entities\LocaleCollection
215
     */
216 4
    public function getAllLocales()
217
    {
218 4
        return $this->localesManager->getAllLocales();
219
    }
220
221
    /**
222
     * Set and return current locale.
223
     *
224
     * @param  string|null  $locale
225
     *
226
     * @return string
227
     */
228 544
    public function setLocale($locale = null)
229
    {
230 544
        return $this->localesManager->setLocale($locale);
231
    }
232
233
    /**
234
     * Sets the base url for the site.
235
     *
236
     * @param  string  $url
237
     *
238
     * @return $this
239
     */
240 544
    public function setBaseUrl($url)
241
    {
242 544
        if (substr($url, -1) !== '/') $url .= '/';
243
244 544
        $this->baseUrl = $url;
245
246 544
        return $this;
247
    }
248
249
    /* -----------------------------------------------------------------
250
     |  Main Methods
251
     | -----------------------------------------------------------------
252
     */
253
254
    /**
255
     * Translate routes and save them to the translated routes array (used in the localize route filter).
256
     *
257
     * @param  string  $routeName
258
     *
259
     * @return string
260
     */
261 544
    public function transRoute($routeName)
262
    {
263 544
        return $this->routeTranslator->trans($routeName);
264
    }
265
266
    /**
267
     * Returns an URL adapted to $locale or current locale.
268
     *
269
     * @param  string|null  $url
270
     * @param  string|null  $locale
271
     *
272
     * @return string
273
     */
274 8
    public function localizeURL($url = null, $locale = null)
275
    {
276 8
        return $this->getLocalizedURL($locale, $url);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->getLocalizedURL($locale, $url); of type string|false adds false to the return on line 276 which is incompatible with the return type declared by the interface Arcanedev\Localization\C...calization::localizeURL of type string. It seems like you forgot to handle an error condition.
Loading history...
277
    }
278
279
    /**
280
     * It returns an URL without locale (if it has it).
281
     *
282
     * @param  string|null  $url
283
     *
284
     * @return string
285
     */
286 212
    public function getNonLocalizedURL($url = null)
287
    {
288 212
        return $this->getLocalizedURL(false, $url);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Comprehensibility Best Practice introduced by
The expression $this->getLocalizedURL(false, $url); of type string|false adds false to the return on line 288 which is incompatible with the return type declared by the interface Arcanedev\Localization\C...ion::getNonLocalizedURL of type string. It seems like you forgot to handle an error condition.
Loading history...
289
    }
290
291
    /**
292
     * Returns an URL adapted to $locale or current locale.
293
     *
294
     * @param  string|null  $locale
295
     * @param  string|null  $url
296
     * @param  array        $attributes
297
     * @param  bool|false   $showHiddenLocale
298
     *
299
     * @return string|false
300
     */
301 212
    public function getLocalizedURL($locale = null, $url = null, array $attributes = [], $showHiddenLocale = false)
302
    {
303 212
        if (is_null($locale))
304 20
            $locale = $this->getCurrentLocale();
305
306 212
        $this->isLocaleSupportedOrFail($locale);
307
308 212
        if (empty($attributes))
309 212
            $attributes = Url::extractAttributes($url);
310
311 212
        if (empty($url)) {
312 20
            if ($this->routeTranslator->hasCurrentRoute()) {
313 4
                if (empty($attributes))
314 4
                    $attributes = $this->request()->route()->parameters();
315
316 4
                return $this->getUrlFromRouteName(
317 4
                    $locale,
318 4
                    $this->routeTranslator->getCurrentRoute(),
319
                    $attributes,
320
                    $showHiddenLocale
321
                );
322
            }
323
324 20
            $url = $this->request()->fullUrl();
325
        }
326
327
        if (
328 212
            $locale &&
329 212
            $translatedRoute = $this->findTranslatedRouteByUrl($url, $attributes, $this->getCurrentLocale())
330
        ) {
331 104
            return $this->getUrlFromRouteName($locale, $translatedRoute, $attributes, $showHiddenLocale);
332
        }
333
334 212
        $baseUrl    = $this->request()->getBaseUrl();
335 212
        $parsedUrl  = parse_url($url);
336
337 212
        $translatedRoute = $this->routeTranslator->getTranslatedRoute(
338 212
            $baseUrl, $parsedUrl, $this->getDefaultLocale(), $this->getSupportedLocales()
339
        );
340
341 212
        if ($translatedRoute !== false)
342 208
            return $this->getUrlFromRouteName($locale, $translatedRoute, $attributes, $showHiddenLocale);
343
344 180
        if ( ! empty($locale)) {
345 112
            if ($locale !== $this->getDefaultLocale() || ! $this->isDefaultLocaleHiddenInUrl() || $showHiddenLocale) {
346 104
                $parsedUrl['path'] = $locale.'/'.ltrim($parsedUrl['path'], '/');
347
            }
348
        }
349
350 180
        $parsedUrl['path'] = ltrim(ltrim($baseUrl, '/') . '/' . $parsedUrl['path'], '/');
351 180
        $parsedUrl['path'] = rtrim($parsedUrl['path'], '/');
352
353 180
        $url = Url::unparse($parsedUrl);
354
355 180
        if (filter_var($url, FILTER_VALIDATE_URL)) return $url;
356
357 4
        return $this->createUrlFromUri(
358 4
            empty($url) ? $parsedUrl['path'] : $url
359
        );
360
    }
361
362
    /**
363
     * Create an url from the uri.
364
     *
365
     * @param  string  $uri
366
     *
367
     * @return string
368
     */
369 220
    public function createUrlFromUri($uri)
370
    {
371 220
        $uri = ltrim($uri, '/');
372
373 220
        return empty($this->baseUrl)
374
            ? $this->app[\Illuminate\Contracts\Routing\UrlGenerator::class]->to($uri)
375 220
            : $this->baseUrl.$uri;
376
    }
377
378
    /**
379
     * Get locales navigation bar.
380
     *
381
     * @return string
382
     */
383 4
    public function localesNavbar()
384
    {
385
        /** @var  \Illuminate\Contracts\View\Factory  $view */
386 2
        $view = $this->app[ViewFactoryContract::class];
387
388 4
        return $view->make('localization::navbar', ['supportedLocales' => $this->getSupportedLocales()])
389 4
                    ->render();
390
    }
391
392
    /* -----------------------------------------------------------------
393
     |  Translation Methods
394
     | -----------------------------------------------------------------
395
     */
396
397
    /**
398
     * Returns the translated route for an url and the attributes given and a locale
399
     *
400
     * @param  string  $url
401
     * @param  array   $attributes
402
     * @param  string  $locale
403
     *
404
     * @return string|false
405
     */
406 208
    private function findTranslatedRouteByUrl($url, $attributes, $locale)
407
    {
408 208
        if (empty($url))
409
            return false;
410
411
        // check if this url is a translated url
412 208
        foreach ($this->routeTranslator->getTranslatedRoutes() as $translatedRoute) {
413 208
            $translatedUrl = $this->getUrlFromRouteName($locale, $translatedRoute, $attributes);
414
415 208
            if ($this->getNonLocalizedURL($translatedUrl) === $this->getNonLocalizedURL($url))
0 ignored issues
show
Security Bug introduced by
It seems like $translatedUrl defined by $this->getUrlFromRouteNa...atedRoute, $attributes) on line 413 can also be of type false; however, Arcanedev\Localization\L...n::getNonLocalizedURL() does only seem to accept string|null, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
416 104
                return $translatedRoute;
417
        }
418
419 112
        return false;
420
    }
421
422
    /**
423
     * Returns an URL adapted to the route name and the locale given.
424
     *
425
     * @param  string|bool  $locale
426
     * @param  string       $transKey
427
     * @param  array        $attributes
428
     * @param  bool|false   $showHiddenLocale
429
     *
430
     * @return string|false
431
     */
432 220
    public function getUrlFromRouteName($locale, $transKey, array $attributes = [], $showHiddenLocale = false)
433
    {
434 220
        $this->isLocaleSupportedOrFail($locale);
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 432 can also be of type boolean; however, Arcanedev\Localization\L...LocaleSupportedOrFail() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
435
436 216
        $route = $this->routeTranslator->getUrlFromRouteName(
437 216
            $locale,
438 216
            $this->getDefaultLocale(),
439
            $transKey,
440
            $attributes,
441 216
            $this->isDefaultLocaleHiddenInUrl(),
442
            $showHiddenLocale
443
        );
444
445
        // This locale does not have any key for this route name
446 216
        if (empty($route)) return false;
447
448 216
        return rtrim($this->createUrlFromUri($route));
449
    }
450
451
    /**
452
     * Set route name from request.
453
     *
454
     * @param  \Illuminate\Http\Request  $request
455
     */
456 4
    public function setRouteNameFromRequest(Request $request)
457
    {
458 4
        $routeName = $this->routeTranslator->getRouteNameFromPath(
459 4
            $request->getUri(), $this->getCurrentLocale()
460
        );
461
462 4
        $this->routeTranslator->setCurrentRoute($routeName);
463 4
    }
464
465
    /* -----------------------------------------------------------------
466
     |  Check Methods
467
     | -----------------------------------------------------------------
468
     */
469
470
    /**
471
     * Hide the default locale in URL ??
472
     *
473
     * @return bool
474
     */
475 216
    public function isDefaultLocaleHiddenInUrl()
476
    {
477 216
        return $this->localesManager->isDefaultLocaleHiddenInUrl();
478
    }
479
480
    /**
481
     * Check if Locale exists on the supported locales collection.
482
     *
483
     * @param  string|bool  $locale
484
     *
485
     * @return bool
486
     */
487 224
    public function isLocaleSupported($locale)
488
    {
489 224
        return ! ($locale !== false && ! $this->localesManager->isSupportedLocale($locale));
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 487 can also be of type boolean; however, Arcanedev\Localization\C...er::isSupportedLocale() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
490
    }
491
492
    /**
493
     * Check if the locale is supported or fail if not.
494
     *
495
     * @param  string  $locale
496
     *
497
     * @throws \Arcanedev\Localization\Exceptions\UnsupportedLocaleException
498
     */
499 224
    private function isLocaleSupportedOrFail($locale): void
500
    {
501 224
        if ( ! $this->isLocaleSupported($locale))
502 4
            throw new UnsupportedLocaleException(
503 4
                "Locale '{$locale}' is not in the list of supported locales."
504
            );
505 220
    }
506
}
507