browser_detector::setupOpera()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nop 0
dl 0
loc 11
rs 10
nc 3
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines https://www.simplemachines.org
8
 * @copyright 2022 Simple Machines and individual contributors
9
 * @license https://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1.0
12
 */
13
14
if (!defined('SMF'))
15
	die('No direct access...');
16
17
/**
18
 * Class browser_detector
19
 *  This class is an experiment for the job of correctly detecting browsers and settings needed for them.
20
 * - Detects the following browsers
21
 * - Opera, Webkit, Firefox, Web_tv, Konqueror, IE, Gecko
22
 * - Webkit variants: Chrome, iphone, blackberry, android, safari, ipad, ipod
23
 * - Opera Versions: 6, 7, 8 ... 10 ... and mobile mini and mobi
24
 * - Firefox Versions: 1, 2, 3 .... 11 ...
25
 * - Chrome Versions: 1 ... 18 ...
26
 * - IE Versions: 4, 5, 5.5, 6, 7, 8, 9, 10 ... mobile and Mac
27
 * - MS Edge
28
 * - Nokia
29
 */
30
class browser_detector
31
{
32
	/**
33
	 * @var array Holds all the browser information. Its contents will be placed into $context['browser']
34
	 */
35
	private $_browsers = null;
36
37
	/**
38
	 * @var boolean Whether or not this might be a mobile device
39
	 */
40
	private $_is_mobile = null;
41
42
	/**
43
	 * The main method of this class, you know the one that does the job: detect the thing.
44
	 *  - determines the user agent (browser) as best it can.
45
	 */
46
	function detectBrowser()
47
	{
48
		global $context, $user_info;
49
50
		// Init
51
		$this->_browsers = array();
52
		$this->_is_mobile = false;
53
54
		// Initialize some values we'll set differently if necessary...
55
		$this->_browsers['needs_size_fix'] = false;
56
57
		// One at a time, one at a time, and in this order too
58
		if ($this->isOpera())
59
			$this->setupOpera();
60
		// Meh...
61
		elseif ($this->isEdge())
62
			$this->setupEdge();
63
		// Them webkits need to be set up too
64
		elseif ($this->isWebkit())
65
			$this->setupWebkit();
66
		// We may have work to do on Firefox...
67
		elseif ($this->isFirefox())
68
			$this->setupFirefox();
69
		// Old friend, old frenemy
70
		elseif ($this->isIe())
71
			$this->setupIe();
72
73
		// Just a few mobile checks
74
		$this->isOperaMini();
75
		$this->isOperaMobi();
76
77
		// IE11 seems to be fine by itself without being lumped into the "is_ie" category
78
		$this->isIe11();
79
80
		// Be you robot or human?
81
		if ($user_info['possibly_robot'])
82
		{
83
			// This isn't meant to be reliable, it's just meant to catch most bots to prevent PHPSESSID from showing up.
84
			$this->_browsers['possibly_robot'] = !empty($user_info['possibly_robot']);
85
86
			// Robots shouldn't be logging in or registering.  So, they aren't a bot.  Better to be wrong than sorry (or people won't be able to log in!), anyway.
87
			if ((isset($_REQUEST['action']) && in_array($_REQUEST['action'], array('login', 'login2', 'register', 'signup'))) || !$user_info['is_guest'])
88
				$this->_browsers['possibly_robot'] = false;
89
		}
90
		else
91
			$this->_browsers['possibly_robot'] = false;
92
93
		// Fill out the historical array as needed to support old mods that don't use isBrowser
94
		$this->fillInformation();
95
96
		// Make it easy to check if the browser is on a mobile device.
97
		$this->_browsers['is_mobile'] = $this->_is_mobile;
98
99
		// Last step ...
100
		$this->setupBrowserPriority();
101
102
		// Now see what you've done!
103
		$context['browser'] = $this->_browsers;
104
	}
105
106
	/**
107
	 * Determine if the browser is Opera or not
108
	 *
109
	 * @return boolean Whether or not this is Opera
110
	 */
111
	function isOpera()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
112
	{
113
		if (!isset($this->_browsers['is_opera']))
114
			$this->_browsers['is_opera'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') !== false;
115
		return $this->_browsers['is_opera'];
116
	}
117
118
	/**
119
	 * Determine if the browser is IE or not
120
	 *
121
	 * @return boolean true Whether or not the browser is IE
122
	 */
123
	function isIe()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
124
	{
125
		// I'm IE, Yes I'm the real IE; All you other IEs are just imitating.
126
		if (!isset($this->_browsers['is_ie']))
127
			$this->_browsers['is_ie'] = !$this->isOpera() && !$this->isGecko() && !$this->isWebTv() && preg_match('~MSIE \d+~', $_SERVER['HTTP_USER_AGENT']) === 1;
128
		return $this->_browsers['is_ie'];
129
	}
130
131
	/**
132
	 * Determine if the browser is IE11 or not
133
	 *
134
	 * @return boolean Whether or not the browser is IE11
135
	 */
136
	function isIe11()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
137
	{
138
		// IE11 is a bit different than earlier versions
139
		// The isGecko() part is to ensure we get this right...
140
		if (!isset($this->_browsers['is_ie11']))
141
			$this->_browsers['is_ie11'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') !== false && $this->isGecko();
142
		return $this->_browsers['is_ie11'];
143
	}
144
145
	/**
146
	 * Determine if the browser is Edge or not
147
	 *
148
	 * @return boolean Whether or not the browser is Edge
149
	 */
150
	function isEdge()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
151
	{
152
		if (!isset($this->_browsers['is_edge']))
153
			$this->_browsers['is_edge'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Edge') !== false;
154
		return $this->_browsers['is_edge'];
155
	}
156
157
	/**
158
	 * Determine if the browser is a Webkit based one or not
159
	 *
160
	 * @return boolean Whether or not this is a Webkit-based browser
161
	 */
162
	function isWebkit()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
163
	{
164
		if (!isset($this->_browsers['is_webkit']))
165
			$this->_browsers['is_webkit'] = strpos($_SERVER['HTTP_USER_AGENT'], 'AppleWebKit') !== false;
166
		return $this->_browsers['is_webkit'];
167
	}
168
169
	/**
170
	 * Determine if the browser is Firefox or one of its variants
171
	 *
172
	 * @return boolean Whether or not this is Firefox (or one of its variants)
173
	 */
174
	function isFirefox()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
175
	{
176
		if (!isset($this->_browsers['is_firefox']))
177
			$this->_browsers['is_firefox'] = preg_match('~(?:Firefox|Ice[wW]easel|IceCat|Shiretoko|Minefield)/~', $_SERVER['HTTP_USER_AGENT']) === 1 && $this->isGecko();
178
		return $this->_browsers['is_firefox'];
179
	}
180
181
	/**
182
	 * Determine if the browser is WebTv or not
183
	 *
184
	 * @return boolean Whether or not this is WebTV
185
	 */
186
	function isWebTv()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
187
	{
188
		if (!isset($this->_browsers['is_web_tv']))
189
			$this->_browsers['is_web_tv'] = strpos($_SERVER['HTTP_USER_AGENT'], 'WebTV') !== false;
190
		return $this->_browsers['is_web_tv'];
191
	}
192
193
	/**
194
	 * Determine if the browser is konqueror or not
195
	 *
196
	 * @return boolean Whether or not this is Konqueror
197
	 */
198
	function isKonqueror()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
199
	{
200
		if (!isset($this->_browsers['is_konqueror']))
201
			$this->_browsers['is_konqueror'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror') !== false;
202
		return $this->_browsers['is_konqueror'];
203
	}
204
205
	/**
206
	 * Determine if the browser is Gecko or not
207
	 *
208
	 * @return boolean Whether or not this is a Gecko-based browser
209
	 */
210
	function isGecko()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
211
	{
212
		if (!isset($this->_browsers['is_gecko']))
213
			$this->_browsers['is_gecko'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko') !== false && !$this->isWebkit() && !$this->isKonqueror();
214
		return $this->_browsers['is_gecko'];
215
	}
216
217
	/**
218
	 * Determine if the browser is Opera Mini or not
219
	 *
220
	 * @return boolean Whether or not this is Opera Mini
221
	 */
222
	function isOperaMini()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
223
	{
224
		if (!isset($this->_browsers['is_opera_mini']))
225
			$this->_browsers['is_opera_mini'] = (isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA']) || stripos($_SERVER['HTTP_USER_AGENT'], 'opera mini') !== false);
226
		if ($this->_browsers['is_opera_mini'])
227
			$this->_is_mobile = true;
228
		return $this->_browsers['is_opera_mini'];
229
	}
230
231
	/**
232
	 * Determine if the browser is Opera Mobile or not
233
	 *
234
	 * @return boolean Whether or not this is Opera Mobile
235
	 */
236
	function isOperaMobi()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
237
	{
238
		if (!isset($this->_browsers['is_opera_mobi']))
239
			$this->_browsers['is_opera_mobi'] = stripos($_SERVER['HTTP_USER_AGENT'], 'opera mobi') !== false;
240
		if ($this->_browsers['is_opera_mobi'])
241
			$this->_is_mobile = true;
242
		return $this->_browsers['is_opera_mini'];
243
	}
244
245
	/**
246
	 * Detect Safari / Chrome / iP[ao]d / iPhone / Android / Blackberry from webkit.
247
	 *  - set the browser version for Safari and Chrome
248
	 *  - set the mobile flag for mobile based useragents
249
	 */
250
	private function setupWebkit()
251
	{
252
		$this->_browsers += array(
253
			'is_chrome' => strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') !== false,
254
			'is_iphone' => (strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'iPod') !== false) && strpos($_SERVER['HTTP_USER_AGENT'], 'iPad') === false,
255
			'is_blackberry' => stripos($_SERVER['HTTP_USER_AGENT'], 'BlackBerry') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'PlayBook') !== false,
256
			'is_android' => strpos($_SERVER['HTTP_USER_AGENT'], 'Android') !== false,
257
			'is_nokia' => strpos($_SERVER['HTTP_USER_AGENT'], 'SymbianOS') !== false,
258
		);
259
260
		// blackberry, playbook, iphone, nokia, android and ipods set a mobile flag
261
		if ($this->_browsers['is_iphone'] || $this->_browsers['is_blackberry'] || $this->_browsers['is_android'] || $this->_browsers['is_nokia'])
262
			$this->_is_mobile = true;
263
264
		// @todo what to do with the blaPad? ... for now leave it detected as Safari ...
265
		$this->_browsers['is_safari'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== false && !$this->_browsers['is_chrome'] && !$this->_browsers['is_iphone'];
266
		$this->_browsers['is_ipad'] = strpos($_SERVER['HTTP_USER_AGENT'], 'iPad') !== false;
267
268
		// if Chrome, get the major version
269
		if ($this->_browsers['is_chrome'])
270
		{
271
			if (preg_match('~chrome[/]([0-9][0-9]?[.])~i', $_SERVER['HTTP_USER_AGENT'], $match) === 1)
272
				$this->_browsers['is_chrome' . (int) $match[1]] = true;
273
		}
274
275
		// or if Safari get its major version
276
		if ($this->_browsers['is_safari'])
277
		{
278
			if (preg_match('~version/?(.*)safari.*~i', $_SERVER['HTTP_USER_AGENT'], $match) === 1)
279
				$this->_browsers['is_safari' . (int) trim($match[1])] = true;
280
		}
281
	}
282
283
	/**
284
	 * Additional IE checks and settings.
285
	 *  - determines the version of the IE browser in use
286
	 *  - detects ie4 onward
287
	 *  - attempts to distinguish between IE and IE in compatibility view
288
	 *  - checks for old IE on macs as well, since we can
289
	 */
290
	private function setupIe()
291
	{
292
		$this->_browsers['is_ie_compat_view'] = false;
293
294
		// get the version of the browser from the msie tag
295
		if (preg_match('~MSIE\s?([0-9][0-9]?.[0-9])~i', $_SERVER['HTTP_USER_AGENT'], $msie_match) === 1)
296
		{
297
			$msie_match[1] = trim($msie_match[1]);
298
			$msie_match[1] = (($msie_match[1] - (int) $msie_match[1]) == 0) ? (int) $msie_match[1] : $msie_match[1];
299
			$this->_browsers['is_ie' . $msie_match[1]] = true;
300
		}
301
302
		// "modern" ie uses trident 4=ie8, 5=ie9, 6=ie10, 7=ie11 even in compatibility view
303
		if (preg_match('~Trident/([0-9.])~i', $_SERVER['HTTP_USER_AGENT'], $trident_match) === 1)
304
		{
305
			$this->_browsers['is_ie' . ((int) $trident_match[1] + 4)] = true;
306
307
			// If trident is set, see the (if any) msie tag in the user agent matches ... if not its in some compatibility view
308
			if (isset($msie_match[1]) && ($msie_match[1] < $trident_match[1] + 4))
309
				$this->_browsers['is_ie_compat_view'] = true;
310
		}
311
312
		// Detect true IE6 and IE7 and not IE in compat mode.
313
		$this->_browsers['is_ie7'] = !empty($this->_browsers['is_ie7']) && ($this->_browsers['is_ie_compat_view'] === false);
314
		$this->_browsers['is_ie6'] = !empty($this->_browsers['is_ie6']) && ($this->_browsers['is_ie_compat_view'] === false);
315
316
		// IE mobile 7 or 9, ... shucks why not
317
		if ((!empty($this->_browsers['is_ie7']) && strpos($_SERVER['HTTP_USER_AGENT'], 'IEMobile/7') !== false) || (!empty($this->_browsers['is_ie9']) && strpos($_SERVER['HTTP_USER_AGENT'], 'IEMobile/9') !== false))
318
		{
319
			$this->_browsers['is_ie_mobi'] = true;
320
			$this->_is_mobile = true;
321
		}
322
323
		// And some throwbacks to a bygone era, deposited here like cholesterol in your arteries
324
		$this->_browsers += array(
325
			'is_ie4' => !empty($this->_browsers['is_ie4']) && !$this->_browsers['is_web_tv'],
326
			'is_mac_ie' => strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 5.') !== false && strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') !== false
327
		);
328
329
		// Before IE8 we need to fix IE... lots!
330
		$this->_browsers['ie_standards_fix'] = (($this->_browsers['is_ie6'] === true) || ($this->_browsers['is_ie7'] === true)) ? true : false;
331
332
		// We may even need a size fix...
333
		$this->_browsers['needs_size_fix'] = (!empty($this->_browsers['is_ie5']) || !empty($this->_browsers['is_ie5.5']) || !empty($this->_browsers['is_ie4'])) && !$this->_browsers['is_mac_ie'];
334
	}
335
336
	/**
337
	 * Additional firefox checks.
338
	 * - Gets the version of the FF browser in use
339
	 * - Considers all FF variants as FF including IceWeasel, IceCat, Shiretoko and Minefiled
340
	 */
341
	private function setupFirefox()
342
	{
343
		if (preg_match('~(?:Firefox|Ice[wW]easel|IceCat|Shiretoko|Minefield)[\/ \(]([^ ;\)]+)~', $_SERVER['HTTP_USER_AGENT'], $match) === 1)
344
			$this->_browsers['is_firefox' . (int) $match[1]] = true;
345
	}
346
347
	/**
348
	 * More Opera checks if we are opera.
349
	 *  - checks for the version of Opera in use
350
	 *  - uses checks for 10 first and falls through to <9
351
	 */
352
	private function setupOpera()
353
	{
354
		// Opera 10+ uses the version tag at the end of the string
355
		if (preg_match('~\sVersion/([0-9]+)\.[0-9]+(?:\s*|$)~', $_SERVER['HTTP_USER_AGENT'], $match))
356
			$this->_browsers['is_opera' . (int) $match[1]] = true;
357
		// Opera pre 10 is supposed to uses the Opera tag alone, as do some spoofers
358
		elseif (preg_match('~Opera[ /]([0-9]+)(?!\\.[89])~', $_SERVER['HTTP_USER_AGENT'], $match))
359
			$this->_browsers['is_opera' . (int) $match[1]] = true;
360
361
		// Needs size fix?
362
		$this->_browsers['needs_size_fix'] = !empty($this->_browsers['is_opera6']);
363
	}
364
365
	/**
366
	 * Sets the version number for MS edge.
367
	 */
368
	private function setupEdge()
369
	{
370
		if (preg_match('~Edge[\/]([0-9][0-9]?[\.][0-9][0-9])~i', $_SERVER['HTTP_USER_AGENT'], $match) === 1)
371
			$this->_browsers['is_edge' . (int) $match[1]] = true;
372
	}
373
374
	/**
375
	 * Get the browser name that we will use in the <body id="this_browser">
376
	 *  - The order of each browser in $browser_priority is important
377
	 *  - if you want to have id='ie6' and not id='ie' then it must appear first in the list of ie browsers
378
	 *  - only sets browsers that may need some help via css for compatibility
379
	 */
380
	private function setupBrowserPriority()
381
	{
382
		global $context;
383
384
		if ($this->_is_mobile)
385
			$context['browser_body_id'] = 'mobile';
386
		else
387
		{
388
			// add in any specific detection conversions here if you want a special body id e.g. 'is_opera9' => 'opera9'
389
			$browser_priority = array(
390
				'is_ie6' => 'ie6',
391
				'is_ie7' => 'ie7',
392
				'is_ie8' => 'ie8',
393
				'is_ie9' => 'ie9',
394
				'is_ie10' => 'ie10',
395
				'is_ie11' => 'ie11',
396
				'is_ie' => 'ie',
397
				'is_edge' => 'edge',
398
				'is_firefox' => 'firefox',
399
				'is_chrome' => 'chrome',
400
				'is_safari' => 'safari',
401
				'is_opera10' => 'opera10',
402
				'is_opera11' => 'opera11',
403
				'is_opera12' => 'opera12',
404
				'is_opera' => 'opera',
405
				'is_konqueror' => 'konqueror',
406
			);
407
408
			$context['browser_body_id'] = 'smf';
409
			$active = array_reverse(array_keys($this->_browsers, true));
410
			foreach ($active as $browser)
411
			{
412
				if (array_key_exists($browser, $browser_priority))
413
				{
414
					$context['browser_body_id'] = $browser_priority[$browser];
415
					break;
416
				}
417
			}
418
		}
419
	}
420
421
	/**
422
	 * Fill out the historical array
423
	 *  - needed to support old mods that don't use isBrowser
424
	 */
425
	function fillInformation()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
426
	{
427
		$this->_browsers += array(
428
			'is_opera' => false,
429
			'is_opera6' => false,
430
			'is_opera7' => false,
431
			'is_opera8' => false,
432
			'is_opera9' => false,
433
			'is_opera10' => false,
434
			'is_webkit' => false,
435
			'is_mac_ie' => false,
436
			'is_web_tv' => false,
437
			'is_konqueror' => false,
438
			'is_firefox' => false,
439
			'is_firefox1' => false,
440
			'is_firefox2' => false,
441
			'is_firefox3' => false,
442
			'is_iphone' => false,
443
			'is_android' => false,
444
			'is_chrome' => false,
445
			'is_safari' => false,
446
			'is_gecko' => false,
447
			'is_edge' => false,
448
			'is_ie8' => false,
449
			'is_ie7' => false,
450
			'is_ie6' => false,
451
			'is_ie5.5' => false,
452
			'is_ie5' => false,
453
			'is_ie' => false,
454
			'is_ie4' => false,
455
			'ie_standards_fix' => false,
456
			'needs_size_fix' => false,
457
			'possibly_robot' => false,
458
		);
459
	}
460
}
461
462
?>