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
Push — master ( 194435...e98428 )
by Marc
01:23
created

LanguageNegotiator   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 5
Bugs 3 Features 0
Metric Value
wmc 30
c 5
b 3
f 0
lcom 1
cbo 1
dl 0
loc 170
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 25 5
C negotiateLanguage() 0 45 15
C getMatchesFromAcceptedLanguages() 0 49 10
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
     * @var bool
27
     */
28
    private $use_intl = false;
29
30
    /**
31
     * @param string  $defaultLocale
32
     * @param array   $supportedLanguages
33
     * @param Request $request
34
     */
35
    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...
36
    {
37
        $this->defaultLocale = $defaultLocale;
38
39
        if (class_exists('Locale')) {
40
            $this->use_intl = true;
41
42
            foreach ($supportedLanguages as $key => $supportedLanguage) {
43
                if ( ! isset($supportedLanguage['lang'])) {
44
                    $supportedLanguage['lang'] = Locale::canonicalize($key);
45
                } else {
46
                    $supportedLanguage['lang'] = Locale::canonicalize($supportedLanguage['lang']);
47
                }
48
                if (isset($supportedLanguage['regional'])) {
49
                    $supportedLanguage['regional'] = Locale::canonicalize($supportedLanguage['regional']);
50
                }
51
52
                $this->supportedLanguages[$key] = $supportedLanguage;
53
            }
54
        } else {
55
            $this->supportedLanguages = $supportedLanguages;
56
        }
57
58
        $this->request = $request;
59
    }
60
61
    /**
62
     * Negotiates language with the user's browser through the Accept-Language
63
     * HTTP header or the user's host address.  Language codes are generally in
64
     * the form "ll" for a language spoken in only one country, or "ll-CC" for a
65
     * language spoken in a particular country.  For example, U.S. English is
66
     * "en-US", while British English is "en-UK".  Portuguese as spoken in
67
     * Portugal is "pt-PT", while Brazilian Portuguese is "pt-BR".
68
     *
69
     * This function is based on negotiateLanguage from Pear HTTP2
70
     * http://pear.php.net/package/HTTP2/
71
     *
72
     * Quality factors in the Accept-Language: header are supported, e.g.:
73
     *      Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8
74
     *
75
     * @return string The negotiated language result or app.locale.
76
     */
77
    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...
78
    {
79
        $matches = $this->getMatchesFromAcceptedLanguages();
80
        foreach ($matches as $key => $q) {
81
            if (!empty($this->supportedLanguages[$key])) {
82
                return $key;
83
            }
84
85
            if ($this->use_intl) {
86
                $key = Locale::canonicalize($key);
87
            }
88
89
            // Search for acceptable locale by 'regional' => 'af_ZA' or 'lang' => 'af-ZA' match.
90
            foreach ( $this->supportedLanguages as $key_supported => $locale ) {
91
                if ( (isset($locale['regional']) && $locale['regional'] == $key) || (isset($locale['lang']) && $locale['lang'] == $key) ) {
92
                    return $key_supported;
93
                }
94
            }
95
        }
96
        // If any (i.e. "*") is acceptable, return the first supported format
97
        if (isset($matches['*'])) {
98
            reset($this->supportedLanguages);
99
100
            return key($this->supportedLanguages);
101
        }
102
103
        if ($this->use_intl && !empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
104
            $http_accept_language = Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']);
105
106
            if (!empty($this->supportedLanguages[$http_accept_language])) {
107
                return $http_accept_language;
108
            }
109
        }
110
111
        if ($this->request->server('REMOTE_HOST')) {
112
            $remote_host = explode('.', $this->request->server('REMOTE_HOST'));
113
            $lang = strtolower(end($remote_host));
114
115
            if (!empty($this->supportedLanguages[$lang])) {
116
                return $lang;
117
            }
118
        }
119
120
        return $this->defaultLocale;
121
    }
122
123
    /**
124
     * Return all the accepted languages from the browser.
125
     *
126
     * @return array Matches from the header field Accept-Languages
127
     */
128
    private function getMatchesFromAcceptedLanguages()
129
    {
130
        $matches = [];
131
132
        if ($acceptLanguages = $this->request->header('Accept-Language')) {
133
            $acceptLanguages = explode(',', $acceptLanguages);
134
135
            $generic_matches = [];
136
            foreach ($acceptLanguages as $option) {
137
                $option = array_map('trim', explode(';', $option));
138
                $l = $option[0];
139
                if (isset($option[1])) {
140
                    $q = (float) str_replace('q=', '', $option[1]);
141
                } else {
142
                    $q = null;
143
                    // Assign default low weight for generic values
144
                    if ($l == '*/*') {
145
                        $q = 0.01;
146
                    } elseif (substr($l, -1) == '*') {
147
                        $q = 0.02;
148
                    }
149
                }
150
                // Unweighted values, get high weight by their position in the
151
                // list
152
                $q = isset($q) ? $q : 1000 - count($matches);
153
                $matches[$l] = $q;
154
155
                //If for some reason the Accept-Language header only sends language with country
156
                //we should make the language without country an accepted option, with a value
157
                //less than it's parent.
158
                $l_ops = explode('-', $l);
159
                array_pop($l_ops);
160
                while (!empty($l_ops)) {
161
                    //The new generic option needs to be slightly less important than it's base
162
                    $q -= 0.001;
163
                    $op = implode('-', $l_ops);
164
                    if (empty($generic_matches[$op]) || $generic_matches[$op] > $q) {
165
                        $generic_matches[$op] = $q;
166
                    }
167
                    array_pop($l_ops);
168
                }
169
            }
170
            $matches = array_merge($generic_matches, $matches);
171
172
            arsort($matches, SORT_NUMERIC);
173
        }
174
175
        return $matches;
176
    }
177
}
178