LocalesManager::__construct()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 4
nop 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
namespace Charcoal\Translator;
4
5
use InvalidArgumentException;
6
7
/**
8
 * Locales Manager
9
 *
10
 * The manager handles the collection of available languages, their definitions,
11
 * the default language, and tracks the current language.
12
 */
13
class LocalesManager
14
{
15
    /**
16
     * Default sorting priority for locales.
17
     *
18
     * @const integer
19
     */
20
    const DEFAULT_SORT_PRIORITY = 10;
21
22
    /**
23
     * Dictionary of language definitions.
24
     *
25
     * @var array
26
     */
27
    private $locales;
28
29
    /**
30
     * List of language codes.
31
     *
32
     * @var string[]
33
     */
34
    private $languages;
35
36
    /**
37
     * Language code for the default locale.
38
     *
39
     * @var string
40
     */
41
    private $defaultLanguage;
42
43
    /**
44
     * Language code for the current locale.
45
     *
46
     * @var string|null
47
     */
48
    private $currentLanguage;
49
50
    /**
51
     * Create the Locales Manager with locales and optional options.
52
     *
53
     * Required parameters:
54
     * - **locales** (`array`)
55
     *
56
     * Optional parameters:
57
     * - **default_language** (`string`)
58
     *   - If none is set, the first language (from _locales_) will be used.
59
     * - **current_language** (`string`)
60
     *   - If none is set, then the default language will be used.
61
     *
62
     * @param  array $data Constructor dependencies.
63
     * @throws InvalidArgumentException If the default language is not a valid language.
64
     */
65
    public function __construct(array $data)
66
    {
67
        $this->setLocales($data['locales']);
68
69
        $default = isset($data['default_language']) ? $data['default_language'] : null;
70
        $this->setDefaultLocale($default);
71
72
        $current = isset($data['current_language']) ? $data['current_language'] : null;
73
        $this->setCurrentLocale($current);
74
    }
75
76
    /**
77
     * Retrieve the available locales information.
78
     *
79
     * @return array
80
     */
81
    public function locales()
82
    {
83
        return $this->locales;
84
    }
85
86
    /**
87
     * Retrieve the available locales (language codes).
88
     *
89
     * @return string[]
90
     */
91
    public function availableLocales()
92
    {
93
        return $this->languages;
94
    }
95
96
    /**
97
     * Set the default language.
98
     *
99
     * @param  string|null $lang The default language code.
100
     *    If NULL, the first language is assigned.
101
     * @throws InvalidArgumentException If the language is invalid.
102
     * @return void
103
     */
104
    private function setDefaultLocale($lang)
105
    {
106
        if ($lang === null) {
107
            $this->defaultLanguage = $this->languages[0];
108
            return;
109
        }
110
111
        if (!$this->hasLocale($lang)) {
112
            if (!is_string($lang)) {
0 ignored issues
show
introduced by
The condition is_string($lang) is always true.
Loading history...
113
                $lang = is_object($lang) ? get_class($lang) : gettype($lang);
114
            }
115
116
            throw new InvalidArgumentException(sprintf(
117
                'Unsupported default language; must be one of "%s", received "%s"',
118
                implode(', ', $this->availableLocales()),
119
                $lang
120
            ));
121
        }
122
123
        $this->defaultLanguage = $lang;
124
    }
125
126
    /**
127
     * Retrieve the default language.
128
     *
129
     * @return string
130
     */
131
    public function defaultLocale()
132
    {
133
        return $this->defaultLanguage;
134
    }
135
136
    /**
137
     * Set the current language.
138
     *
139
     * @param  string|null $lang The current language code.
140
     *    If NULL, the current language is unset.
141
     * @throws InvalidArgumentException If the language is invalid.
142
     * @return void
143
     */
144
    public function setCurrentLocale($lang)
145
    {
146
        if ($lang === null) {
147
            $this->currentLanguage = null;
148
            return;
149
        }
150
151
        if (!$this->hasLocale($lang)) {
152
            if (!is_string($lang)) {
0 ignored issues
show
introduced by
The condition is_string($lang) is always true.
Loading history...
153
                $lang = is_object($lang) ? get_class($lang) : gettype($lang);
154
            }
155
156
            throw new InvalidArgumentException(sprintf(
157
                'Unsupported language; must be one of "%s", received "%s"',
158
                implode(', ', $this->availableLocales()),
159
                $lang
160
            ));
161
        }
162
163
        $this->currentLanguage = $lang;
164
    }
165
166
    /**
167
     * Retrieve the current language.
168
     *
169
     * @return string
170
     */
171
    public function currentLocale()
172
    {
173
        if ($this->currentLanguage === null) {
174
            return $this->defaultLanguage;
175
        }
176
        return $this->currentLanguage;
177
    }
178
179
    /**
180
     * Determine if a locale is available.
181
     *
182
     * @param  string $lang The language code to check.
183
     * @return boolean
184
     */
185
    public function hasLocale($lang)
186
    {
187
        return isset($this->locales[$lang]);
188
    }
189
190
    /**
191
     * Set the available languages.
192
     *
193
     * Ensure that explicitely inactive locales are excluded and that the required
194
     * values are set on the locales configuration structure.
195
     *
196
     * This method is only called from the constructor.
197
     *
198
     * @param  array $locales The locales configuration structure.
199
     * @throws InvalidArgumentException If there are no active locales.
200
     * @return void
201
     */
202
    private function setLocales(array $locales)
203
    {
204
        $locales = $this->filterLocales($locales);
205
        uasort($locales, [ $this, 'sortLocalesByPriority' ]);
206
207
        $this->locales   = [];
208
        $this->languages = [];
209
        foreach ($locales as $langCode => $locale) {
210
            $this->locales[$langCode] = $locale;
211
            $this->languages[] = $langCode;
212
        }
213
214
        if (empty($this->locales)) {
215
            throw new InvalidArgumentException(
216
                'Locales can not be empty.'
217
            );
218
        }
219
    }
220
221
    /**
222
     * Filter the available languages.
223
     *
224
     * Routine:
225
     * 1. Removes disabled languages
226
     * 2. Assigns a priority, if absent
227
     * 3. Assigns a locale, if absent
228
     *
229
     * @param  array $locales The locales configuration structure.
230
     * @return array The parsed language structures.
231
     */
232
    private function filterLocales(array $locales)
233
    {
234
        $z = self::DEFAULT_SORT_PRIORITY;
235
236
        $filteredLocales = [];
237
        foreach ($locales as $langCode => $locale) {
238
            if (isset($locale['active']) && !$locale['active']) {
239
                continue;
240
            }
241
242
            if (!isset($locale['priority'])) {
243
                $locale['priority'] = $z++;
244
            }
245
246
            if (!isset($locale['locale'])) {
247
                $locale['locale'] = $langCode;
248
            }
249
250
            $filteredLocales[$langCode] = $locale;
251
        }
252
253
        return $filteredLocales;
254
    }
255
256
    /**
257
     * To be called with {@see uasort()}.
258
     *
259
     * @param  array $a Sortable action A.
260
     * @param  array $b Sortable action B.
261
     * @return integer
262
     */
263
    private function sortLocalesByPriority(array $a, array $b)
264
    {
265
        $a = isset($a['priority']) ? $a['priority'] : 0;
266
        $b = isset($b['priority']) ? $b['priority'] : 0;
267
268
        if ($a === $b) {
269
            return 0;
270
        }
271
        return ($a < $b) ? (-1) : 1;
272
    }
273
}
274