1
|
|
|
<?php |
2
|
|
|
declare(strict_types = 1); |
3
|
|
|
namespace hexydec\agentzero; |
4
|
|
|
|
5
|
|
|
class browsers { |
6
|
|
|
|
7
|
88 |
|
protected static function getBrowser(string $value, int $i, array $tokens, string $key, array $config = []) : array { |
8
|
88 |
|
if (!\str_contains($value, 'Opera Mini') && ($browser = \mb_strrchr($value, ' ')) !== false) { |
9
|
2 |
|
$value = \ltrim($browser); |
10
|
|
|
} |
11
|
88 |
|
$parts = \explode('/', $value); // split more incase there are more slashes |
12
|
88 |
|
$map = [ |
13
|
88 |
|
'opr' => 'Opera', |
14
|
88 |
|
'crios' => 'Chrome', |
15
|
88 |
|
'edg' => 'Edge', |
16
|
88 |
|
'edgios' => 'Edge', |
17
|
88 |
|
'fxios' => 'Firefox', |
18
|
88 |
|
'edga' => 'Edge', |
19
|
88 |
|
'webpositive' => 'WebPositive', |
20
|
88 |
|
'nintendobrowser' => 'NintendoBrowser', |
21
|
88 |
|
'up.browser' => 'UP.Browser', |
22
|
88 |
|
'ucbrowser' => 'UCBrowser', |
23
|
88 |
|
'netfront' => 'NetFront', |
24
|
88 |
|
'seamonkey' => 'SeaMonkey', |
25
|
88 |
|
'icecat' => 'IceCat', |
26
|
88 |
|
'iceape' => 'IceApe', |
27
|
88 |
|
'iceweasel' => 'IceWeasel', |
28
|
88 |
|
'bonecho' => 'BonEcho', |
29
|
88 |
|
'palemoon' => 'PaleMoon', |
30
|
88 |
|
'k-meleon' => 'K-Meleon', |
31
|
88 |
|
'samsungbrowser' => 'Samsung Internet', |
32
|
88 |
|
'huaweibrowser' => 'Huawei Browser', |
33
|
88 |
|
'qqbrowser' => 'QQ Browser', |
34
|
88 |
|
'miuibrowser' => 'MIUI Browser', |
35
|
88 |
|
'vivobrowser' => 'Vivo Browser', |
36
|
88 |
|
'android-com.vivo.browser' => 'Vivo Browser' |
37
|
88 |
|
]; |
38
|
88 |
|
$data = ['type' => 'human']; |
39
|
88 |
|
$browser = \mb_strtolower(\array_shift($parts)); |
40
|
88 |
|
$data['browser'] = $map[$browser] ?? \mb_convert_case($browser, MB_CASE_TITLE); |
41
|
|
|
|
42
|
|
|
// get browser version |
43
|
88 |
|
foreach ($parts AS $item) { |
44
|
88 |
|
if (\strpbrk($item, '1234567890') !== false) { |
45
|
88 |
|
$data['browserversion'] = $item; |
46
|
88 |
|
break; |
47
|
|
|
} |
48
|
|
|
} |
49
|
88 |
|
$map = [ |
50
|
88 |
|
'Samsung Internet' => 'samsung', |
51
|
88 |
|
'Huawei Browser' => 'huawei', |
52
|
88 |
|
'K-Meleon' => 'kmeleon', |
53
|
88 |
|
'Fennec' => 'firefox', |
54
|
88 |
|
'Native Opera Mini' => 'opera', |
55
|
88 |
|
'Cronet' => 'chrome' |
56
|
88 |
|
]; |
57
|
88 |
|
$key = $map[$data['browser']] ?? \mb_strtolower($data['browser']); |
58
|
88 |
|
return \array_merge( |
59
|
88 |
|
$data, |
60
|
88 |
|
isset($data['browserversion']) ? versions::get($key, $data['browserversion'], $config) : [] |
61
|
88 |
|
); |
62
|
|
|
} |
63
|
|
|
/** |
64
|
|
|
* Generates a configuration array for matching browsers |
65
|
|
|
* |
66
|
|
|
* @return array<string,props> An array with keys representing the string to match, and values a props object defining how to generate the match and which properties to set |
67
|
|
|
*/ |
68
|
63 |
|
public static function get() : array { |
69
|
49 |
|
$fn = [ |
70
|
49 |
|
'browserslash' => fn (string $value, int $i, array $tokens, string $key, array $config = []) : array => self::getBrowser($value, $i, $tokens, $key, $config), |
71
|
49 |
|
'gecko' => function (string $value, int $i, array $tokens, string $key, array $config = []) : array { |
72
|
27 |
|
$parts = \explode('/', $value, 2); |
73
|
27 |
|
return \array_replace(self::getBrowser($value, $i, $tokens, $key, $config), [ |
74
|
27 |
|
'engine' => 'Gecko', |
75
|
27 |
|
'engineversion' => $parts[1] ?? null, |
76
|
27 |
|
]); |
77
|
49 |
|
}, |
78
|
49 |
|
'presto' => function (string $value, int $i, array $tokens, string $key, array $config = []) : array { |
79
|
5 |
|
$parts = \explode('/', $value, 2); |
80
|
5 |
|
return \array_replace(self::getBrowser($value, $i, $tokens, $key, $config), [ |
81
|
5 |
|
'engine' => 'Presto', |
82
|
5 |
|
'engineversion' => $parts[1] ?? null, |
83
|
5 |
|
]); |
84
|
49 |
|
}, |
85
|
49 |
|
'chromium' => function (string $value, int $i, array $tokens, string $key, array $config = []) : array { |
86
|
63 |
|
$parts = \explode('/', $value, 3); |
87
|
63 |
|
$engineversion = isset($parts[1]) && \strspn($parts[1], '1234567890.') === \strlen($parts[1]) ? $parts[1] : null; |
88
|
63 |
|
return \array_replace(self::getBrowser($value, $i, $tokens, $key, $config), [ |
89
|
63 |
|
'engine' => \intval($engineversion ?? 28) < 28 ? 'WebKit' : 'Blink', |
90
|
63 |
|
'engineversion' => $engineversion |
91
|
63 |
|
]); |
92
|
49 |
|
}, |
93
|
49 |
|
'safari' => function (string $value, int $i, array $tokens, string $key, array $config = []) : array { |
94
|
35 |
|
$parts = \explode('/', $value, 2); |
95
|
35 |
|
$version = $parts[1] ?? null; |
96
|
35 |
|
foreach ($tokens AS $item) { |
97
|
35 |
|
if (\mb_stripos($item, 'Version/') === 0) { |
98
|
23 |
|
$version = \mb_substr($item, 8); |
99
|
|
|
} |
100
|
|
|
} |
101
|
35 |
|
return \array_replace([ |
102
|
35 |
|
'type' => 'human', |
103
|
35 |
|
'browser' => 'Safari', |
104
|
35 |
|
'browserversion' => $version ?? null, |
105
|
35 |
|
'engine' => 'WebKit', |
106
|
35 |
|
'engineversion' => $parts[1] ?? null |
107
|
35 |
|
], $version !== null ? versions::get('safari', $version, $config) : []); |
108
|
49 |
|
}, |
109
|
49 |
|
]; |
110
|
17 |
|
return [ |
111
|
17 |
|
'Opera Mini/' => new props('start', $fn['presto']), |
112
|
17 |
|
'Native Opera Mini/' => new props('start', $fn['presto']), |
113
|
17 |
|
'Opera/' => new props('start', $fn['presto']), |
114
|
17 |
|
'OPR/' => new props('start', $fn['browserslash']), |
115
|
17 |
|
'CriOS/' => new props('start', $fn['browserslash']), |
116
|
17 |
|
'Brave/' => new props('start', $fn['browserslash']), |
117
|
17 |
|
'Vivaldi/' => new props('start', $fn['browserslash']), |
118
|
17 |
|
'Maxthon/' => new props('start', $fn['browserslash']), |
119
|
17 |
|
'Maxthon ' => new props('start', fn (string $value, int $i, array $tokens, string $key, array $config = []) : array => \array_merge([ |
120
|
17 |
|
'browser' => 'Maxthon', |
121
|
17 |
|
'browserversion' => \mb_substr($value, 8) |
122
|
17 |
|
], versions::get('maxathon', \mb_substr($value, 8), $config))), |
123
|
17 |
|
'Konqueror/' => new props('start', $fn['browserslash']), |
124
|
17 |
|
'K-Meleon/' => new props('start', $fn['browserslash']), |
125
|
17 |
|
'UCBrowser/' => new props('start', $fn['browserslash']), |
126
|
17 |
|
'Waterfox/' => new props('start', $fn['browserslash']), |
127
|
17 |
|
'PaleMoon/' => new props('start', $fn['browserslash']), |
128
|
17 |
|
'IceWeasel/' => new props('start', $fn['browserslash']), |
129
|
17 |
|
'IceCat/' => new props('start', $fn['browserslash']), |
130
|
17 |
|
'IceApe/' => new props('start', $fn['browserslash']), |
131
|
17 |
|
'Basilisk/' => new props('start', $fn['browserslash']), |
132
|
17 |
|
'SeaMonkey/' => new props('start', $fn['browserslash']), |
133
|
17 |
|
'UP.Browser/' => new props('start', $fn['browserslash']), |
134
|
17 |
|
'Netscape/' => new props('start', $fn['browserslash']), |
135
|
17 |
|
'Thunderbird/' => new props('start', $fn['browserslash']), |
136
|
17 |
|
'Galeon/' => new props('start', $fn['browserslash']), |
137
|
17 |
|
'WebPositive/' => new props('start', $fn['browserslash']), |
138
|
17 |
|
'K-Ninja/' => new props('start', $fn['browserslash']), |
139
|
17 |
|
'SamsungBrowser/' => new props('start', $fn['browserslash']), |
140
|
17 |
|
'HuaweiBrowser/' => new props('start', $fn['browserslash']), |
141
|
17 |
|
'NintendoBrowser/' => new props('start', $fn['browserslash']), |
142
|
17 |
|
'Epiphany/' => new props('start', $fn['browserslash']), |
143
|
17 |
|
'Silk/' => new props('start', $fn['browserslash']), |
144
|
17 |
|
'NetFront/' => new props('start', $fn['browserslash']), |
145
|
17 |
|
'Dooble/' => new props('start', $fn['browserslash']), |
146
|
17 |
|
'Falkon/' => new props('start', $fn['browserslash']), |
147
|
17 |
|
'Namoroka/' => new props('start', $fn['browserslash']), |
148
|
17 |
|
'CocCoc/' => new props('start', $fn['browserslash']), |
149
|
17 |
|
'Obigo/' => new props('start', $fn['browserslash']), |
150
|
17 |
|
'VivoBrowser/' => new props('start', $fn['browserslash']), |
151
|
17 |
|
'com.vivo.browser/' => new props('any', $fn['browserslash']), |
152
|
17 |
|
'QQBrowser/' => new props('any', function (string $value, int $i, array $tokens, string $key, array $config = []) use ($fn) : array { |
153
|
2 |
|
return $fn['browserslash'](\mb_substr($value, \mb_stripos($value, 'QQBrowser/') ?: 0), $i, $tokens, $key, $config); // sometimes missing a space from previous declaration, and MQQBrowser for mobile. |
154
|
17 |
|
}), |
155
|
17 |
|
'MiuiBrowser/' => new props('any', function (string $value, int $i, array $tokens, string $key, array $config = []) use ($fn) : array { |
156
|
2 |
|
return $fn['browserslash'](\mb_substr($value, \mb_stripos($value, 'MiuiBrowser/') ?: 0), $i, $tokens, $key, $config); |
157
|
17 |
|
}), |
158
|
17 |
|
'Lynx/' => new props('start', fn (string $value) : array => [ |
159
|
2 |
|
'browser' => 'Lynx', |
160
|
2 |
|
'browserversion' => \explode('/', $value, 2)[1] ?? null, |
161
|
2 |
|
'engine' => 'Libwww', |
162
|
2 |
|
'type' => 'human', |
163
|
2 |
|
'category' => 'desktop' |
164
|
2 |
|
]), |
165
|
17 |
|
'Midori' => new props('start', function (string $value, int $i, array $tokens, string $key, array $config = []) : array { |
166
|
1 |
|
$parts = \explode('/', $value, 2); |
167
|
1 |
|
$version = $parts[1] ?? \explode(' ', $value, 2)[1] ?? null; |
168
|
1 |
|
$major = $version !== null ? \intval($version) : null; |
169
|
1 |
|
return \array_merge([ |
170
|
1 |
|
'type' => 'human', |
171
|
1 |
|
'browser' => 'Midori', |
172
|
1 |
|
'engine' => $major >= 11 ? 'Gecko' : ($major < 9 ? 'WebKit' : 'Blink'), |
173
|
1 |
|
'browserversion' => $version |
174
|
1 |
|
], $version !== null ? versions::get('midori', $version, $config) : []); |
175
|
17 |
|
}), |
176
|
17 |
|
'PrivacyBrowser/' => new props('start', $fn['browserslash']), |
177
|
17 |
|
'Fennec/' => new props('start', $fn['gecko']), |
178
|
17 |
|
'Firefox/' => new props('start', $fn['gecko']), |
179
|
17 |
|
' Firefox/' => new props('any', $fn['gecko']), |
180
|
17 |
|
'Firefox' => new props('exact', [ |
181
|
17 |
|
'type' => 'human', |
182
|
17 |
|
'engine' => 'Gecko', |
183
|
17 |
|
'browser' => 'Firefox' |
184
|
17 |
|
]), |
185
|
17 |
|
'Minimo/' => new props('start', function (string $value, int $i, array $tokens, string $key, array $config = []) use ($fn) : array { |
186
|
1 |
|
$data = $fn['browserslash']($value, $i, $tokens, $key, $config); |
187
|
1 |
|
return \array_merge($data, [ |
188
|
1 |
|
'engine' => 'Gecko' |
189
|
1 |
|
]); |
190
|
17 |
|
}), |
191
|
17 |
|
'BonEcho/' => new props('start', function (string $value, int $i, array $tokens, string $key, array $config = []) use ($fn) : array { |
192
|
1 |
|
$data = $fn['browserslash']($value, $i, $tokens, $key, $config); |
193
|
1 |
|
return \array_merge($data, [ |
194
|
1 |
|
'engine' => 'Gecko' |
195
|
1 |
|
]); |
196
|
17 |
|
}), |
197
|
17 |
|
'Links/' => new props('start', $fn['browserslash']), |
198
|
17 |
|
'Links' => new props('exact', fn (string $value, int $i, array $tokens, string $key, array $config = []) => [ |
199
|
2 |
|
'type' => 'human', |
200
|
2 |
|
'browser' => $value, |
201
|
2 |
|
'browserversion' => $tokens[$i + 1] |
202
|
2 |
|
]), |
203
|
17 |
|
'Elinks/' => new props('start', $fn['browserslash']), |
204
|
17 |
|
'ELinks' => new props('exact', fn (string $value, int $i, array $tokens, string $key, array $config = []) => [ |
205
|
2 |
|
'type' => 'human', |
206
|
2 |
|
'browser' => $value, |
207
|
2 |
|
'browserversion' => $tokens[$i + 1] |
208
|
2 |
|
]), |
209
|
17 |
|
'Blazer' => new props('start', fn (string $value, int $i, array $tokens, string $key, array $config = []) : array => [ |
210
|
3 |
|
'type' => 'human', |
211
|
3 |
|
'browser' => 'Blazer', |
212
|
3 |
|
'browserversion' => \mb_substr($value, 7), |
213
|
3 |
|
'engine' => 'Proprietary' |
214
|
3 |
|
]), |
215
|
17 |
|
'Edg/' => new props('start', $fn['browserslash']), |
216
|
17 |
|
'EdgA/' => new props('start', $fn['browserslash']), |
217
|
17 |
|
'Edge/' => new props('start', $fn['browserslash']), |
218
|
17 |
|
'EdgiOS/' => new props('start', $fn['browserslash']), |
219
|
17 |
|
'FxiOS/' => new props('start', $fn['browserslash']), |
220
|
17 |
|
'MSIE ' => new props('start', fn (string $value, int $i, array $tokens, string $key, array $config = []) : array => \array_merge([ |
221
|
17 |
|
'type' => 'human', |
222
|
17 |
|
'browser' => 'Internet Explorer', |
223
|
17 |
|
'browserversion' => \mb_substr($value, 5), |
224
|
17 |
|
'engine' => 'Trident' |
225
|
17 |
|
], versions::get('ie', \mb_substr($value, 5), $config))), |
226
|
17 |
|
'BOIE9' => new props('start', fn (string $value, int $i, array $tokens, string $key, array $config = []) => \array_merge([ |
227
|
17 |
|
'type' => 'human', |
228
|
17 |
|
'browser' => 'Internet Explorer', |
229
|
17 |
|
'browserversion' => '9.0', |
230
|
17 |
|
'engine' => 'Trident' |
231
|
17 |
|
], versions::get('ie', '9.0', $config))), |
232
|
17 |
|
'IEMobile/' => new props('start', fn (string $value, int $i, array $tokens, string $key, array $config = []) : array => \array_merge([ |
233
|
17 |
|
'type' => 'human', |
234
|
17 |
|
'browser' => 'Internet Explorer', |
235
|
17 |
|
'browserversion' => \mb_substr($value, 9), |
236
|
17 |
|
'engine' => 'Trident' |
237
|
17 |
|
], versions::get('ie', \mb_substr($value, 9), $config))), |
238
|
17 |
|
'Trident' => new props('start', [ // infill for missing browser name |
239
|
17 |
|
'browser' => 'Internet Explorer' |
240
|
17 |
|
]), |
241
|
17 |
|
'Cronet/' => new props('start', $fn['chromium']), |
242
|
17 |
|
'Chromium/' => new props('start', $fn['chromium']), |
243
|
17 |
|
'Chrome/' => new props('any', $fn['chromium']), |
244
|
17 |
|
'Safari/' => new props('start', $fn['safari']), |
245
|
17 |
|
'Mozilla/' => new props('start', $fn['browserslash']), |
246
|
17 |
|
'rv:' => new props('start', fn (string $value) : array => [ |
247
|
38 |
|
'browserversion' => \mb_substr($value, 3) |
248
|
38 |
|
]) |
249
|
17 |
|
]; |
250
|
|
|
} |
251
|
|
|
} |