1
|
|
|
<?php |
2
|
|
|
declare(strict_types = 1); |
3
|
|
|
namespace Browscap\Data\Validator; |
4
|
|
|
|
5
|
|
|
use Assert\Assertion; |
6
|
|
|
|
7
|
|
|
class DivisionDataValidator implements ValidatorInterface |
8
|
|
|
{ |
9
|
|
|
/** |
10
|
|
|
* valdates the structure of a division |
11
|
|
|
* |
12
|
|
|
* @param array $divisionData Data to validate |
13
|
|
|
* @param string $filename |
14
|
|
|
* @param array &$allDivisions |
15
|
|
|
* @param bool $isCore |
16
|
|
|
*/ |
17
|
|
|
public function validate( |
18
|
|
|
array $divisionData, |
19
|
|
|
string $filename, |
20
|
|
|
array &$allDivisions = [], |
21
|
|
|
bool $isCore = false |
22
|
|
|
) : void { |
23
|
|
|
Assertion::keyExists($divisionData, 'division', 'required attibute "division" is missing in File ' . $filename); |
24
|
|
|
Assertion::string($divisionData['division'], 'required attibute "division" has to be a string in File ' . $filename); |
25
|
|
|
|
26
|
|
|
Assertion::keyExists($divisionData, 'sortIndex', 'required attibute "sortIndex" is missing in File ' . $filename); |
27
|
|
|
Assertion::integer($divisionData['sortIndex'], 'required attibute "sortIndex" has to be a integer in File ' . $filename); |
28
|
|
|
|
29
|
|
|
if (!$isCore) { |
30
|
|
|
Assertion::greaterThan($divisionData['sortIndex'], 0, 'required attibute "sortIndex" has to be a positive integer in File ' . $filename); |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
Assertion::keyExists($divisionData, 'lite', 'required attibute "lite" is missing in File ' . $filename); |
34
|
|
|
Assertion::boolean($divisionData['lite'], 'required attibute "lite" has to be an boolean in File ' . $filename); |
35
|
|
|
|
36
|
|
|
Assertion::keyExists($divisionData, 'standard', 'required attibute "standard" is missing in File ' . $filename); |
37
|
|
|
Assertion::boolean($divisionData['standard'], 'required attibute "standard" has to be an boolean in File ' . $filename); |
38
|
|
|
|
39
|
|
|
Assertion::keyExists($divisionData, 'userAgents', 'required attibute "userAgents" is missing in File ' . $filename); |
40
|
|
|
Assertion::isArray($divisionData['userAgents'], 'required attibute "userAgents" should be an array in File ' . $filename); |
41
|
|
|
Assertion::notEmpty($divisionData['userAgents'], 'required attibute "userAgents" should be an non-empty array in File ' . $filename); |
42
|
|
|
|
43
|
|
View Code Duplication |
if (isset($divisionData['versions']) && is_array($divisionData['versions'])) { |
|
|
|
|
44
|
|
|
$versions = $divisionData['versions']; |
45
|
|
|
} else { |
46
|
|
|
$versions = ['0.0']; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
foreach ($divisionData['userAgents'] as $index => $useragentData) { |
50
|
|
|
$this->validateUserAgentSection($useragentData, $versions, $allDivisions, $isCore, $filename, $index); |
51
|
|
|
|
52
|
|
|
$allDivisions[] = $useragentData['userAgent']; |
53
|
|
|
} |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @param array $useragentData |
58
|
|
|
* @param array $versions |
59
|
|
|
* @param array $allDivisions |
60
|
|
|
* @param bool $isCore |
61
|
|
|
* @param string $filename |
62
|
|
|
* @param int $index |
63
|
|
|
*/ |
64
|
|
|
private function validateUserAgentSection( |
65
|
|
|
array $useragentData, |
66
|
|
|
array $versions, |
67
|
|
|
array $allDivisions, |
68
|
|
|
bool $isCore, |
69
|
|
|
string $filename, |
70
|
|
|
int $index |
71
|
|
|
) : void { |
72
|
|
|
Assertion::keyExists($useragentData, 'userAgent', 'required attibute "userAgent" is missing in userAgents section ' . $index . ' in File ' . $filename); |
73
|
|
|
Assertion::string($useragentData['userAgent'], 'required attibute "userAgent" has to be a string in userAgents section ' . $index . ' in File ' . $filename); |
74
|
|
|
|
75
|
|
|
if (preg_match('/[\[\]]/', $useragentData['userAgent'])) { |
76
|
|
|
throw new \LogicException('required attibute "userAgent" includes invalid characters in userAgents section ' . $index . ' in File ' . $filename); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
if (false === mb_strpos($useragentData['userAgent'], '#') |
80
|
|
|
&& in_array($useragentData['userAgent'], $allDivisions) |
81
|
|
|
) { |
82
|
|
|
throw new \LogicException('Division "' . $useragentData['userAgent'] . '" is defined twice in file "' . $filename . '"'); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
View Code Duplication |
if ((false !== mb_strpos($useragentData['userAgent'], '#MAJORVER#') |
|
|
|
|
86
|
|
|
|| false !== mb_strpos($useragentData['userAgent'], '#MINORVER#')) |
87
|
|
|
&& ['0.0'] === $versions |
88
|
|
|
) { |
89
|
|
|
throw new \LogicException( |
90
|
|
|
'Division "' . $useragentData['userAgent'] |
91
|
|
|
. '" is defined with version placeholders, but no versions are set in file "' . $filename . '"' |
92
|
|
|
); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
View Code Duplication |
if (false === mb_strpos($useragentData['userAgent'], '#MAJORVER#') |
|
|
|
|
96
|
|
|
&& false === mb_strpos($useragentData['userAgent'], '#MINORVER#') |
97
|
|
|
&& ['0.0'] !== $versions |
98
|
|
|
&& 1 < count($versions) |
99
|
|
|
) { |
100
|
|
|
throw new \LogicException( |
101
|
|
|
'Division "' . $useragentData['userAgent'] |
102
|
|
|
. '" is defined without version placeholders, but there are versions set in file "' . $filename . '"' |
103
|
|
|
); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
Assertion::keyExists($useragentData, 'properties', 'required attibute "properties" is missing in userAgents section ' . $index . ' in File ' . $filename); |
107
|
|
|
Assertion::isArray($useragentData['properties'], 'required attibute "properties" should be an array in userAgents section ' . $index . ' in File ' . $filename); |
108
|
|
|
Assertion::notEmpty($useragentData['properties'], 'required attibute "properties" should be an non-empty array in userAgents section ' . $index . ' in File ' . $filename); |
109
|
|
|
|
110
|
|
|
if (!$isCore) { |
111
|
|
|
Assertion::keyExists($useragentData['properties'], 'Parent', 'the "Parent" property is missing for key "' . $useragentData['userAgent'] . '" in file "' . $filename . '"'); |
112
|
|
|
Assertion::same($useragentData['properties']['Parent'], 'DefaultProperties', 'the "Parent" property is not linked to the "DefaultProperties" for key "' . $useragentData['userAgent'] . '" in file "' . $filename . '"'); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
Assertion::keyExists($useragentData['properties'], 'Comment', 'the "Comment" property is missing for key "' . $useragentData['userAgent'] . '" in file "' . $filename . '"'); |
116
|
|
|
Assertion::string($useragentData['properties']['Comment'], 'the "Comment" property has to be a string for key "' . $useragentData['userAgent'] . '" in file "' . $filename . '"'); |
117
|
|
|
|
118
|
|
|
if (!array_key_exists('Version', $useragentData['properties']) && ['0.0'] !== $versions) { |
119
|
|
|
throw new \LogicException( |
120
|
|
|
'the "Version" property is missing for key "' . $useragentData['userAgent'] . '" in file "' . $filename |
121
|
|
|
. '", but there are defined versions' |
122
|
|
|
); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
if ($isCore) { |
126
|
|
|
return; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
if (array_key_exists('Version', $useragentData['properties'])) { |
130
|
|
|
Assertion::string($useragentData['properties']['Version'], 'the "Version" property has to be a string for key "' . $useragentData['userAgent'] . '" in file "' . $filename . '"'); |
131
|
|
|
|
132
|
|
View Code Duplication |
if ((false !== mb_strpos($useragentData['properties']['Version'], '#MAJORVER#') |
|
|
|
|
133
|
|
|
|| false !== mb_strpos($useragentData['properties']['Version'], '#MINORVER#')) |
134
|
|
|
&& ['0.0'] === $versions) { |
135
|
|
|
throw new \LogicException( |
136
|
|
|
'the "Version" property has version placeholders for key "' . $useragentData['userAgent'] . '" in file "' . $filename |
137
|
|
|
. '", but no versions are defined' |
138
|
|
|
); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
View Code Duplication |
if (false === mb_strpos($useragentData['properties']['Version'], '#MAJORVER#') |
|
|
|
|
142
|
|
|
&& false === mb_strpos($useragentData['properties']['Version'], '#MINORVER#') |
143
|
|
|
&& ['0.0'] !== $versions |
144
|
|
|
&& 1 < count($versions) |
145
|
|
|
) { |
146
|
|
|
throw new \LogicException( |
147
|
|
|
'the "Version" property has no version placeholders for key "' . $useragentData['userAgent'] . '" in file "' . $filename |
148
|
|
|
. '", but versions are defined' |
149
|
|
|
); |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
$this->checkPlatformProperties( |
154
|
|
|
$useragentData['properties'], |
155
|
|
|
'the properties array contains platform data for key "' . $useragentData['userAgent'] |
156
|
|
|
. '", please use the "platform" keyword' |
157
|
|
|
); |
158
|
|
|
|
159
|
|
|
$this->checkEngineProperties( |
160
|
|
|
$useragentData['properties'], |
161
|
|
|
'the properties array contains engine data for key "' . $useragentData['userAgent'] |
162
|
|
|
. '", please use the "engine" keyword' |
163
|
|
|
); |
164
|
|
|
|
165
|
|
|
$this->checkDeviceProperties( |
166
|
|
|
$useragentData['properties'], |
167
|
|
|
'the properties array contains device data for key "' . $useragentData['userAgent'] |
168
|
|
|
. '", please use the "device" keyword' |
169
|
|
|
); |
170
|
|
|
|
171
|
|
|
Assertion::keyExists($useragentData, 'children', 'required attibute "children" is missing in userAgents section ' . $index . ' in File ' . $filename); |
172
|
|
|
Assertion::isArray($useragentData['children'], 'required attibute "children" should be an array in userAgents section ' . $index . ' in File ' . $filename); |
173
|
|
|
Assertion::notEmpty($useragentData['children'], 'required attibute "children" should be an non-empty array in userAgents section ' . $index . ' in File ' . $filename); |
174
|
|
|
|
175
|
|
|
Assertion::keyNotExists($useragentData['children'], 'match', 'the children property shall not have the "match" entry for key "' . $useragentData['userAgent'] . '" in file "' . $filename . '"'); |
176
|
|
|
|
177
|
|
|
foreach ($useragentData['children'] as $child) { |
178
|
|
|
Assertion::isArray($child, 'each entry of the children property has to be an array for key "' . $useragentData['userAgent'] . '"'); |
179
|
|
|
|
180
|
|
|
$this->validateChildSection($child, $useragentData, $versions); |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* @param array $childData The children section to be validated |
186
|
|
|
* @param array $useragentData The complete UserAgent section which is the parent of the children section |
187
|
|
|
* @param array $versions The versions from the division |
188
|
|
|
* |
189
|
|
|
* @throws \LogicException |
190
|
|
|
*/ |
191
|
|
|
private function validateChildSection(array $childData, array $useragentData, array $versions) : void |
192
|
|
|
{ |
193
|
|
|
if (array_key_exists('device', $childData) && array_key_exists('devices', $childData)) { |
194
|
|
|
throw new \LogicException( |
195
|
|
|
'a child entry may not define both the "device" and the "devices" entries for key "' |
196
|
|
|
. $useragentData['userAgent'] . '"' |
197
|
|
|
); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
View Code Duplication |
if (array_key_exists('devices', $childData)) { |
|
|
|
|
201
|
|
|
Assertion::isArray($childData['devices'], 'the "devices" entry for key "' . $useragentData['userAgent'] . '" has to be an array'); |
202
|
|
|
|
203
|
|
|
if (1 < count($childData['devices']) |
204
|
|
|
&& false === mb_strpos($childData['match'], '#DEVICE#') |
205
|
|
|
) { |
206
|
|
|
throw new \LogicException( |
207
|
|
|
'the "devices" entry contains multiple devices but there is no #DEVICE# token for key "' |
208
|
|
|
. $useragentData['userAgent'] . '"' |
209
|
|
|
); |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
if (array_key_exists('device', $childData)) { |
214
|
|
|
Assertion::string($childData['device'], 'the "device" entry has to be a string for key "' . $useragentData['userAgent'] . '"'); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
Assertion::keyExists($childData, 'match', 'each entry of the children property requires an "match" entry for key "' . $useragentData['userAgent'] . '"'); |
218
|
|
|
Assertion::string($childData['match'], 'the "match" entry for key "' . $useragentData['userAgent'] . '" has to be a string'); |
219
|
|
|
|
220
|
|
|
if (preg_match('/[\[\]]/', $childData['match'])) { |
221
|
|
|
throw new \LogicException('key "' . $childData['match'] . '" includes invalid characters'); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
Assertion::notSame($childData['match'], $useragentData['userAgent'], 'the "match" entry is identical to its parents "userAgent" entry'); |
225
|
|
|
|
226
|
|
|
if (false !== mb_strpos($childData['match'], '#PLATFORM#') |
227
|
|
|
&& !array_key_exists('platforms', $childData) |
228
|
|
|
) { |
229
|
|
|
throw new \LogicException( |
230
|
|
|
'the key "' . $childData['match'] |
231
|
|
|
. '" is defined with platform placeholder, but no platforms are assigned' |
232
|
|
|
); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
View Code Duplication |
if (array_key_exists('platforms', $childData)) { |
|
|
|
|
236
|
|
|
Assertion::isArray($childData['platforms'], 'the "platforms" entry for key "' . $useragentData['userAgent'] . '" has to be an array'); |
237
|
|
|
|
238
|
|
|
if (1 < count($childData['platforms']) |
239
|
|
|
&& false === mb_strpos($childData['match'], '#PLATFORM#') |
240
|
|
|
) { |
241
|
|
|
throw new \LogicException( |
242
|
|
|
'the "platforms" entry contains multiple platforms but there is no #PLATFORM# token for key "' |
243
|
|
|
. $useragentData['userAgent'] . '"' |
244
|
|
|
); |
245
|
|
|
} |
246
|
|
|
} |
247
|
|
|
|
248
|
|
View Code Duplication |
if ((false !== mb_strpos($childData['match'], '#MAJORVER#') |
|
|
|
|
249
|
|
|
|| false !== mb_strpos($childData['match'], '#MINORVER#')) |
250
|
|
|
&& ['0.0'] === $versions |
251
|
|
|
) { |
252
|
|
|
throw new \LogicException( |
253
|
|
|
'the key "' . $childData['match'] |
254
|
|
|
. '" is defined with version placeholders, but no versions are set' |
255
|
|
|
); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
if (false === mb_strpos($childData['match'], '#MAJORVER#') |
259
|
|
|
&& false === mb_strpos($childData['match'], '#MINORVER#') |
260
|
|
|
&& ['0.0'] !== $versions |
261
|
|
|
&& 1 < count($versions) |
262
|
|
|
) { |
263
|
|
|
if (!array_key_exists('platforms', $childData)) { |
264
|
|
|
throw new \LogicException( |
265
|
|
|
'the key "' . $childData['match'] |
266
|
|
|
. '" is defined without version placeholders, but there are versions set' |
267
|
|
|
); |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
$dynamicPlatforms = false; |
271
|
|
|
|
272
|
|
|
foreach ($childData['platforms'] as $platform) { |
273
|
|
|
if (false !== mb_stripos($platform, 'dynamic')) { |
274
|
|
|
$dynamicPlatforms = true; |
275
|
|
|
|
276
|
|
|
break; |
277
|
|
|
} |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
if (!$dynamicPlatforms) { |
281
|
|
|
throw new \LogicException( |
282
|
|
|
'the key "' . $childData['match'] |
283
|
|
|
. '" is defined without version placeholders, but there are versions set' |
284
|
|
|
); |
285
|
|
|
} |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
if (false !== mb_strpos($childData['match'], '#DEVICE#') |
289
|
|
|
&& !array_key_exists('devices', $childData) |
290
|
|
|
) { |
291
|
|
|
throw new \LogicException( |
292
|
|
|
'the key "' . $childData['match'] |
293
|
|
|
. '" is defined with device placeholder, but no devices are assigned' |
294
|
|
|
); |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
if (array_key_exists('properties', $childData)) { |
298
|
|
|
Assertion::isArray($childData['properties'], 'the "properties" entry for key "' . $childData['match'] . '" has to be an array'); |
299
|
|
|
Assertion::keyNotExists($childData['properties'], 'Parent', 'the Parent property must not set inside the children array for key "' . $childData['match'] . '"'); |
300
|
|
|
|
301
|
|
|
if (array_key_exists('Version', $childData['properties']) |
302
|
|
|
&& array_key_exists('properties', $useragentData) |
303
|
|
|
&& array_key_exists('Version', $useragentData['properties']) |
304
|
|
|
&& $useragentData['properties']['Version'] === $childData['properties']['Version'] |
305
|
|
|
) { |
306
|
|
|
throw new \LogicException( |
307
|
|
|
'the "Version" property is set for key "' . $childData['match'] |
308
|
|
|
. '", but was already set for its parent "' . $useragentData['userAgent'] . '" with the same value' |
309
|
|
|
); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
$this->checkPlatformProperties( |
313
|
|
|
$childData['properties'], |
314
|
|
|
'the properties array contains platform data for key "' . $childData['match'] |
315
|
|
|
. '", please use the "platforms" keyword' |
316
|
|
|
); |
317
|
|
|
|
318
|
|
|
$this->checkEngineProperties( |
319
|
|
|
$childData['properties'], |
320
|
|
|
'the properties array contains engine data for key "' . $childData['match'] |
321
|
|
|
. '", please use the "engine" keyword' |
322
|
|
|
); |
323
|
|
|
|
324
|
|
|
$this->checkDeviceProperties( |
325
|
|
|
$childData['properties'], |
326
|
|
|
'the properties array contains device data for key "' . $childData['match'] |
327
|
|
|
. '", please use the "device" or the "devices" keyword' |
328
|
|
|
); |
329
|
|
|
} |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* checks if platform properties are set inside a properties array |
334
|
|
|
* |
335
|
|
|
* @param array $properties |
336
|
|
|
* @param string $message |
337
|
|
|
* |
338
|
|
|
* @throws \LogicException |
339
|
|
|
*/ |
340
|
|
|
private function checkPlatformProperties(array $properties, string $message) : void |
341
|
|
|
{ |
342
|
|
|
if (array_key_exists('Platform', $properties) |
343
|
|
|
|| array_key_exists('Platform_Description', $properties) |
344
|
|
|
|| array_key_exists('Platform_Maker', $properties) |
345
|
|
|
|| array_key_exists('Platform_Bits', $properties) |
346
|
|
|
|| array_key_exists('Platform_Version', $properties) |
347
|
|
|
|| array_key_exists('Win16', $properties) |
348
|
|
|
|| array_key_exists('Win32', $properties) |
349
|
|
|
|| array_key_exists('Win64', $properties) |
350
|
|
|
|| array_key_exists('Browser_Bits', $properties) |
351
|
|
|
) { |
352
|
|
|
throw new \LogicException($message); |
353
|
|
|
} |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* checks if platform properties are set inside a properties array |
358
|
|
|
* |
359
|
|
|
* @param array $properties |
360
|
|
|
* @param string $message |
361
|
|
|
* |
362
|
|
|
* @throws \LogicException |
363
|
|
|
*/ |
364
|
|
|
public function checkEngineProperties(array $properties, string $message) : void |
365
|
|
|
{ |
366
|
|
|
if (array_key_exists('RenderingEngine_Name', $properties) |
367
|
|
|
|| array_key_exists('RenderingEngine_Version', $properties) |
368
|
|
|
|| array_key_exists('RenderingEngine_Description', $properties) |
369
|
|
|
|| array_key_exists('RenderingEngine_Maker', $properties) |
370
|
|
|
|| array_key_exists('VBScript', $properties) |
371
|
|
|
|| array_key_exists('ActiveXControls', $properties) |
372
|
|
|
|| array_key_exists('BackgroundSounds', $properties) |
373
|
|
|
) { |
374
|
|
|
throw new \LogicException($message); |
375
|
|
|
} |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* checks if device properties are set inside a properties array |
380
|
|
|
* |
381
|
|
|
* @param array $properties |
382
|
|
|
* @param string $message |
383
|
|
|
* |
384
|
|
|
* @throws \LogicException |
385
|
|
|
*/ |
386
|
|
|
public function checkDeviceProperties(array $properties, string $message) : void |
387
|
|
|
{ |
388
|
|
|
if (array_key_exists('Device_Name', $properties) |
389
|
|
|
|| array_key_exists('Device_Maker', $properties) |
390
|
|
|
|| array_key_exists('Device_Type', $properties) |
391
|
|
|
|| array_key_exists('Device_Pointing_Method', $properties) |
392
|
|
|
|| array_key_exists('Device_Code_Name', $properties) |
393
|
|
|
|| array_key_exists('Device_Brand_Name', $properties) |
394
|
|
|
|| array_key_exists('isMobileDevice', $properties) |
395
|
|
|
|| array_key_exists('isTablet', $properties) |
396
|
|
|
) { |
397
|
|
|
throw new \LogicException($message); |
398
|
|
|
} |
399
|
|
|
} |
400
|
|
|
} |
401
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.