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