Completed
Push — master ( 0eceaa...aee3a6 )
by Ben
15:21 queued 05:47
created

Config::offsetExists()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3.1406

Importance

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