PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.25
Advanced Ads – Ad Manager & AdSense v2.0.25
2.0.25 2.0.24 2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / packages / mobiledetect / mobiledetectlib / src / MobileDetect.php
advanced-ads / packages / mobiledetect / mobiledetectlib / src Last commit date
MobileDetect.php 1 year ago
MobileDetect.php
1420 lines
1 <?php
2 /**
3 * Mobile Detect Library
4 * Motto: "Every business should have a mobile detection script to detect mobile readers"
5 *
6 * Mobile_Detect is a lightweight PHP class for detecting mobile devices (including tablets).
7 * It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.
8 *
9 * Homepage: http://mobiledetect.net
10 * GitHub: https://github.com/serbanghita/Mobile-Detect
11 * README: https://github.com/serbanghita/Mobile-Detect/blob/master/README.md
12 * CONTRIBUTING: https://github.com/serbanghita/Mobile-Detect/blob/master/docs/CONTRIBUTING.md
13 * KNOWN LIMITATIONS: https://github.com/serbanghita/Mobile-Detect/blob/master/docs/KNOWN_LIMITATIONS.md
14 * EXAMPLES: https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples
15 *
16 * @license https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE
17 * @author Serban Ghita <serbanghita@gmail.com> (since 2012)
18 * @author Nick Ilyin <nick.ilyin@gmail.com>
19 * @author: Victor Stanciu <vic.stanciu@gmail.com> (original author)
20 *
21 * @version 3.74.3
22 */
23 namespace Detection;
24
25 use BadMethodCallException;
26
27 /**
28 * Auto-generated isXXXX() magic methods.
29 * php export/dump_magic_methods.php
30 *
31 * @method bool isiPhone()
32 * @method bool isBlackBerry()
33 * @method bool isPixel()
34 * @method bool isHTC()
35 * @method bool isNexus()
36 * @method bool isDell()
37 * @method bool isMotorola()
38 * @method bool isSamsung()
39 * @method bool isLG()
40 * @method bool isSony()
41 * @method bool isAsus()
42 * @method bool isXiaomi()
43 * @method bool isNokiaLumia()
44 * @method bool isMicromax()
45 * @method bool isPalm()
46 * @method bool isVertu()
47 * @method bool isPantech()
48 * @method bool isFly()
49 * @method bool isWiko()
50 * @method bool isiMobile()
51 * @method bool isSimValley()
52 * @method bool isWolfgang()
53 * @method bool isAlcatel()
54 * @method bool isNintendo()
55 * @method bool isAmoi()
56 * @method bool isINQ()
57 * @method bool isOnePlus()
58 * @method bool isGenericPhone()
59 * @method bool isiPad()
60 * @method bool isNexusTablet()
61 * @method bool isGoogleTablet()
62 * @method bool isSamsungTablet()
63 * @method bool isKindle()
64 * @method bool isSurfaceTablet()
65 * @method bool isHPTablet()
66 * @method bool isAsusTablet()
67 * @method bool isBlackBerryTablet()
68 * @method bool isHTCtablet()
69 * @method bool isMotorolaTablet()
70 * @method bool isNookTablet()
71 * @method bool isAcerTablet()
72 * @method bool isToshibaTablet()
73 * @method bool isLGTablet()
74 * @method bool isFujitsuTablet()
75 * @method bool isPrestigioTablet()
76 * @method bool isLenovoTablet()
77 * @method bool isDellTablet()
78 * @method bool isYarvikTablet()
79 * @method bool isMedionTablet()
80 * @method bool isArnovaTablet()
81 * @method bool isIntensoTablet()
82 * @method bool isIRUTablet()
83 * @method bool isMegafonTablet()
84 * @method bool isEbodaTablet()
85 * @method bool isAllViewTablet()
86 * @method bool isArchosTablet()
87 * @method bool isAinolTablet()
88 * @method bool isNokiaLumiaTablet()
89 * @method bool isSonyTablet()
90 * @method bool isPhilipsTablet()
91 * @method bool isCubeTablet()
92 * @method bool isCobyTablet()
93 * @method bool isMIDTablet()
94 * @method bool isMSITablet()
95 * @method bool isSMiTTablet()
96 * @method bool isRockChipTablet()
97 * @method bool isFlyTablet()
98 * @method bool isbqTablet()
99 * @method bool isHuaweiTablet()
100 * @method bool isNecTablet()
101 * @method bool isPantechTablet()
102 * @method bool isBronchoTablet()
103 * @method bool isVersusTablet()
104 * @method bool isZyncTablet()
105 * @method bool isPositivoTablet()
106 * @method bool isNabiTablet()
107 * @method bool isKoboTablet()
108 * @method bool isDanewTablet()
109 * @method bool isTexetTablet()
110 * @method bool isPlaystationTablet()
111 * @method bool isTrekstorTablet()
112 * @method bool isPyleAudioTablet()
113 * @method bool isAdvanTablet()
114 * @method bool isDanyTechTablet()
115 * @method bool isGalapadTablet()
116 * @method bool isMicromaxTablet()
117 * @method bool isKarbonnTablet()
118 * @method bool isAllFineTablet()
119 * @method bool isPROSCANTablet()
120 * @method bool isYONESTablet()
121 * @method bool isChangJiaTablet()
122 * @method bool isGUTablet()
123 * @method bool isPointOfViewTablet()
124 * @method bool isOvermaxTablet()
125 * @method bool isHCLTablet()
126 * @method bool isDPSTablet()
127 * @method bool isVistureTablet()
128 * @method bool isCrestaTablet()
129 * @method bool isMediatekTablet()
130 * @method bool isConcordeTablet()
131 * @method bool isGoCleverTablet()
132 * @method bool isModecomTablet()
133 * @method bool isVoninoTablet()
134 * @method bool isECSTablet()
135 * @method bool isStorexTablet()
136 * @method bool isVodafoneTablet()
137 * @method bool isEssentielBTablet()
138 * @method bool isRossMoorTablet()
139 * @method bool isiMobileTablet()
140 * @method bool isTolinoTablet()
141 * @method bool isAudioSonicTablet()
142 * @method bool isAMPETablet()
143 * @method bool isSkkTablet()
144 * @method bool isTecnoTablet()
145 * @method bool isJXDTablet()
146 * @method bool isiJoyTablet()
147 * @method bool isFX2Tablet()
148 * @method bool isXoroTablet()
149 * @method bool isViewsonicTablet()
150 * @method bool isVerizonTablet()
151 * @method bool isOdysTablet()
152 * @method bool isCaptivaTablet()
153 * @method bool isIconbitTablet()
154 * @method bool isTeclastTablet()
155 * @method bool isOndaTablet()
156 * @method bool isJaytechTablet()
157 * @method bool isBlaupunktTablet()
158 * @method bool isDigmaTablet()
159 * @method bool isEvolioTablet()
160 * @method bool isLavaTablet()
161 * @method bool isAocTablet()
162 * @method bool isMpmanTablet()
163 * @method bool isCelkonTablet()
164 * @method bool isWolderTablet()
165 * @method bool isMediacomTablet()
166 * @method bool isMiTablet()
167 * @method bool isNibiruTablet()
168 * @method bool isNexoTablet()
169 * @method bool isLeaderTablet()
170 * @method bool isUbislateTablet()
171 * @method bool isPocketBookTablet()
172 * @method bool isKocasoTablet()
173 * @method bool isHisenseTablet()
174 * @method bool isHudl()
175 * @method bool isTelstraTablet()
176 * @method bool isGenericTablet()
177 * @method bool isAndroidOS()
178 * @method bool isBlackBerryOS()
179 * @method bool isPalmOS()
180 * @method bool isSymbianOS()
181 * @method bool isWindowsMobileOS()
182 * @method bool isWindowsPhoneOS()
183 * @method bool isiOS()
184 * @method bool isiPadOS()
185 * @method bool isSailfishOS()
186 * @method bool isMeeGoOS()
187 * @method bool isMaemoOS()
188 * @method bool isJavaOS()
189 * @method bool iswebOS()
190 * @method bool isbadaOS()
191 * @method bool isBREWOS()
192 * @method bool isChrome()
193 * @method bool isDolfin()
194 * @method bool isOpera()
195 * @method bool isSkyfire()
196 * @method bool isEdge()
197 * @method bool isIE()
198 * @method bool isFirefox()
199 * @method bool isBolt()
200 * @method bool isTeaShark()
201 * @method bool isBlazer()
202 * @method bool isSafari()
203 * @method bool isWeChat()
204 * @method bool isUCBrowser()
205 * @method bool isbaiduboxapp()
206 * @method bool isbaidubrowser()
207 * @method bool isDiigoBrowser()
208 * @method bool isMercury()
209 * @method bool isObigoBrowser()
210 * @method bool isNetFront()
211 * @method bool isGenericBrowser()
212 * @method bool isPaleMoon()
213 * @method bool isWebKit()
214 * @method bool isConsole()
215 * @method bool isWatch()
216 */
217 class MobileDetect
218 {
219 /**
220 * A frequently used regular expression to extract version #s.
221 *
222 * @deprecated since version 2.6.9
223 */
224 const VER = '([\w._\+]+)';
225
226 /**
227 * Stores the version number of the current release.
228 */
229 const VERSION = '3.74.3';
230
231 /**
232 * A type for the version() method indicating a string return value.
233 */
234 const VERSION_TYPE_STRING = 'text';
235
236 /**
237 * A type for the version() method indicating a float return value.
238 */
239 const VERSION_TYPE_FLOAT = 'float';
240
241 /**
242 * A cache for resolved matches
243 * @var array
244 */
245 protected array $cache = [];
246
247 /**
248 * The User-Agent HTTP header is stored in here.
249 * @var string|null
250 */
251 protected ?string $userAgent = null;
252
253 /**
254 * HTTP headers in the PHP-flavor. So HTTP_USER_AGENT and SERVER_SOFTWARE.
255 * @var array
256 */
257 protected array $httpHeaders = [];
258
259 /**
260 * CloudFront headers. E.g. CloudFront-Is-Desktop-Viewer, CloudFront-Is-Mobile-Viewer & CloudFront-Is-Tablet-Viewer.
261 * @var array
262 */
263 protected array $cloudfrontHeaders = [];
264
265 /**
266 * The matching Regex.
267 * This is good for debug.
268 * @var string|null
269 */
270 protected ?string $matchingRegex = null;
271
272 /**
273 * The matches extracted from the regex expression.
274 * This is good for debug.
275 *
276 * @var array|null
277 */
278 protected ?array $matchesArray = null;
279
280 /**
281 * HTTP headers that trigger the 'isMobile' detection
282 * to be true.
283 *
284 * @var array
285 */
286 protected static array $mobileHeaders = [
287
288 'HTTP_ACCEPT' => [
289 'matches' => [
290 // Opera Mini
291 // @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/
292 'application/x-obml2d',
293 // BlackBerry devices.
294 'application/vnd.rim.html',
295 'text/vnd.wap.wml',
296 'application/vnd.wap.xhtml+xml'
297 ]],
298 'HTTP_X_WAP_PROFILE' => null,
299 'HTTP_X_WAP_CLIENTID' => null,
300 'HTTP_WAP_CONNECTION' => null,
301 'HTTP_PROFILE' => null,
302 // Reported by Opera on Nokia devices (eg. C3).
303 'HTTP_X_OPERAMINI_PHONE_UA' => null,
304 'HTTP_X_NOKIA_GATEWAY_ID' => null,
305 'HTTP_X_ORANGE_ID' => null,
306 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
307 'HTTP_X_HUAWEI_USERID' => null,
308 // Reported by Windows Smartphones.
309 'HTTP_UA_OS' => null,
310 // Reported by Verizon, Vodafone proxy system.
311 'HTTP_X_MOBILE_GATEWAY' => null,
312 // Seen this on HTC Sensation. SensationXE_Beats_Z715e.
313 'HTTP_X_ATT_DEVICEID' => null,
314 // Seen this on a HTC.
315 'HTTP_UA_CPU' => ['matches' => ['ARM']],
316 ];
317
318 /**
319 * List of mobile devices (phones).
320 *
321 * @var array
322 */
323 protected static array $phoneDevices = [
324 'iPhone' => '\biPhone\b|\biPod\b', // |\biTunes
325 'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+|\b(BBA100|BBB100|BBD100|BBE100|BBF100|STH100)\b-[0-9]+',
326 'Pixel' => '; \bPixel\b',
327 'HTC' => 'HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\bEVO\b|T-Mobile G1|Z520m|Android [0-9.]+; Pixel',
328 'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 5X|Nexus 6',
329 // @todo: Is 'Dell Streak' a tablet or a phone? ;)
330 'Dell' => 'Dell[;]? (Streak|Aero|Venue|Venue Pro|Flash|Smoke|Mini 3iX)|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b',
331 'Motorola' => 'Motorola|DROIDX|DROID BIONIC|\bDroid\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\bMoto E\b|XT1068|XT1092|XT1052',
332 'Samsung' => '\bSamsung\b|SM-G950F|SM-G955F|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K|SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558|GT-I9205|SM-G9350|SM-J120F|SM-G920F|SM-G920V|SM-G930F|SM-N910C|SM-A310F|GT-I9190|SM-J500FN|SM-G903F|SM-J330F|SM-G610F|SM-G981B|SM-G892A|SM-A530F|SM-G988N|SM-G781B|SM-A805N|SM-G965F',
333 'LG' => '\bLG\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323|M257)|LM-G710',
334 'Sony' => 'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533|SOV34|601SO|F8332',
335 'Asus' => 'Asus.*Galaxy|PadFone.*Mobile|ASUS_Z01QD|ASUS_X00TD',
336 'Xiaomi' => '^(?!.*\bx11\b).*xiaomi.*$|POCOPHONE F1|\bMI\b 8|\bMi\b 10|Redmi Note 9S|Redmi 5A|Redmi Note 5A Prime|Redmi Note 7 Pro|N2G47H|M2001J2G|M2001J2I|M1805E10A|M2004J11G|M1902F1G|M2002J9G|M2004J19G|M2003J6A1G|M2012K11C|M2007J1SC',
337 'NokiaLumia' => 'Lumia [0-9]{3,4}',
338 // http://www.micromaxinfo.com/mobiles/smartphones
339 // Added because the codes might conflict with Acer Tablets.
340 'Micromax' => 'Micromax.*\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\b',
341 // @todo Complete the regex.
342 'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ;
343 'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature', // Just for fun ;)
344 // http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH)
345 // Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android.
346 'Pantech' => 'PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790',
347 // http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones.
348 'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250',
349 // http://fr.wikomobile.com
350 'Wiko' => 'KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY|BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM',
351 'iMobile' => 'i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)',
352 // Added simvalley mobile just for fun. They have some interesting devices.
353 // http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html
354 'SimValley' => '\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\b',
355 // Wolfgang - a brand that is sold by Aldi supermarkets.
356 // http://www.wolfgangmobile.com/
357 'Wolfgang' => 'AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q',
358 'Alcatel' => 'Alcatel',
359 'Nintendo' => 'Nintendo (3DS|Switch)',
360 // http://en.wikipedia.org/wiki/Amoi
361 'Amoi' => 'Amoi',
362 // http://en.wikipedia.org/wiki/INQ
363 'INQ' => 'INQ',
364 'OnePlus' => 'ONEPLUS',
365 // @Tapatalk is a mobile app; http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039
366 'GenericPhone' => 'Tapatalk|PDA;|SAGEM|\bmmp\b|pocket|\bpsp\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\bwap\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser',
367 ];
368
369 /**
370 * List of tablet devices.
371 *
372 * @var array
373 */
374 protected static array $tabletDevices = [
375 // @todo: check for mobile friendly emails topic.
376 'iPad' => 'iPad|iPad.*Mobile',
377 // Removed |^.*Android.*Nexus(?!(?:Mobile).)*$
378 // @see #442
379 // @todo Merge NexusTablet into GoogleTablet.
380 'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)',
381 // https://en.wikipedia.org/wiki/Pixel_C
382 'GoogleTablet' => 'Android.*Pixel C',
383 'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-T116BU|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y?|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587|SM-P350|SM-P555M|SM-P355M|SM-T113NU|SM-T815Y|SM-T585|SM-T285|SM-T825|SM-W708|SM-T835|SM-T830|SM-T837V|SM-T720|SM-T510|SM-T387V|SM-P610|SM-T290|SM-T515|SM-T590|SM-T595|SM-T725|SM-T817P|SM-P585N0|SM-T395|SM-T295|SM-T865|SM-P610N|SM-P615|SM-T970|SM-T380|SM-T5950|SM-T905|SM-T231|SM-T500|SM-T860|SM-T536|SM-T837A|SM-X200|SM-T220|SM-T870|SM-X906C|SM-X700|SM-X706|SM-X706B|SM-X706U|SM-X706N|SM-X800|SM-X806|SM-X806B|SM-X806U|SM-X806N|SM-X900|SM-X906|SM-X906B|SM-X906U|SM-X906N|SM-P613', // SCH-P709|SCH-P729|SM-T2558|GT-I9205 - Samsung Mega - treat them like a regular phone.
384 // http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
385 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\b|Android.*Silk/[0-9.]+ like Chrome/[0-9.]+ (?!Mobile)',
386 // Only the Surface tablets with Windows RT are considered mobile.
387 // http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
388 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)',
389 // http://shopping1.hp.com/is-bin/INTERSHOP.enfinity/WFS/WW-USSMBPublicStore-Site/en_US/-/USD/ViewStandardCatalog-Browse?CatalogCategoryID=JfIQ7EN5lqMAAAEyDcJUDwMT
390 'HPTablet' => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10',
391 // Watch out for PadFone, see #132.
392 // http://www.asus.com/de/Tablets_Mobile/Memo_Pad_Products/
393 'AsusTablet' => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b|\bK00C\b|\bK00E\b|\bK00L\b|TX201LA|ME176C|ME102A|\bM80TA\b|ME372CL|ME560CG|ME372CG|ME302KL| K01A | K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\bME70C\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z|\bP027\b|\bP024\b|\bP00C\b',
394 'BlackBerryTablet' => 'PlayBook|RIM Tablet',
395 'HTCtablet' => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410',
396 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
397 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
398 // http://www.acer.ro/ac/ro/RO/content/drivers
399 // http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer)
400 // http://us.acer.com/ac/en/US/content/group/tablets
401 // http://www.acer.de/ac/de/DE/content/models/tablets/
402 // Can conflict with Micromax and Motorola phones codes.
403 'AcerTablet' => 'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\b|W3-810|\bA3-A10\b|\bA3-A11\b|\bA3-A20\b|\bA3-A30|A3-A40',
404 // http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/
405 // http://us.toshiba.com/tablets/tablet-finder
406 // http://www.toshiba.co.jp/regza/tablet/
407 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
408 // http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html
409 // http://www.lg.com/us/tablets
410 'LGTablet' => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b',
411 'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b',
412 // Prestigio Tablets http://www.prestigio.com/support
413 'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002',
414 // http://support.lenovo.com/en_GB/downloads/default.page?#
415 'LenovoTablet' => 'Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-850M|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)|TB-X103F|TB-X304X|TB-X304F|TB-X304L|TB-X505F|TB-X505L|TB-X505X|TB-X605F|TB-X605L|TB-8703F|TB-8703X|TB-8703N|TB-8704N|TB-8704F|TB-8704X|TB-8704V|TB-7304F|TB-7304I|TB-7304X|Tab2A7-10F|Tab2A7-20F|TB2-X30L|YT3-X50L|YT3-X50F|YT3-X50M|YT-X705F|YT-X703F|YT-X703L|YT-X705L|YT-X705X|TB2-X30F|TB2-X30L|TB2-X30M|A2107A-F|A2107A-H|TB3-730F|TB3-730M|TB3-730X|TB-7504F|TB-7504X|TB-X704F|TB-X104F|TB3-X70F|TB-X705F|TB-8504F|TB3-X70L|TB3-710F|TB-X704L|TB-J606F|TB-X606F|TB-X306X|YT-J706X|TB128FU',
416 // http://www.dell.com/support/home/us/en/04/Products/tab_mob/tablets
417 'DellTablet' => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7',
418 'XiaomiTablet' => '21051182G',
419 // http://www.yarvik.com/en/matrix/tablets/
420 'YarvikTablet' => 'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b',
421 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
422 'ArnovaTablet' => '97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2',
423 // http://www.intenso.de/kategorie_en.php?kategorie=33
424 // @todo: http://www.nbhkdz.com/read/b8e64202f92a2df129126bff.html - investigate
425 'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004',
426 // IRU.ru Tablets http://www.iru.ru/catalog/soho/planetable/
427 'IRUTablet' => 'M702pro',
428 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
429 // http://www.e-boda.ro/tablete-pc.html
430 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
431 // http://www.allview.ro/produse/droseries/lista-tablete-pc/
432 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
433 // http://wiki.archosfans.com/index.php?title=Main_Page
434 // @note Rewrite the regex format after we add more UAs.
435 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b',
436 // http://www.ainol.com/plugin.php?identifier=ainol&module=product
437 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
438 'NokiaLumiaTablet' => 'Lumia 2520',
439 // @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER
440 // Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser
441 // http://www.sony.jp/support/tablet/
442 'SonyTablet' => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP641|SGP612|SOT31|SGP771|SGP611|SGP612|SGP712',
443 // http://www.support.philips.com/support/catalog/worldproducts.jsp?userLanguage=en&userCountry=cn&categoryid=3G_LTE_TABLET_SU_CN_CARE&title=3G%20tablets%20/%20LTE%20range&_dyncharset=UTF-8
444 'PhilipsTablet' => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b',
445 // db + http://www.cube-tablet.com/buy-products.html
446 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
447 // http://www.cobyusa.com/?p=pcat&pcat_id=3001
448 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
449 // http://www.match.net.cn/products.asp
450 'MIDTablet' => 'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10',
451 // http://www.msi.com/support
452 // @todo Research the Windows Tablets.
453 'MSITablet' => 'MSI \b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\b',
454 // @todo http://www.kyoceramobile.com/support/drivers/
455 // 'KyoceraTablet' => null,
456 // @todo http://intexuae.com/index.php/category/mobile-devices/tablets-products/
457 // 'IntextTablet' => null,
458 // http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets)
459 // http://www.imp3.net/14/show.php?itemid=20454
460 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
461 // http://www.rock-chips.com/index.php?do=prod&pid=2
462 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
463 // http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/
464 'FlyTablet' => 'IQ310|Fly Vision',
465 // http://www.bqreaders.com/gb/tablets-prices-sale.html
466 'bqTablet' => 'Android.*(bq)?.*\b(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris ([E|M]10|M8))\b|Maxwell.*Lite|Maxwell.*Plus',
467 // http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290
468 // http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets)
469 'HuaweiTablet' => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim|M2-A01L|BAH-L09|BAH-W09|AGS-L09|CMR-AL19|KOB2-L09|BG2-U01|BG2-W09|BG2-U03',
470 // Nec or Medias Tab
471 'NecTablet' => '\bN-06D|\bN-08D',
472 // Pantech Tablets: http://www.pantechusa.com/phones/
473 'PantechTablet' => 'Pantech.*P4100',
474 // Broncho Tablets: http://www.broncho.cn/ (hard to find)
475 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
476 // http://versusuk.com/support.html
477 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
478 // http://www.zync.in/index.php/our-products/tablet-phablets
479 'ZyncTablet' => 'z1000|Z99 2G|z930|z990|z909|Z919|z900', // Removed "z999" because of https://github.com/serbanghita/Mobile-Detect/issues/717
480 // http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/
481 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
482 // https://www.nabitablet.com/
483 'NabiTablet' => 'Android.*\bNabi',
484 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
485 // French Danew Tablets http://www.danew.com/produits-tablette.php
486 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
487 // Texet Tablets and Readers http://www.texet.ru/tablet/
488 'TexetTablet' => 'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE',
489 // Avoid detecting 'PLAYSTATION 3' as mobile.
490 'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
491 // http://www.trekstor.de/surftabs.html
492 'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab',
493 // http://www.pyleaudio.com/Products.aspx?%2fproducts%2fPersonal-Electronics%2fTablets
494 'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b',
495 // http://www.advandigital.com/index.php?link=content-product&jns=JP001
496 // because of the short codenames we have to include whitespaces to reduce the possible conflicts.
497 'AdvanTablet' => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ',
498 // http://www.danytech.com/category/tablet-pc
499 'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1',
500 // http://www.galapad.net/product.html ; https://github.com/serbanghita/Mobile-Detect/issues/761
501 'GalapadTablet' => 'Android [0-9.]+; [a-z-]+; \bG1\b',
502 // http://www.micromaxinfo.com/tablet/funbook
503 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
504 // http://www.karbonnmobiles.com/products_tablet.php
505 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
506 // http://www.myallfine.com/Products.asp
507 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
508 // http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr=
509 'PROSCANTablet' => '\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\b',
510 // http://www.yonesnav.com/products/products.php
511 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
512 // http://www.cjshowroom.com/eproducts.aspx?classcode=004001001
513 // China manufacturer makes tablets for different small brands (eg. http://www.zeepad.net/index.html)
514 'ChangJiaTablet' => 'TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503',
515 // http://www.gloryunion.cn/products.asp
516 // http://www.allwinnertech.com/en/apply/mobile.html
517 // http://www.ptcl.com.pk/pd_content.php?pd_id=284 (EVOTAB)
518 // @todo: Softwiner tablets?
519 // aka. Cute or Cool tablets. Not sure yet, must research to avoid collisions.
520 'GUTablet' => 'TX-A1301|TX-M9002|Q702|kf026', // A12R|D75A|D77|D79|R83|A95|A106C|R15|A75|A76|D71|D72|R71|R73|R77|D82|R85|D92|A97|D92|R91|A10F|A77F|W71F|A78F|W78F|W81F|A97F|W91F|W97F|R16G|C72|C73E|K72|K73|R96G
521 // http://www.pointofview-online.com/showroom.php?shop_mode=product_listing&category_id=118
522 'PointOfViewTablet' => 'TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10',
523 // http://www.overmax.pl/pl/katalog-produktow,p8/tablety,c14/
524 // @todo: add more tests.
525 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)|Qualcore 1027',
526 // http://hclmetablet.com/India/index.php
527 'HCLTablet' => 'HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync',
528 // http://www.edigital.hu/Tablet_es_e-book_olvaso/Tablet-c18385.html
529 'DPSTablet' => 'DPS Dream 9|DPS Dual 7',
530 // http://www.visture.com/index.asp
531 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
532 // http://www.mijncresta.nl/tablet
533 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
534 // MediaTek - http://www.mediatek.com/_en/01_products/02_proSys.php?cata_sn=1&cata1_sn=1&cata2_sn=309
535 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
536 // Concorde tab
537 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
538 // GoClever Tablets - http://www.goclever.com/uk/products,c1/tablet,c5/
539 'GoCleverTablet' => 'GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042',
540 // Modecom Tablets - http://www.modecom.eu/tablets/portal/
541 'ModecomTablet' => 'FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003',
542 // Vonino Tablets
543 'VoninoTablet' => '\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b',
544 // ECS Tablets - http://www.ecs.com.tw/ECSWebSite/Product/Product_Tablet_List.aspx?CategoryID=14&MenuID=107&childid=M_107&LanID=0
545 'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1',
546 // Storex Tablets - http://storex.fr/espace_client/support.html
547 // @note: no need to add all the tablet codes since they are guided by the first regex.
548 'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab',
549 // Generic Vodafone tablets.
550 'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497|VFD 1400',
551 // French tablets - Essentiel B http://www.boulanger.fr/tablette_tactile_e-book/tablette_tactile_essentiel_b/cl_68908.htm?multiChoiceToDelete=brand&mc_brand=essentielb
552 // Aka: http://www.essentielb.fr/
553 'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2',
554 // Ross & Moor - http://ross-moor.ru/
555 'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711',
556 // i-mobile http://product.i-mobilephone.com/Mobile_Device
557 'iMobileTablet' => 'i-mobile i-note',
558 // http://www.tolino.de/de/vergleichen/
559 'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine',
560 // AudioSonic - a Kmart brand
561 // http://www.kmart.com.au/webapp/wcs/stores/servlet/Search?langId=-1&storeId=10701&catalogId=10001&categoryId=193001&pageSize=72&currentPage=1&searchCategory=193001%2b4294965664&sortBy=p_MaxPrice%7c1
562 'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b',
563 // AMPE Tablets - http://www.ampe.com.my/product-category/tablets/
564 // @todo: add them gradually to avoid conflicts.
565 'AMPETablet' => 'Android.* A78 ',
566 // Skk Mobile - http://skkmobile.com.ph/product_tablets.php
567 'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)',
568 // Tecno Mobile (only tablet) - http://www.tecno-mobile.com/index.php/product?filterby=smart&list_order=all&page=1
569 'TecnoTablet' => 'TECNO P9|TECNO DP8D',
570 // JXD (consoles & tablets) - http://jxd.hk/products.asp?selectclassid=009008&clsid=3
571 'JXDTablet' => 'Android.* \b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b',
572 // i-Joy tablets - http://www.i-joy.es/en/cat/products/tablets/
573 'iJoyTablet' => 'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)',
574 // http://www.intracon.eu/tablet
575 'FX2Tablet' => 'FX2 PAD7|FX2 PAD10',
576 // http://www.xoro.de/produkte/
577 // @note: Might be the same brand with 'Simply tablets'
578 'XoroTablet' => 'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151',
579 // http://www1.viewsonic.com/products/computing/tablets/
580 'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a',
581 // https://www.verizonwireless.com/tablets/verizon/
582 'VerizonTablet' => 'QTAQZ3|QTAIR7|QTAQTZ3|QTASUN1|QTASUN2|QTAXIA1',
583 // http://www.odys.de/web/internet-tablet_en.html
584 'OdysTablet' => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10',
585 // http://www.captiva-power.de/products.html#tablets-en
586 'CaptivaTablet' => 'CAPTIVA PAD',
587 // IconBIT - http://www.iconbit.com/products/tablets/
588 'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S',
589 // http://www.teclast.com/topic.php?channelID=70&topicID=140&pid=63
590 'TeclastTablet' => 'T98 4G|\bP80\b|\bX90HD\b|X98 Air|X98 Air 3G|\bX89\b|P80 3G|\bX80h\b|P98 Air|\bX89HD\b|P98 3G|\bP90HD\b|P89 3G|X98 3G|\bP70h\b|P79HD 3G|G18d 3G|\bP79HD\b|\bP89s\b|\bA88\b|\bP10HD\b|\bP19HD\b|G18 3G|\bP78HD\b|\bA78\b|\bP75\b|G17s 3G|G17h 3G|\bP85t\b|\bP90\b|\bP11\b|\bP98t\b|\bP98HD\b|\bG18d\b|\bP85s\b|\bP11HD\b|\bP88s\b|\bA80HD\b|\bA80se\b|\bA10h\b|\bP89\b|\bP78s\b|\bG18\b|\bP85\b|\bA70h\b|\bA70\b|\bG17\b|\bP18\b|\bA80s\b|\bA11s\b|\bP88HD\b|\bA80h\b|\bP76s\b|\bP76h\b|\bP98\b|\bA10HD\b|\bP78\b|\bP88\b|\bA11\b|\bA10t\b|\bP76a\b|\bP76t\b|\bP76e\b|\bP85HD\b|\bP85a\b|\bP86\b|\bP75HD\b|\bP76v\b|\bA12\b|\bP75a\b|\bA15\b|\bP76Ti\b|\bP81HD\b|\bA10\b|\bT760VE\b|\bT720HD\b|\bP76\b|\bP73\b|\bP71\b|\bP72\b|\bT720SE\b|\bC520Ti\b|\bT760\b|\bT720VE\b|T720-3GE|T720-WiFi',
591 // Onda - http://www.onda-tablet.com/buy-android-onda.html?dir=desc&limit=all&order=price
592 'OndaTablet' => '\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\b[\s]+|V10 \b4G\b',
593 'JaytechTablet' => 'TPC-PA762',
594 'BlaupunktTablet' => 'Endeavour 800NG|Endeavour 1010',
595 // http://www.digma.ru/support/download/
596 // @todo: Ebooks also (if requested)
597 'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b',
598 // http://www.evolioshop.com/ro/tablete-pc.html
599 // http://www.evolio.ro/support/downloads_static.html?cat=2
600 // @todo: Research some more
601 'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b',
602 // @todo http://www.lavamobiles.com/tablets-data-cards
603 'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY|\bE-TAB\b',
604 // http://www.breezetablet.com/
605 'AocTablet' => 'MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712',
606 // http://www.mpmaneurope.com/en/products/internet-tablets-14/android-tablets-14/
607 'MpmanTablet' => 'MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\bMPG7\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010',
608 // https://www.celkonmobiles.com/?_a=categoryphones&sid=2
609 'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b',
610 // http://www.wolderelectronics.com/productos/manuales-y-guias-rapidas/categoria-2-miTab
611 'WolderTablet' => 'miTab \b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\b',
612 'MediacomTablet' => 'M-MPI10C3G|M-SP10EG|M-SP10EGP|M-SP10HXAH|M-SP7HXAH|M-SP10HXBH|M-SP8HXAH|M-SP8MXA',
613 // http://www.mi.com/en
614 'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b',
615 // http://www.nbru.cn/index.html
616 'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One',
617 // http://navroad.com/products/produkty/tablety/
618 // http://navroad.com/products/produkty/tablety/
619 'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI',
620 // http://leader-online.com/new_site/product-category/tablets/
621 // http://www.leader-online.net.au/List/Tablet
622 'LeaderTablet' => 'TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100',
623 // http://www.datawind.com/ubislate/
624 'UbislateTablet' => 'UbiSlate[\s]?7C',
625 // http://www.pocketbook-int.com/ru/support
626 'PocketBookTablet' => 'Pocketbook',
627 // http://www.kocaso.com/product_tablet.html
628 'KocasoTablet' => '\b(TB-1207)\b',
629 // http://global.hisense.com/product/asia/tablet/Sero7/201412/t20141215_91832.htm
630 'HisenseTablet' => '\b(F5281|E2371)\b',
631 // http://www.tesco.com/direct/hudl/
632 'Hudl' => 'Hudl HT7S3|Hudl 2',
633 // http://www.telstra.com.au/home-phone/thub-2/
634 'TelstraTablet' => 'T-Hub2',
635 'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\bM6pro\b|CT1020W|arc 10HD|\bTP750\b|\bQTAQZ3\b|WVT101|TM1088|KT107'
636 ];
637
638 /**
639 * List of mobile Operating Systems.
640 *
641 * @var array
642 */
643 protected static array $operatingSystems = [
644 'AndroidOS' => 'Android',
645 'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os',
646 'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino',
647 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b',
648 // @reference: http://en.wikipedia.org/wiki/Windows_Mobile
649 'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Windows Mobile|Windows Phone [0-9.]+|WCE;',
650 // @reference: http://en.wikipedia.org/wiki/Windows_Phone
651 // http://wifeng.cn/?r=blog&a=view&id=106
652 // http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx
653 // http://msdn.microsoft.com/library/ms537503.aspx
654 // https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
655 'WindowsPhoneOS' => 'Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;',
656 'iOS' => '\biPhone.*Mobile|\biPod|\biPad|AppleCoreMedia',
657 // https://en.wikipedia.org/wiki/IPadOS
658 'iPadOS' => 'CPU OS 13',
659 // @reference https://en.m.wikipedia.org/wiki/Sailfish_OS
660 // https://sailfishos.org/
661 'SailfishOS' => 'Sailfish',
662 // http://en.wikipedia.org/wiki/MeeGo
663 // @todo: research MeeGo in UAs
664 'MeeGoOS' => 'MeeGo',
665 // http://en.wikipedia.org/wiki/Maemo
666 // @todo: research Maemo in UAs
667 'MaemoOS' => 'Maemo',
668 'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b', // '|Java/' produces bug #135
669 'webOS' => 'webOS|hpwOS',
670 'badaOS' => '\bBada\b',
671 'BREWOS' => 'BREW',
672 ];
673
674 /**
675 * List of mobile User Agents.
676 *
677 * IMPORTANT: This is a list of only mobile browsers.
678 * Mobile Detect 2.x supports only mobile browsers,
679 * it was never designed to detect all browsers.
680 * The change will come in 2017 in the 3.x release for PHP7.
681 *
682 * @var array
683 */
684 protected static array $browsers = [
685 //'Vivaldi' => 'Vivaldi',
686 // @reference: https://developers.google.com/chrome/mobile/docs/user-agent
687 'Chrome' => '\bCrMo\b|CriOS.*Mobile|Android.*Chrome/[.0-9]* Mobile',
688 'Dolfin' => '\bDolfin\b',
689 'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+$|Coast/[0-9.]+',
690 'Skyfire' => 'Skyfire',
691 // Added "Edge on iOS" https://github.com/serbanghita/Mobile-Detect/issues/764
692 'Edge' => 'EdgiOS.*Mobile|Mobile Safari/[.0-9]* Edge',
693 'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+
694 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS.*Mobile',
695 'Bolt' => 'bolt',
696 'TeaShark' => 'teashark',
697 'Blazer' => 'Blazer',
698 // @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3
699 // Excluded "Edge on iOS" https://github.com/serbanghita/Mobile-Detect/issues/764
700 'Safari' => 'Version((?!\bEdgiOS\b).)*Mobile.*Safari|Safari.*Mobile|MobileSafari',
701 // http://en.wikipedia.org/wiki/Midori_(web_browser)
702 //'Midori' => 'midori',
703 //'Tizen' => 'Tizen',
704 'WeChat' => '\bMicroMessenger\b',
705 'UCBrowser' => 'UC.*Browser|UCWEB',
706 'baiduboxapp' => 'baiduboxapp',
707 'baidubrowser' => 'baidubrowser',
708 // https://github.com/serbanghita/Mobile-Detect/issues/7
709 'DiigoBrowser' => 'DiigoBrowser',
710 // http://www.puffinbrowser.com/index.php
711 // https://github.com/serbanghita/Mobile-Detect/issues/752
712 // 'Puffin' => 'Puffin',
713 // http://mercury-browser.com/index.html
714 'Mercury' => '\bMercury\b',
715 // http://en.wikipedia.org/wiki/Obigo_Browser
716 'ObigoBrowser' => 'Obigo',
717 // http://en.wikipedia.org/wiki/NetFront
718 'NetFront' => 'NF-Browser',
719 // @reference: http://en.wikipedia.org/wiki/Minimo
720 // http://en.wikipedia.org/wiki/Vision_Mobile_Browser
721 'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger',
722 // @reference: https://en.wikipedia.org/wiki/Pale_Moon_(web_browser)
723 'PaleMoon' => 'Android.*PaleMoon|Mobile.*PaleMoon',
724 ];
725
726 /**
727 * All possible HTTP headers that represent the
728 * User-Agent string.
729 *
730 * @var array
731 */
732 protected static array $uaHttpHeaders = [
733 // The default User-Agent string.
734 'HTTP_USER_AGENT',
735 // Header can occur on devices using Opera Mini.
736 'HTTP_X_OPERAMINI_PHONE_UA',
737 // Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/
738 'HTTP_X_DEVICE_USER_AGENT',
739 'HTTP_X_ORIGINAL_USER_AGENT',
740 'HTTP_X_SKYFIRE_PHONE',
741 'HTTP_X_BOLT_PHONE_UA',
742 'HTTP_DEVICE_STOCK_UA',
743 'HTTP_X_UCBROWSER_DEVICE_UA'
744 ];
745
746 /**
747 * The individual segments that could exist in a User-Agent string. VER refers to the regular
748 * expression defined in the constant self::VER.
749 *
750 * @var array
751 */
752 protected static array $properties = [
753
754 // Build
755 'Mobile' => 'Mobile/[VER]',
756 'Build' => 'Build/[VER]',
757 'Version' => 'Version/[VER]',
758 'VendorID' => 'VendorID/[VER]',
759
760 // Devices
761 'iPad' => 'iPad.*CPU[a-z ]+[VER]',
762 'iPhone' => 'iPhone.*CPU[a-z ]+[VER]',
763 'iPod' => 'iPod.*CPU[a-z ]+[VER]',
764 //'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'),
765 'Kindle' => 'Kindle/[VER]',
766
767 // Browser
768 'Chrome' => ['Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'],
769 'Coast' => ['Coast/[VER]'],
770 'Dolfin' => 'Dolfin/[VER]',
771 // @reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
772 'Firefox' => ['Firefox/[VER]', 'FxiOS/[VER]'],
773 'Fennec' => 'Fennec/[VER]',
774 // http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
775 // https://msdn.microsoft.com/en-us/library/ie/hh869301(v=vs.85).aspx
776 'Edge' => 'Edge/[VER]',
777 'IE' => ['IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];', 'Trident/[0-9.]+;.*rv:[VER]'],
778 // http://en.wikipedia.org/wiki/NetFront
779 'NetFront' => 'NetFront/[VER]',
780 'NokiaBrowser' => 'NokiaBrowser/[VER]',
781 'Opera' => [' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]'],
782 'Opera Mini' => 'Opera Mini/[VER]',
783 'Opera Mobi' => 'Version/[VER]',
784 'UCBrowser' => ['UCWEB[VER]', 'UC.*Browser/[VER]'],
785 'MQQBrowser' => 'MQQBrowser/[VER]',
786 'MicroMessenger' => 'MicroMessenger/[VER]',
787 'baiduboxapp' => 'baiduboxapp/[VER]',
788 'baidubrowser' => 'baidubrowser/[VER]',
789 'SamsungBrowser' => 'SamsungBrowser/[VER]',
790 'Iron' => 'Iron/[VER]',
791 // @note: Safari 7534.48.3 is actually Version 5.1.
792 // @note: On BlackBerry the Version is overwriten by the OS.
793 'Safari' => ['Version/[VER]', 'Safari/[VER]'],
794 'Skyfire' => 'Skyfire/[VER]',
795 'Tizen' => 'Tizen/[VER]',
796 'Webkit' => 'webkit[ /][VER]',
797 'PaleMoon' => 'PaleMoon/[VER]',
798 'SailfishBrowser' => 'SailfishBrowser/[VER]',
799
800 // Engine
801 'Gecko' => 'Gecko/[VER]',
802 'Trident' => 'Trident/[VER]',
803 'Presto' => 'Presto/[VER]',
804 'Goanna' => 'Goanna/[VER]',
805
806 // OS
807 'iOS' => ' \bi?OS\b [VER][ ;]{1}',
808 'Android' => 'Android [VER]',
809 'Sailfish' => 'Sailfish [VER]',
810 'BlackBerry' => ['BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'],
811 'BREW' => 'BREW [VER]',
812 'Java' => 'Java/[VER]',
813 // @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx
814 // @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases
815 'Windows Phone OS' => ['Windows Phone OS [VER]', 'Windows Phone [VER]'],
816 'Windows Phone' => 'Windows Phone [VER]',
817 'Windows CE' => 'Windows CE/[VER]',
818 // http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd
819 'Windows NT' => 'Windows NT [VER]',
820 'Symbian' => ['SymbianOS/[VER]', 'Symbian/[VER]'],
821 'webOS' => ['webOS/[VER]', 'hpwOS/[VER];'],
822 ];
823
824 /**
825 * Construct an instance of this class.
826 *
827 * @param array|null $headers Specify the headers as injection. Should be PHP _SERVER flavored.
828 * If left empty, will use the global _SERVER['HTTP_*'] vars instead.
829 * @param string|null $userAgent Inject the User-Agent header. If null, will use HTTP_USER_AGENT
830 * from the $headers array instead.
831 */
832 public function __construct(array $headers = null, string $userAgent = null)
833 {
834 $this->setHttpHeaders($headers);
835 $this->setUserAgent($userAgent);
836 }
837
838 /**
839 * Get the current script version.
840 * This is useful for the demo.php file,
841 * so people can check on what version they are testing
842 * for mobile devices.
843 *
844 * @return string The version number in semantic version format.
845 */
846 public static function getScriptVersion(): string
847 {
848 return self::VERSION;
849 }
850
851 /**
852 * Set the HTTP Headers. Must be PHP-flavored. This method will reset existing headers.
853 *
854 * @param array|null $httpHeaders The headers to set. If null, then using PHP's _SERVER to extract
855 * the headers. The default null is left for backwards compatibility.
856 */
857 public function setHttpHeaders(array $httpHeaders = null)
858 {
859 // use global _SERVER if $httpHeaders aren't defined
860 if (!is_array($httpHeaders) || !count($httpHeaders)) {
861 $httpHeaders = $_SERVER;
862 }
863
864 // clear existing headers
865 $this->httpHeaders = array();
866
867 // Only save HTTP headers. In PHP land, that means only _SERVER vars that
868 // start with HTTP_.
869 foreach ($httpHeaders as $key => $value) {
870 if (substr($key, 0, 5) === 'HTTP_') {
871 $this->httpHeaders[$key] = $value;
872 }
873 }
874
875 // In case we're dealing with CloudFront, we need to know.
876 $this->setCfHeaders($httpHeaders);
877 }
878
879 /**
880 * Retrieves the HTTP headers.
881 *
882 * @return array
883 */
884 public function getHttpHeaders(): array
885 {
886 return $this->httpHeaders;
887 }
888
889 /**
890 * Retrieves a particular header. If it doesn't exist, no exception/error is caused.
891 * Simply null is returned.
892 *
893 * @param string $header The name of the header to retrieve. Can be HTTP compliant such as
894 * "User-Agent" or "X-Device-User-Agent" or can be php-esque with the
895 * all-caps, HTTP_ prefixed, underscore separated awesomeness.
896 *
897 * @return string|null The value of the header.
898 */
899 public function getHttpHeader(string $header): ?string
900 {
901 // are we using PHP-flavored headers?
902 if (strpos($header, '_') === false) {
903 $header = str_replace('-', '_', $header);
904 $header = strtoupper($header);
905 }
906
907 // test the alternate, too
908 $altHeader = 'HTTP_' . $header;
909
910 //Test both the regular and the HTTP_ prefix
911 if (isset($this->httpHeaders[$header])) {
912 return $this->httpHeaders[$header];
913 } elseif (isset($this->httpHeaders[$altHeader])) {
914 return $this->httpHeaders[$altHeader];
915 }
916
917 return null;
918 }
919
920 public function getMobileHeaders(): array
921 {
922 return static::$mobileHeaders;
923 }
924
925 /**
926 * Get all possible HTTP headers that
927 * can contain the User-Agent string.
928 *
929 * @return array List of HTTP headers.
930 */
931 public function getUaHttpHeaders(): array
932 {
933 return static::$uaHttpHeaders;
934 }
935
936
937 /**
938 * Set CloudFront headers
939 * http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-device
940 *
941 * @param array|null $cfHeaders List of HTTP headers
942 *
943 * @return bool If there were CloudFront headers to be set
944 */
945 public function setCfHeaders(array $cfHeaders = null): bool
946 {
947 // use global _SERVER if $cfHeaders aren't defined
948 if (!is_array($cfHeaders) || !count($cfHeaders)) {
949 $cfHeaders = $_SERVER;
950 }
951
952 // clear existing headers
953 $this->cloudfrontHeaders = array();
954
955 // Only save CLOUDFRONT headers. In PHP land, that means only _SERVER vars that
956 // start with cloudfront-.
957 $response = false;
958 foreach ($cfHeaders as $key => $value) {
959 if (substr(strtolower($key), 0, 16) === 'http_cloudfront_') {
960 $this->cloudfrontHeaders[strtoupper($key)] = $value;
961 $response = true;
962 }
963 }
964
965 return $response;
966 }
967
968 /**
969 * Retrieves the cloudfront headers.
970 *
971 * @return array
972 */
973 public function getCfHeaders(): array
974 {
975 return $this->cloudfrontHeaders;
976 }
977
978 /**
979 * @param string $userAgent
980 * @return string
981 */
982 private function prepareUserAgent(string $userAgent): string
983 {
984 $userAgent = trim($userAgent);
985 return substr($userAgent, 0, 500);
986 }
987
988 /**
989 * Set the User-Agent to be used.
990 *
991 * @param string|null $userAgent The user agent string to set.
992 *
993 * @return string|null
994 */
995 public function setUserAgent(string $userAgent = null): ?string
996 {
997 // Invalidate cache due to #375
998 $this->cache = array();
999
1000 if (false === empty($userAgent)) {
1001 return $this->userAgent = $this->prepareUserAgent($userAgent);
1002 } else {
1003 $this->userAgent = null;
1004 foreach ($this->getUaHttpHeaders() as $altHeader) {
1005 // @todo: should use getHttpHeader(), but it would be slow. (Serban)
1006 if (false === empty($this->httpHeaders[$altHeader])) {
1007 $this->userAgent .= $this->httpHeaders[$altHeader] . " ";
1008 }
1009 }
1010
1011 if (!empty($this->userAgent)) {
1012 return $this->userAgent = $this->prepareUserAgent($this->userAgent);
1013 }
1014 }
1015
1016 if (count($this->getCfHeaders()) > 0) {
1017 return $this->userAgent = 'Amazon CloudFront';
1018 }
1019 return $this->userAgent = null;
1020 }
1021
1022 /**
1023 * Retrieve the User-Agent.
1024 *
1025 * @return string|null The user agent if it's set.
1026 */
1027 public function getUserAgent(): ?string
1028 {
1029 return $this->userAgent;
1030 }
1031
1032 public function getMatchingRegex(): ?string
1033 {
1034 return $this->matchingRegex;
1035 }
1036
1037 public function getMatchesArray(): ?array
1038 {
1039 return $this->matchesArray;
1040 }
1041
1042 /**
1043 * Retrieve the list of known phone devices.
1044 *
1045 * @return array List of phone devices.
1046 */
1047 public static function getPhoneDevices(): array
1048 {
1049 return static::$phoneDevices;
1050 }
1051
1052 /**
1053 * Retrieve the list of known tablet devices.
1054 *
1055 * @return array List of tablet devices.
1056 */
1057 public static function getTabletDevices(): array
1058 {
1059 return static::$tabletDevices;
1060 }
1061
1062 /**
1063 * Alias for getBrowsers() method.
1064 *
1065 * @return array List of user agents.
1066 */
1067 public static function getUserAgents(): array
1068 {
1069 return static::getBrowsers();
1070 }
1071
1072 /**
1073 * Retrieve the list of known browsers. Specifically, the user agents.
1074 *
1075 * @return array List of browsers / user agents.
1076 */
1077 public static function getBrowsers(): array
1078 {
1079 return static::$browsers;
1080 }
1081
1082 /**
1083 * Method gets the mobile detection rules. This method is used for the magic methods $detect->is*().
1084 * Retrieve the current set of rules.
1085 *
1086 * @return array
1087 */
1088 public function getRules(): array
1089 {
1090 static $rules;
1091
1092 if (!$rules) {
1093 $rules = array_merge(
1094 static::$phoneDevices,
1095 static::$tabletDevices,
1096 static::$operatingSystems,
1097 static::$browsers
1098 );
1099 }
1100
1101 return $rules;
1102 }
1103
1104 /**
1105 * Retrieve the list of mobile operating systems.
1106 *
1107 * @return array The list of mobile operating systems.
1108 */
1109 public static function getOperatingSystems(): array
1110 {
1111 return static::$operatingSystems;
1112 }
1113
1114 /**
1115 * Check the HTTP headers for signs of mobile.
1116 * This is the fastest mobile check possible; it's used
1117 * inside isMobile() method.
1118 *
1119 * @return bool
1120 */
1121 public function checkHttpHeadersForMobile(): bool
1122 {
1123
1124 foreach ($this->getMobileHeaders() as $mobileHeader => $matchType) {
1125 if (isset($this->httpHeaders[$mobileHeader])) {
1126 if (isset($matchType['matches']) && is_array($matchType['matches'])) {
1127 foreach ($matchType['matches'] as $_match) {
1128 if (strpos($this->httpHeaders[$mobileHeader], $_match) !== false) {
1129 return true;
1130 }
1131 }
1132
1133 return false;
1134 } else {
1135 return true;
1136 }
1137 }
1138 }
1139
1140 return false;
1141 }
1142
1143 /**
1144 * Magic overloading method.
1145 *
1146 * @method boolean is[...]()
1147 * @param string $name
1148 * @param array $arguments
1149 * @return bool
1150 * @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is'
1151 */
1152 public function __call(string $name, array $arguments)
1153 {
1154 // make sure the name starts with 'is', otherwise
1155 if (substr($name, 0, 2) !== 'is') {
1156 throw new BadMethodCallException("No such method exists: $name");
1157 }
1158
1159 $key = substr($name, 2);
1160
1161 return $this->matchUAAgainstKey($key);
1162 }
1163
1164 /**
1165 * Find a detection rule that matches the current User-agent.
1166 *
1167 * @param string|null $userAgent deprecated
1168 * @return bool
1169 */
1170 protected function matchDetectionRulesAgainstUA(string $userAgent = null): bool
1171 {
1172 // Begin general search.
1173 foreach ($this->getRules() as $_regex) {
1174 if (empty($_regex)) {
1175 continue;
1176 }
1177
1178 if ($this->match($_regex, $userAgent)) {
1179 return true;
1180 }
1181 }
1182
1183 return false;
1184 }
1185
1186 /**
1187 * Search for a certain key in the rules array.
1188 * If the key is found then try to match the corresponding
1189 * regex against the User-Agent.
1190 *
1191 * @param string $key
1192 *
1193 * @return bool
1194 */
1195 protected function matchUAAgainstKey(string $key): bool
1196 {
1197 // Make the keys lowercase, so we can match: isIphone(), isiPhone(), isiphone(), etc.
1198 $key = strtolower($key);
1199 if (false === isset($this->cache[$key])) {
1200 // change the keys to lower case
1201 $_rules = array_change_key_case($this->getRules());
1202
1203 if (false === empty($_rules[$key])) {
1204 $this->cache[$key] = $this->match($_rules[$key]);
1205 }
1206
1207 if (false === isset($this->cache[$key])) {
1208 $this->cache[$key] = false;
1209 }
1210 }
1211
1212 return $this->cache[$key];
1213 }
1214
1215 /**
1216 * Check if the device is mobile.
1217 * Returns true if any type of mobile device detected, including special ones
1218 * @param string|null $userAgent deprecated
1219 * @param array|null $httpHeaders deprecated
1220 * @return bool
1221 */
1222 public function isMobile(string $userAgent = null, array $httpHeaders = null): bool
1223 {
1224
1225 if ($httpHeaders) {
1226 $this->setHttpHeaders($httpHeaders);
1227 }
1228
1229 if ($userAgent) {
1230 $this->setUserAgent($userAgent);
1231 }
1232
1233 // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
1234 if ($this->getUserAgent() === 'Amazon CloudFront') {
1235 $cfHeaders = $this->getCfHeaders();
1236 if (array_key_exists('HTTP_CLOUDFRONT_IS_MOBILE_VIEWER', $cfHeaders) &&
1237 $cfHeaders['HTTP_CLOUDFRONT_IS_MOBILE_VIEWER'] === 'true'
1238 ) {
1239 return true;
1240 }
1241 }
1242
1243 if ($this->checkHttpHeadersForMobile()) {
1244 return true;
1245 } else {
1246 return $this->matchDetectionRulesAgainstUA();
1247 }
1248 }
1249
1250 /**
1251 * Check if the device is a tablet.
1252 * Return true if any type of tablet device is detected.
1253 *
1254 * @param string|null $userAgent deprecated
1255 * @param array|null $httpHeaders deprecated
1256 * @return bool
1257 */
1258 public function isTablet(string $userAgent = null, array $httpHeaders = null): bool
1259 {
1260 // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
1261 if ($this->getUserAgent() === 'Amazon CloudFront') {
1262 $cfHeaders = $this->getCfHeaders();
1263 if (array_key_exists('HTTP_CLOUDFRONT_IS_TABLET_VIEWER', $cfHeaders) &&
1264 $cfHeaders['HTTP_CLOUDFRONT_IS_TABLET_VIEWER'] === 'true'
1265 ) {
1266 return true;
1267 }
1268 }
1269
1270 foreach (static::$tabletDevices as $_regex) {
1271 if ($this->match($_regex, $userAgent)) {
1272 return true;
1273 }
1274 }
1275
1276 return false;
1277 }
1278
1279 /**
1280 * This method checks for a certain property in the
1281 * userAgent.
1282 * @param string $key
1283 * @param string|null $userAgent deprecated
1284 * @param array|null $httpHeaders deprecated
1285 * @return bool
1286 *
1287 * @todo: The httpHeaders part is not yet used.
1288 */
1289 public function is(string $key, string $userAgent = null, array $httpHeaders = null): bool
1290 {
1291 // Set the UA and HTTP headers only if needed (eg. batch mode).
1292 if ($httpHeaders) {
1293 $this->setHttpHeaders($httpHeaders);
1294 }
1295
1296 if ($userAgent) {
1297 $this->setUserAgent($userAgent);
1298 }
1299
1300 return $this->matchUAAgainstKey($key);
1301 }
1302
1303 /**
1304 * Some detection rules are relative (not standard),
1305 * because of the diversity of devices, vendors and
1306 * their conventions in representing the User-Agent or
1307 * the HTTP headers.
1308 *
1309 * This method will be used to check custom regexes against
1310 * the User-Agent string.
1311 *
1312 * @param string $regex
1313 * @param string|null $userAgent
1314 * @return bool
1315 *
1316 * @todo: search in the HTTP headers too.
1317 */
1318 public function match(string $regex, string $userAgent = null): bool
1319 {
1320 if (!\is_string($userAgent) && !\is_string($this->userAgent)) {
1321 return false;
1322 }
1323
1324 $match = (bool) preg_match(
1325 sprintf('#%s#is', $regex),
1326 (false === empty($userAgent) ? $userAgent : $this->userAgent),
1327 $matches
1328 );
1329 // If positive match is found, store the results for debug.
1330 if ($match) {
1331 $this->matchingRegex = $regex;
1332 $this->matchesArray = $matches;
1333 }
1334
1335 return $match;
1336 }
1337
1338 /**
1339 * Get the properties array.
1340 *
1341 * @return array
1342 */
1343 public static function getProperties(): array
1344 {
1345 return static::$properties;
1346 }
1347
1348 /**
1349 * Prepare the version number.
1350 *
1351 * @param string $ver The string version, like "2.6.21.2152";
1352 *
1353 * @return float
1354 *
1355 * @todo Remove the error suppression from str_replace() call.
1356 */
1357 public function prepareVersionNo(string $ver): float
1358 {
1359 $ver = str_replace(array('_', ' ', '/'), '.', $ver);
1360 $arrVer = explode('.', $ver, 2);
1361
1362 if (isset($arrVer[1])) {
1363 $arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions.
1364 }
1365
1366 return (float) implode('.', $arrVer);
1367 }
1368
1369 /**
1370 * Check the version of the given property in the User-Agent.
1371 * Will return a float number. (e.g. 2_0 will return 2.0, 4.3.1 will return 4.31)
1372 *
1373 * @param string $propertyName The name of the property. See self::getProperties() array
1374 * keys for all possible properties.
1375 * @param string $type Either self::VERSION_TYPE_STRING to get a string value or
1376 * self::VERSION_TYPE_FLOAT indicating a float value. This parameter
1377 * is optional and defaults to self::VERSION_TYPE_STRING. Passing an
1378 * invalid parameter will default to the type as well.
1379 *
1380 * @return string|float|false The version of the property we are trying to extract.
1381 */
1382 public function version(string $propertyName, string $type = self::VERSION_TYPE_STRING)
1383 {
1384 if (empty($propertyName)) {
1385 return false;
1386 }
1387
1388 if (!\is_string($this->userAgent)) {
1389 return false;
1390 }
1391
1392 // set the $type to the default if we don't recognize the type
1393 if ($type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT) {
1394 $type = self::VERSION_TYPE_STRING;
1395 }
1396
1397 $properties = self::getProperties();
1398
1399 // Check if the property exists in the properties array.
1400 if (true === isset($properties[$propertyName])) {
1401 // Prepare the pattern to be matched.
1402 // Make sure we always deal with an array (string is converted).
1403 $properties[$propertyName] = (array) $properties[$propertyName];
1404
1405 foreach ($properties[$propertyName] as $propertyMatchString) {
1406 $propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);
1407
1408 // Identify and extract the version.
1409 preg_match(sprintf('#%s#is', $propertyPattern), $this->userAgent, $match);
1410
1411 if (false === empty($match[1])) {
1412 return ($type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1]);
1413 }
1414 }
1415 }
1416
1417 return false;
1418 }
1419 }
1420