GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#389)
by
unknown
01:43
created

LanguageNegotiator::negotiateLanguage()   C

Complexity

Conditions 12
Paths 35

Size

Total Lines 42
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 42
rs 5.1612
cc 12
eloc 21
nc 35
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Mcamara\LaravelLocalization;
4
5
use Illuminate\Http\Request;
6
use Locale;
7
8
class LanguageNegotiator
9
{
10
    /**
11
     * @var string
12
     */
13
    private $defaultLocale;
14
15
    /**
16
     * @var array
17
     */
18
    private $supportedLanguages;
19
20
    /**
21
     * @var Request
22
     */
23
    private $request;
24
25
    /**
26
     * @param string  $defaultLocale
27
     * @param array   $supportedLanguages
28
     * @param Request $request
29
     */
30
    public function __construct($defaultLocale, $supportedLanguages, Request $request)
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
31
    {
32
        $this->defaultLocale = $defaultLocale;
33
        $this->supportedLanguages = $supportedLanguages;
34
        $this->request = $request;
35
    }
36
37
    /**
38
     * Negotiates language with the user's browser through the Accept-Language
39
     * HTTP header or the user's host address.  Language codes are generally in
40
     * the form "ll" for a language spoken in only one country, or "ll-CC" for a
41
     * language spoken in a particular country.  For example, U.S. English is
42
     * "en-US", while British English is "en-UK".  Portuguese as spoken in
43
     * Portugal is "pt-PT", while Brazilian Portuguese is "pt-BR".
44
     *
45
     * This function is based on negotiateLanguage from Pear HTTP2
46
     * http://pear.php.net/package/HTTP2/
47
     *
48
     * Quality factors in the Accept-Language: header are supported, e.g.:
49
     *      Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8
50
     *
51
     * @return string The negotiated language result or app.locale.
52
     */
53
    public function negotiateLanguage()
0 ignored issues
show
Coding Style introduced by
negotiateLanguage uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
54
    {
55
        $matches = $this->getMatchesFromAcceptedLanguages();
56
        foreach ($matches as $key => $q) {
57
            if (!empty($this->supportedLanguages[$key])) {
58
                return $key;
59
            }
60
        }
61
        
62
        // Search for acceptable locale by 'regional' => 'af_ZA' or 'lang' => 'af-ZA' match.
63
        foreach ( $this->supportedLanguages as $key_supported => $locale ) {
64
            if ( $locale['regional'] == $key || $locale['lang'] == $key ) {
0 ignored issues
show
Bug introduced by
The variable $key seems to be defined by a foreach iteration on line 56. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
65
                return $key_supported;
66
            }
67
        }
68
        
69
        // If any (i.e. "*") is acceptable, return the first supported format
70
        if (isset($matches['*'])) {
71
            reset($this->supportedLanguages);
72
73
            return key($this->supportedLanguages);
74
        }
75
76
        if (class_exists('Locale') && !empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
77
            $http_accept_language = Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']);
78
79
            if (!empty($this->supportedLanguages[$http_accept_language])) {
80
                return $http_accept_language;
81
            }
82
        }
83
84
        if ($this->request->server('REMOTE_HOST')) {
85
            $remote_host = explode('.', $this->request->server('REMOTE_HOST'));
86
            $lang = strtolower(end($remote_host));
87
88
            if (!empty($this->supportedLanguages[$lang])) {
89
                return $lang;
90
            }
91
        }
92
93
        return $this->defaultLocale;
94
    }
95
96
    /**
97
     * Return all the accepted languages from the browser.
98
     *
99
     * @return array Matches from the header field Accept-Languages
100
     */
101
    private function getMatchesFromAcceptedLanguages()
102
    {
103
        $matches = [];
104
105
        if ($acceptLanguages = $this->request->header('Accept-Language')) {
106
            $acceptLanguages = explode(',', $acceptLanguages);
107
108
            $generic_matches = [];
109
            foreach ($acceptLanguages as $option) {
110
                $option = array_map('trim', explode(';', $option));
111
                $l = $option[0];
112
                if (isset($option[1])) {
113
                    $q = (float) str_replace('q=', '', $option[1]);
114
                } else {
115
                    $q = null;
116
                    // Assign default low weight for generic values
117
                    if ($l == '*/*') {
118
                        $q = 0.01;
119
                    } elseif (substr($l, -1) == '*') {
120
                        $q = 0.02;
121
                    }
122
                }
123
                // Unweighted values, get high weight by their position in the
124
                // list
125
                $q = isset($q) ? $q : 1000 - count($matches);
126
                $matches[$l] = $q;
127
128
                //If for some reason the Accept-Language header only sends language with country
129
                //we should make the language without country an accepted option, with a value
130
                //less than it's parent.
131
                $l_ops = explode('-', $l);
132
                array_pop($l_ops);
133
                while (!empty($l_ops)) {
134
                    //The new generic option needs to be slightly less important than it's base
135
                    $q -= 0.001;
136
                    $op = implode('-', $l_ops);
137
                    if (empty($generic_matches[$op]) || $generic_matches[$op] > $q) {
138
                        $generic_matches[$op] = $q;
139
                    }
140
                    array_pop($l_ops);
141
                }
142
            }
143
            $matches = array_merge($generic_matches, $matches);
144
145
            arsort($matches, SORT_NUMERIC);
146
        }
147
148
        return $matches;
149
    }
150
}
151