Completed
Push — master ( 6de04a...8f6f83 )
by Andrii
05:12
created

Module::getLanguages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
ccs 0
cts 6
cp 0
rs 9.4285
cc 2
eloc 5
nc 2
nop 0
crap 6
1
<?php
2
3
/*
4
 * Yii2 module for language switching
5
 *
6
 * @link      https://github.com/hiqdev/yii2-language
7
 * @package   yii2-language
8
 * @license   BSD-3-Clause
9
 * @copyright Copyright (c) 2016, HiQDev (http://hiqdev.com/)
10
 */
11
12
namespace hiqdev\yii2\language;
13
14
use Yii;
15
use yii\base\BootstrapInterface;
16
use yii\web\Cookie;
17
18
/**
19
 * Language Module.
20
 *
21
 * Example application configuration:
22
 *
23
 * ```php
24
 * 'modules' => [
25
 *     'language' => [
26
 *         'class' => \hiqdev\yii2\language\Module::class,
27
 *         'languages' => [
28
 *             'en' => 'English',
29
 *             'ru' => 'Русский',
30
 *         ],
31
 *     ],
32
 * ],
33
 * ```
34
 */
35
class Module extends \yii\base\Module implements BootstrapInterface
36
{
37
    /**
38
     * @var array list of available language codes. More specific patterns should come first, e.g. 'en_us' before 'en'.
39
     * Key is a language code, value is a name of language. For example:
40
     *
41
     * ```php
42
     * [
43
     *     'ru' => 'Русский',
44
     *     'en' => 'English',
45
     * ]
46
     * ```
47
     */
48
    protected $_languages = [];
49
50
    public function setLanguages(array $list)
51
    {
52
        $this->_languages = $list;
53
    }
54
55
    public function getLanguages()
56
    {
57
        if (!$this->_languages) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_languages of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
58
            $this->_languages = [
59
                Yii::$app->language => Yii::$app->language,
60
            ];
61
        }
62
63
        return $this->_languages;
64
    }
65
66
    /**
67
     * @var string Name of the cookie.
68
     */
69
    public $cookieName = 'language';
70
71
    /**
72
     * @var integer expiration date of the cookie storing the language of the site.
73
     */
74
    public $expireDays = 30;
75
76
    public function bootstrap($app)
77
    {
78
        $this->initLanguage();
79
    }
80
81
    /**
82
     * Saving language into cookie and database.
83
     * @param string $language - The language to save.
84
     * @return static
85
     */
86
    public function saveLanguage($language)
87
    {
88
        $this->applyLanguage($language);
89
        $this->saveLanguageIntoCookie($language);
90
    }
91
92
    private function initLanguage()
93
    {
94
        if ($this->isValidLanguage(Yii::$app->request->cookies->getValue($this->cookieName))) {
95
            $this->applyLanguage(Yii::$app->request->cookies->getValue($this->cookieName));
96
        } else {
97
            Yii::$app->response->cookies->remove($this->cookieName);
98
99
            if (($language = $this->detectLanguage()) !== false) {
100
                $this->saveLanguage($language);
101
            }
102
        }
103
    }
104
105
    /**
106
     * Determine language based on UserAgent.
107
     */
108
    public function detectLanguage()
109
    {
110
        $acceptableLanguages = Yii::$app->getRequest()->getAcceptableLanguages();
111
        foreach ($acceptableLanguages as $language) {
112
            if ($this->isValidLanguage($language)) {
113
                return $language;
114
            }
115
        }
116
        foreach ($acceptableLanguages as $language) {
117
            $pattern = preg_quote(substr($language, 0, 2), '/');
118
            foreach ($this->languages as $key => $value) {
0 ignored issues
show
Bug introduced by
The property languages does not seem to exist. Did you mean _languages?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
119
                if (preg_match('/^' . $pattern . '/', $value) || preg_match('/^' . $pattern . '/', $key)) {
120
                    return $this->isValidLanguage($key) ? $key : $value;
121
                }
122
            }
123
        }
124
125
        return false;
126
    }
127
128
    /**
129
     * Save language into cookie.
130
     * @param string $language
131
     */
132
    private function saveLanguageIntoCookie($language)
133
    {
134
        $cookie = new Cookie([
135
            'name' => $this->cookieName,
136
            'value' => $language,
137
            'expire' => time() + 86400 * $this->expireDays,
138
        ]);
139
        Yii::$app->response->cookies->add($cookie);
140
    }
141
142
    /**
143
     * Determines whether the language received as a parameter can be processed.
144
     * @param string $language
145
     * @return boolean
146
     */
147
    public function isValidLanguage($language)
148
    {
149
        return is_string($language) && isset($this->languages[$language]);
0 ignored issues
show
Bug introduced by
The property languages does not seem to exist. Did you mean _languages?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
150
    }
151
152
    /**
153
     * @param $language
154
     * @return bool whether the language is correct and saved
155
     */
156
    public function setLanguage($language)
157
    {
158
        if ($this->isValidLanguage($language)) {
159
            $this->saveLanguage($language);
160
161
            return true;
162
        }
163
164
        return false;
165
    }
166
167
    /**
168
     * @param string $language
169
     */
170
    private function applyLanguage($language)
171
    {
172
        Yii::$app->language = $language;
173
        Yii::$app->getFormatter()->locale = $language;
174
    }
175
176
    public static $MODULE_ID = 'language';
177
178 1
    public static function getInstance()
179
    {
180 1
        return Yii::$app->getModule(static::$MODULE_ID);
181
    }
182
}
183