Config::offsetGet()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Thinktomorrow\Locale\Values;
4
5
use Thinktomorrow\Locale\Exceptions\InvalidConfig;
6
7
class Config implements \ArrayAccess
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
8
{
9
    /**
10
     * @var array
11
     */
12
    private $config;
13
14 117
    private function __construct(array $config)
15
    {
16 117
        $this->validate($config);
17
18 105
        $this->config = $this->sanitize($config);
19 105
    }
20
21 117
    public static function from(array $config)
22
    {
23 117
        return new static($config);
24
    }
25
26 98
    public function get($key, $default = null)
27
    {
28 98
        if (!isset($this->config[$key])) {
29 2
            return $default;
30
        }
31
32 96
        return $this->config[$key];
33
    }
34
35 1
    public function all(): array
36
    {
37 1
        return $this->config;
38
    }
39
40 105
    private function sanitize(array $config): array
41
    {
42
        // Sanitize locales
43 105
        $locales = $config['locales'];
44 105
        $locales = $this->convertSingleEntryToDefault($locales);
45 105
        $locales = $this->removeSlashes($locales);
46 105
        $locales = $this->removeTrailingDomainSlashes($locales);
47 105
        $config['locales'] = $locales;
48
49
        // Compute canonicals for all locales
50 105
        $config['canonicals'] = $this->computeCanonicals($config);
51
52 105
        return $config;
53
    }
54
55 105
    private function computeCanonicals(array $config): array
56
    {
57 105
        $canonicals = $config['canonicals'] ?? [];
58
59 105
        foreach ($config['locales'] as $rootKey => $locales) {
60
61
            // wildcard domains are not accepted as canonicals as we cannot know to which root this should resolve to.
62 105
            if (false !== strpos($rootKey, '*')) {
63 105
                continue;
64
            }
65
66 96
            foreach ($locales as $locale) {
67 96
                if (!isset($canonicals[$locale])) {
68 96
                    $canonicals[$locale] = $rootKey;
69
                }
70
            }
71
        }
72
73 105
        return $canonicals;
74
    }
75
76
    /**
77
     * @param array $locales
78
     *
79
     * @return array
80
     */
81 105
    private function removeSlashes(array $locales): array
82
    {
83 105
        foreach ($locales as $group => $segments) {
84 105
            foreach ($segments as $segment => $locale) {
85
                // remove slashes if any e.g. '/nl' will be sanitized to 'nl'
86 105
                if ($segment != '/' && false !== strpos($segment, '/')) {
87 1
                    $_segment = str_replace('/', '', $segment);
88
89 1
                    unset($locales[$group][$segment]);
90 1
                    $locales[$group][$_segment] = $locale;
91
                }
92
            }
93
        }
94
95 105
        return $locales;
96
    }
97
98
    /**
99
     * e.g. example.com/ will be sanitized to example.com.
100
     *
101
     * @param array $locales
102
     *
103
     * @return array
104
     */
105 105
    private function removeTrailingDomainSlashes(array $locales)
106
    {
107 105
        foreach ($locales as $scopeKey => $segments) {
108 105
            unset($locales[$scopeKey]);
109 105
            $locales[rtrim($scopeKey, '/')] = $segments;
110
        }
111
112 105
        return $locales;
113
    }
114
115
    /**
116
     * @param array $locales
117
     *
118
     * @return array
119
     */
120 105
    private function convertSingleEntryToDefault(array $locales): array
121
    {
122 105
        foreach ($locales as $group => $segments) {
123
            // If single locale is passed, it's considered the default for this group
124 105
            if (!is_array($segments)) {
125 105
                $locales[$group] = $segments = ['/' => $segments];
126
            }
127
        }
128
129 105
        return $locales;
130
    }
131
132
    /**
133
     * @param array $config
134
     */
135 117
    private function validate(array $config)
136
    {
137 117
        if (!isset($config['locales'])) {
138 2
            throw new InvalidConfig('Value [Locales] is missing for config structure.');
139
        }
140
141 115
        $locales = $config['locales'];
142
143 115
        if (!isset($locales['*'])) {
144 8
            throw new InvalidConfig('Default group [*] is missing for locales structure.');
145
        }
146
147 107
        if (is_array($locales['*']) && !isset($locales['*']['/'])) {
148 1
            throw new InvalidConfig('Group [default] is missing the default locale. e.g. ["/" => "en"]');
149
        }
150
151 106
        foreach ($locales as $group => $segments) {
152 106
            if (!is_string($group)) {
153
                throw new InvalidConfig('Invalid config structure for locales group ['.$group.']');
154
            }
155
        }
156
157 106
        $this->validateEachCanonicalLocaleExists($config);
158 105
    }
159
160
    /**
161
     * Each custom canonical entry should point to an existing locale.
162
     *
163
     * @param array $config
164
     */
165 106
    private function validateEachCanonicalLocaleExists(array $config)
166
    {
167 106
        $canonicals = $config['canonicals'] ?? [];
168 106
        foreach ($canonicals as $locale => $canonical) {
169 85
            if (!$this->existsAsLocale($config['locales'], $locale)) {
170 1
                throw new InvalidConfig('Canonical key '.$locale.' is not present as locale.');
171
            }
172
        }
173 105
    }
174
175 85
    private function existsAsLocale($existing_locales, $locale): bool
176
    {
177 85
        $flag = false;
178
179 85
        foreach ($existing_locales as $existing_locale) {
180 85
            if (is_array($existing_locale)) {
181 82
                if (true === $this->existsAsLocale($existing_locale, $locale)) {
182 82
                    $flag = true;
183 82
                    break;
184
                }
185
            }
186
187 85
            if ($existing_locale === $locale) {
188 84
                $flag = true;
189 84
                break;
190
            }
191
        }
192
193 85
        return $flag;
194
    }
195
196 73
    public function offsetExists($offset)
197
    {
198 73
        if (!is_string($offset) && !is_int($offset)) {
199
            return false;
200
        }
201
202 73
        return array_key_exists($offset, $this->config);
203
    }
204
205 72
    public function offsetGet($offset)
206
    {
207 72
        return $this->config[$offset];
208
    }
209
210 1
    public function offsetSet($offset, $value)
211
    {
212 1
        if (is_null($offset)) {
213
            $this->config[] = $value;
214
        } else {
215 1
            $this->config[$offset] = $value;
216
        }
217 1
    }
218
219 1
    public function offsetUnset($offset)
220
    {
221 1
        unset($this->config[$offset]);
222 1
    }
223
224 82
    public function toArray(): array
225
    {
226 82
        return $this->config;
227
    }
228
}
229