Completed
Pull Request — master (#100)
by ARCANEDEV
22:26 queued 05:58
created

Localization::createUrlFromUri()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 8
ccs 4
cts 5
cp 0.8
crap 2.032
rs 9.4285
c 0
b 0
f 0
1
<?php namespace Arcanedev\Localization;
2
3
use Arcanedev\Localization\Contracts\LocalesManager as LocalesManagerContract;
4
use Arcanedev\Localization\Contracts\Localization as LocalizationContract;
5
use Arcanedev\Localization\Contracts\RouteTranslator as RouteTranslatorContract;
6
use Arcanedev\Localization\Exceptions\UnsupportedLocaleException;
7
use Arcanedev\Localization\Utilities\Url;
8
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
9
use Illuminate\Contracts\View\Factory as ViewFactoryContract;
10
use Illuminate\Http\Request;
11
12
/**
13
 * Class     Localization
14
 *
15
 * @package  Arcanedev\Localization
16
 * @author   ARCANEDEV <[email protected]>
17
 */
18
class Localization implements LocalizationContract
19
{
20
    /* -----------------------------------------------------------------
21
     |  Properties
22
     | -----------------------------------------------------------------
23
     */
24
25
    /**
26
     * Base url.
27
     *
28
     * @var string
29
     */
30
    protected $baseUrl;
31
32
    /**
33
     * Laravel application instance.
34
     *
35
     * @var \Illuminate\Contracts\Foundation\Application
36
     */
37
    private $app;
38
39
    /**
40
     * The RouteTranslator instance.
41
     *
42
     * @var \Arcanedev\Localization\Contracts\RouteTranslator
43
     */
44
    protected $routeTranslator;
45
46
    /**
47
     * The LocalesManager instance.
48
     *
49
     * @var \Arcanedev\Localization\Contracts\LocalesManager
50
     */
51
    private $localesManager;
52
53
    /* -----------------------------------------------------------------
54
     |  Constructor
55
     | -----------------------------------------------------------------
56
     */
57
58
    /**
59
     * Localization constructor.
60
     *
61
     * @param  \Illuminate\Contracts\Foundation\Application       $app
62
     * @param  \Arcanedev\Localization\Contracts\RouteTranslator  $routeTranslator
63
     * @param  \Arcanedev\Localization\Contracts\LocalesManager   $localesManager
64
     */
65 378
    public function __construct(
66
        ApplicationContract     $app,
67
        RouteTranslatorContract $routeTranslator,
68
        LocalesManagerContract  $localesManager
69
    ) {
70 378
        $this->app             = $app;
71 378
        $this->routeTranslator = $routeTranslator;
72 378
        $this->localesManager  = $localesManager;
73
74 378
        $this->localesManager->setDefaultLocale(
75 378
            $this->app['config']->get('app.locale')
76
        );
77 378
    }
78
79
    /* -----------------------------------------------------------------
80
     |  Getters & Setters
81
     | -----------------------------------------------------------------
82
     */
83
84
    /**
85
     * Get Request instance.
86
     *
87
     * @return \Illuminate\Http\Request
88
     */
89 159
    private function request()
90
    {
91 159
        return $this->app['request'];
92
    }
93
94
    /**
95
     * Returns default locale.
96
     *
97
     * @return string
98
     */
99 174
    public function getDefaultLocale()
100
    {
101 174
        return $this->localesManager->getDefaultLocale();
102
    }
103
104
    /**
105
     * Return an array of all supported Locales.
106
     *
107
     * @return \Arcanedev\Localization\Entities\LocaleCollection
108
     */
109 162
    public function getSupportedLocales()
110
    {
111 162
        return $this->localesManager->getSupportedLocales();
112
    }
113
114
    /**
115
     * Set the supported locales.
116
     *
117
     * @param  array  $supportedLocales
118
     *
119
     * @return self
120
     */
121 6
    public function setSupportedLocales(array $supportedLocales)
122
    {
123 6
        $this->localesManager->setSupportedLocales($supportedLocales);
124
125 3
        return $this;
126
    }
127
128
    /**
129
     * Get supported locales keys.
130
     *
131
     * @return array
132
     */
133 3
    public function getSupportedLocalesKeys()
134
    {
135 3
        return $this->localesManager->getSupportedLocalesKeys();
136
    }
137
138
    /**
139
     * Returns current language.
140
     *
141
     * @return string
142
     */
143 165
    public function getCurrentLocale()
144
    {
145 165
        return $this->localesManager->getCurrentLocale();
146
    }
147
148
    /**
149
     * Returns current language.
150
     *
151
     * @return \Arcanedev\Localization\Entities\Locale
152
     */
153 15
    public function getCurrentLocaleEntity()
154
    {
155 15
        return $this->localesManager->getCurrentLocaleEntity();
156
    }
157
158
    /**
159
     * Returns current locale name.
160
     *
161
     * @return string
162
     */
163 3
    public function getCurrentLocaleName()
164
    {
165 3
        return $this->getCurrentLocaleEntity()->name();
166
    }
167
168
    /**
169
     * Returns current locale script.
170
     *
171
     * @return string
172
     */
173 3
    public function getCurrentLocaleScript()
174
    {
175 3
        return $this->getCurrentLocaleEntity()->script();
176
    }
177
178
    /**
179
     * Returns current locale direction.
180
     *
181
     * @return string
182
     */
183 3
    public function getCurrentLocaleDirection()
184
    {
185 3
        return $this->getCurrentLocaleEntity()->direction();
186
    }
187
188
    /**
189
     * Returns current locale native name.
190
     *
191
     * @return string
192
     */
193 3
    public function getCurrentLocaleNative()
194
    {
195 3
        return $this->getCurrentLocaleEntity()->native();
196
    }
197
198
    /**
199
     * Returns current locale regional.
200
     *
201
     * @return string
202
     */
203 3
    public function getCurrentLocaleRegional()
204
    {
205 3
        return $this->getCurrentLocaleEntity()->regional();
206
    }
207
208
    /**
209
     * Get all locales.
210
     *
211
     * @return \Arcanedev\Localization\Entities\LocaleCollection
212
     */
213 3
    public function getAllLocales()
214
    {
215 3
        return $this->localesManager->getAllLocales();
216
    }
217
218
    /**
219
     * Set and return current locale.
220
     *
221
     * @param  string|null  $locale
222
     *
223
     * @return string
224
     */
225 378
    public function setLocale($locale = null)
226
    {
227 378
        return $this->localesManager->setLocale($locale);
228
    }
229
230
    /**
231
     * Sets the base url for the site.
232
     *
233
     * @param  string  $url
234
     *
235
     * @return self
236
     */
237 378
    public function setBaseUrl($url)
238
    {
239 378
        if (substr($url, -1) !== '/') $url .= '/';
240
241 378
        $this->baseUrl = $url;
242
243 378
        return $this;
244
    }
245
246
    /* -----------------------------------------------------------------
247
     |  Main Methods
248
     | -----------------------------------------------------------------
249
     */
250
251
    /**
252
     * Translate routes and save them to the translated routes array (used in the localize route filter).
253
     *
254
     * @param  string  $routeName
255
     *
256
     * @return string
257
     */
258 378
    public function transRoute($routeName)
259
    {
260 378
        return $this->routeTranslator->trans($routeName);
261
    }
262
263
    /**
264
     * Returns an URL adapted to $locale or current locale.
265
     *
266
     * @param  string|null  $url
267
     * @param  string|null  $locale
268
     *
269
     * @return string
270
     */
271 6
    public function localizeURL($url = null, $locale = null)
272
    {
273 6
        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 273 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...
274
    }
275
276
    /**
277
     * It returns an URL without locale (if it has it).
278
     *
279
     * @param  string|null  $url
280
     *
281
     * @return string
282
     */
283 159
    public function getNonLocalizedURL($url = null)
284
    {
285 159
        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 285 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...
286
    }
287
288
    /**
289
     * Returns an URL adapted to $locale or current locale.
290
     *
291
     * @todo: Refactor this beast
292
     *
293
     * @param  string|null  $locale
294
     * @param  string|null  $url
295
     * @param  array        $attributes
296
     * @param  bool|false   $showHiddenLocale
297
     *
298
     * @return string|false
299
     */
300 159
    public function getLocalizedURL($locale = null, $url = null, array $attributes = [], $showHiddenLocale = false)
301
    {
302 159
        if (is_null($locale))
303 15
            $locale = $this->getCurrentLocale();
304
305 159
        $this->isLocaleSupportedOrFail($locale);
306
307 159
        if (empty($attributes))
308 159
            $attributes = Url::extractAttributes($url);
309
310 159
        if (empty($url)) {
311 15
            if ($this->routeTranslator->hasCurrentRoute()) {
312 3
                if (empty($attributes))
313 3
                    $attributes = $this->request()->route()->parameters();
314
315 3
                return $this->getUrlFromRouteName(
316
                    $locale,
317 3
                    $this->routeTranslator->getCurrentRoute(),
318
                    $attributes,
319 3
                    $showHiddenLocale
320
                );
321
            }
322
323 15
            $url = $this->request()->fullUrl();
324
        }
325
326
        if (
327 159
            $locale &&
328 158
            $translatedRoute = $this->findTranslatedRouteByUrl($url, $attributes, $this->getCurrentLocale())
329
        ) {
330 78
            return $this->getUrlFromRouteName($locale, $translatedRoute, $attributes, $showHiddenLocale);
331
        }
332
333 159
        $baseUrl    = $this->request()->getBaseUrl();
334 159
        $parsedUrl  = parse_url($url);
335
336 159
        $translatedRoute = $this->routeTranslator->getTranslatedRoute(
337 159
            $baseUrl, $parsedUrl, $this->getDefaultLocale(), $this->getSupportedLocales()
338
        );
339
340 159
        if ($translatedRoute !== false)
341 156
            return $this->getUrlFromRouteName($locale, $translatedRoute, $attributes, $showHiddenLocale);
342
343 135
        if ( ! empty($locale)) {
344 84
            if ($locale !== $this->getDefaultLocale() || ! $this->isDefaultLocaleHiddenInUrl() || $showHiddenLocale) {
345 78
                $parsedUrl['path'] = $locale.'/'.ltrim($parsedUrl['path'], '/');
346
            }
347
        }
348
349 135
        $parsedUrl['path'] = ltrim(ltrim($baseUrl, '/') . '/' . $parsedUrl['path'], '/');
350 135
        $parsedUrl['path'] = rtrim($parsedUrl['path'], '/');
351
352 135
        $url = Url::unparse($parsedUrl);
353
354 135
        if (filter_var($url, FILTER_VALIDATE_URL)) return $url;
355
356 3
        return $this->createUrlFromUri(
357 3
            empty($url) ? $parsedUrl['path'] : $url
358
        );
359
    }
360
361
    /**
362
     * Create an url from the uri.
363
     *
364
     * @param  string  $uri
365
     *
366
     * @return string
367
     */
368 165
    public function createUrlFromUri($uri)
369
    {
370 165
        $uri = ltrim($uri, '/');
371
372 165
        return empty($this->baseUrl)
373
            ? $this->app[\Illuminate\Contracts\Routing\UrlGenerator::class]->to($uri)
374 165
            : $this->baseUrl.$uri;
375
    }
376
377
    /**
378
     * Get locales navigation bar.
379
     *
380
     * @return string
381
     */
382 3
    public function localesNavbar()
383
    {
384
        /** @var  \Illuminate\Contracts\View\Factory  $view */
385 3
        $view = $this->app[ViewFactoryContract::class];
386
387 3
        return $view->make('localization::navbar', ['supportedLocales' => $this->getSupportedLocales()])
388 3
                    ->render();
389
    }
390
391
    /* -----------------------------------------------------------------
392
     |  Translation Methods
393
     | -----------------------------------------------------------------
394
     */
395
396
    /**
397
     * Returns the translated route for an url and the attributes given and a locale
398
     *
399
     * @param  string  $url
400
     * @param  array   $attributes
401
     * @param  string  $locale
402
     *
403
     * @return string|false
404
     */
405 156
    private function findTranslatedRouteByUrl($url, $attributes, $locale)
406
    {
407 156
        if (empty($url)) return false;
408
409
        // check if this url is a translated url
410 156
        foreach ($this->routeTranslator->getTranslatedRoutes() as $translatedRoute) {
411 156
            $translatedUrl = $this->getUrlFromRouteName($locale, $translatedRoute, $attributes);
412
413 156
            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 411 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...
414 130
                return $translatedRoute;
415
        }
416
417 84
        return false;
418
    }
419
420
    /**
421
     * Returns an URL adapted to the route name and the locale given.
422
     *
423
     * @param  string|bool  $locale
424
     * @param  string       $transKey
425
     * @param  array        $attributes
426
     * @param  bool|false   $showHiddenLocale
427
     *
428
     * @return string|false
429
     */
430 165
    public function getUrlFromRouteName($locale, $transKey, array $attributes = [], $showHiddenLocale = false)
431
    {
432 165
        $this->isLocaleSupportedOrFail($locale);
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 430 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...
433
434 162
        $route = $this->routeTranslator->getUrlFromRouteName(
435
            $locale,
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 430 can also be of type boolean; however, Arcanedev\Localization\C...::getUrlFromRouteName() 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...
436 162
            $this->getDefaultLocale(),
437
            $transKey,
438
            $attributes,
439 162
            $this->isDefaultLocaleHiddenInUrl(),
440 162
            $showHiddenLocale
441
        );
442
443
        // This locale does not have any key for this route name
444 162
        if (empty($route)) return false;
445
446 162
        return rtrim($this->createUrlFromUri($route));
447
    }
448
449
    /**
450
     * Set route name from request.
451
     *
452
     * @param  \Illuminate\Http\Request  $request
453
     */
454 3
    public function setRouteNameFromRequest(Request $request)
455
    {
456 3
        $routeName = $this->routeTranslator->getRouteNameFromPath(
457 3
            $request->getUri(), $this->getCurrentLocale()
458
        );
459
460 3
        $this->routeTranslator->setCurrentRoute($routeName);
461 3
    }
462
463
    /* -----------------------------------------------------------------
464
     |  Check Methods
465
     | -----------------------------------------------------------------
466
     */
467
468
    /**
469
     * Hide the default locale in URL ??
470
     *
471
     * @return bool
472
     */
473 162
    public function isDefaultLocaleHiddenInUrl()
474
    {
475 162
        return $this->localesManager->isDefaultLocaleHiddenInUrl();
476
    }
477
478
    /**
479
     * Check if Locale exists on the supported locales collection.
480
     *
481
     * @param  string|bool  $locale
482
     *
483
     * @return bool
484
     */
485 168
    public function isLocaleSupported($locale)
486
    {
487 168
        return ! ($locale !== false && ! $this->localesManager->isSupportedLocale($locale));
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 485 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...
488
    }
489
490
    /**
491
     * Check if the locale is supported or fail if not.
492
     *
493
     * @param  string  $locale
494
     *
495
     * @throws \Arcanedev\Localization\Exceptions\UnsupportedLocaleException
496
     */
497 168
    private function isLocaleSupportedOrFail($locale)
498
    {
499 168
        if ( ! $this->isLocaleSupported($locale))
500 3
            throw new UnsupportedLocaleException(
501 3
                "Locale '{$locale}' is not in the list of supported locales."
502
            );
503 165
    }
504
}
505