Passed
Push — release-2.1 ( 0c2197...207d2d )
by Jeremy
05:47
created

browser_detector::setupOpera()   A

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 http://www.simplemachines.org
8
 * @copyright 2018 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 4
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
		// Last step ...
97
		$this->setupBrowserPriority();
98
99
		// Now see what you've done!
100
		$context['browser'] = $this->_browsers;
101
	}
102
103
	/**
104
	* Determine if the browser is Opera or not
105
	* @return boolean Whether or not this is Opera
106
	*/
107
	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...
108
	{
109
		if (!isset($this->_browsers['is_opera']))
110
			$this->_browsers['is_opera'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') !== false;
111
		return $this->_browsers['is_opera'];
112
	}
113
114
	/**
115
	* Determine if the browser is IE or not
116
	* @return boolean true Whether or not the browser is IE
117
	*/
118
	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...
119
	{
120
		// I'm IE, Yes I'm the real IE; All you other IEs are just imitating.
121
		if (!isset($this->_browsers['is_ie']))
122
			$this->_browsers['is_ie'] = !$this->isOpera() && !$this->isGecko() && !$this->isWebTv() && preg_match('~MSIE \d+~', $_SERVER['HTTP_USER_AGENT']) === 1;
123
		return $this->_browsers['is_ie'];
124
	}
125
126
	/**
127
	* Determine if the browser is IE11 or not
128
	* @return boolean Whether or not the browser is IE11
129
	*/
130
	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...
131
	{
132
		// IE11 is a bit different than earlier versions
133
		// The isGecko() part is to ensure we get this right...
134
		if (!isset($this->_browsers['is_ie11']))
135
			$this->_browsers['is_ie11'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') !== false && $this->isGecko();
136
		return $this->_browsers['is_ie11'];
137
 	}
138
139
	/**
140
	* Determine if the browser is Edge or not
141
	* @return boolean Whether or not the browser is Edge
142
	*/
143
	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...
144
	{
145
		if (!isset($this->_browsers['is_edge']))
146
			$this->_browsers['is_edge'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Edge') !== false;
147
		return $this->_browsers['is_edge'];
148
	}
149
150
	/**
151
	* Determine if the browser is a Webkit based one or not
152
	* @return boolean Whether or not this is a Webkit-based browser
153
	*/
154
	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...
155
	{
156
		if (!isset($this->_browsers['is_webkit']))
157
			$this->_browsers['is_webkit'] = strpos($_SERVER['HTTP_USER_AGENT'], 'AppleWebKit') !== false;
158
		return $this->_browsers['is_webkit'];
159
	}
160
161
	/**
162
	* Determine if the browser is Firefox or one of its variants
163
	* @return boolean Whether or not this is Firefox (or one of its variants)
164
	*/
165
	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...
166
	{
167
		if (!isset($this->_browsers['is_firefox']))
168
			$this->_browsers['is_firefox'] = preg_match('~(?:Firefox|Ice[wW]easel|IceCat|Shiretoko|Minefield)/~', $_SERVER['HTTP_USER_AGENT']) === 1 && $this->isGecko();
169
		return $this->_browsers['is_firefox'];
170
	}
171
172
	/**
173
	* Determine if the browser is WebTv or not
174
	* @return boolean Whether or not this is WebTV
175
	*/
176
	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...
177
	{
178
		if (!isset($this->_browsers['is_web_tv']))
179
			$this->_browsers['is_web_tv'] = strpos($_SERVER['HTTP_USER_AGENT'], 'WebTV') !== false;
180
		return $this->_browsers['is_web_tv'];
181
	}
182
183
	/**
184
	* Determine if the browser is konqueror or not
185
	* @return boolean Whether or not this is Konqueror
186
	*/
187
	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...
188
	{
189
		if (!isset($this->_browsers['is_konqueror']))
190
			$this->_browsers['is_konqueror'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror') !== false;
191
		return $this->_browsers['is_konqueror'];
192
	}
193
194
	/**
195
	* Determine if the browser is Gecko or not
196
	* @return boolean Whether or not this is a Gecko-based browser
197
	*/
198
	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...
199
	{
200
		if (!isset($this->_browsers['is_gecko']))
201
			$this->_browsers['is_gecko'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Gecko') !== false && !$this->isWebkit() && !$this->isKonqueror();
202
		return $this->_browsers['is_gecko'];
203
	}
204
205
	/**
206
	* Determine if the browser is Opera Mini or not
207
	* @return boolean Whether or not this is Opera Mini
208
	*/
209
	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...
210
	{
211
		if (!isset($this->_browsers['is_opera_mini']))
212
			$this->_browsers['is_opera_mini'] = (isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA']) || stripos($_SERVER['HTTP_USER_AGENT'], 'opera mini') !== false);
213
		if ($this->_browsers['is_opera_mini'])
214
			$this->_is_mobile = true;
215
		return $this->_browsers['is_opera_mini'];
216
	}
217
218
	/**
219
	* Determine if the browser is Opera Mobile or not
220
	* @return boolean Whether or not this is Opera Mobile
221
	*/
222
	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...
223
	{
224
		if (!isset($this->_browsers['is_opera_mobi']))
225
			$this->_browsers['is_opera_mobi'] = stripos($_SERVER['HTTP_USER_AGENT'], 'opera mobi') !== false;
226
		if ($this->_browsers['is_opera_mobi'])
227
			$this->_is_mobile = true;
228
		return $this->_browsers['is_opera_mini'];
229
	}
230
231
	/**
232
	 * Detect Safari / Chrome / iP[ao]d / iPhone / Android / Blackberry from webkit.
233
	 *  - set the browser version for Safari and Chrome
234
	 *  - set the mobile flag for mobile based useragents
235
	 */
236
	private function setupWebkit()
237
	{
238
		$this->_browsers += array(
239
			'is_chrome' => strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') !== false,
240
			'is_iphone' => (strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'iPod') !== false) && strpos($_SERVER['HTTP_USER_AGENT'], 'iPad') === false,
241
			'is_blackberry' => stripos($_SERVER['HTTP_USER_AGENT'], 'BlackBerry') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'PlayBook') !== false,
242
			'is_android' => strpos($_SERVER['HTTP_USER_AGENT'], 'Android') !== false,
243
			'is_nokia' => strpos($_SERVER['HTTP_USER_AGENT'], 'SymbianOS') !== false,
244
		);
245
246
		// blackberry, playbook, iphone, nokia, android and ipods set a mobile flag
247
		if ($this->_browsers['is_iphone'] || $this->_browsers['is_blackberry'] || $this->_browsers['is_android'] || $this->_browsers['is_nokia'])
248
			$this->_is_mobile = true;
249
250
		// @todo what to do with the blaPad? ... for now leave it detected as Safari ...
251
		$this->_browsers['is_safari'] = strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== false && !$this->_browsers['is_chrome'] && !$this->_browsers['is_iphone'];
252
		$this->_browsers['is_ipad'] = strpos($_SERVER['HTTP_USER_AGENT'], 'iPad') !== false;
253
254
		// if Chrome, get the major version
255
		if ($this->_browsers['is_chrome'])
256
		{
257
			if (preg_match('~chrome[/]([0-9][0-9]?[.])~i', $_SERVER['HTTP_USER_AGENT'], $match) === 1)
258
				$this->_browsers['is_chrome' . (int) $match[1]] = true;
259
		}
260
261
		// or if Safari get its major version
262
		if ($this->_browsers['is_safari'])
263
		{
264
			if (preg_match('~version/?(.*)safari.*~i', $_SERVER['HTTP_USER_AGENT'], $match) === 1)
265
				$this->_browsers['is_safari' . (int) trim($match[1])] = true;
266
		}
267
	}
268
269
	/**
270
	 * Additional IE checks and settings.
271
	 *  - determines the version of the IE browser in use
272
	 *  - detects ie4 onward
273
	 *  - attempts to distinguish between IE and IE in compatibility view
274
	 *  - checks for old IE on macs as well, since we can
275
	 */
276
	private function setupIe()
277
	{
278
		$this->_browsers['is_ie_compat_view'] = false;
279
280
		// get the version of the browser from the msie tag
281
		if (preg_match('~MSIE\s?([0-9][0-9]?.[0-9])~i', $_SERVER['HTTP_USER_AGENT'], $msie_match) === 1)
282
		{
283
			$msie_match[1] = trim($msie_match[1]);
284
			$msie_match[1] = (($msie_match[1] - (int) $msie_match[1]) == 0) ? (int) $msie_match[1] : $msie_match[1];
285
			$this->_browsers['is_ie' . $msie_match[1]] = true;
286
		}
287
288
		// "modern" ie uses trident 4=ie8, 5=ie9, 6=ie10, 7=ie11 even in compatibility view
289
		if (preg_match('~Trident/([0-9.])~i', $_SERVER['HTTP_USER_AGENT'], $trident_match) === 1)
290
		{
291
			$this->_browsers['is_ie' . ((int) $trident_match[1] + 4)] = true;
292
293
			// If trident is set, see the (if any) msie tag in the user agent matches ... if not its in some compatibility view
294
			if (isset($msie_match[1]) && ($msie_match[1] < $trident_match[1] + 4))
295
				$this->_browsers['is_ie_compat_view'] = true;
296
		}
297
298
		// Detect true IE6 and IE7 and not IE in compat mode.
299
		$this->_browsers['is_ie7'] = !empty($this->_browsers['is_ie7']) && ($this->_browsers['is_ie_compat_view'] === false);
300
		$this->_browsers['is_ie6'] = !empty($this->_browsers['is_ie6']) && ($this->_browsers['is_ie_compat_view'] === false);
301
302
		// IE mobile 7 or 9, ... shucks why not
303
		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))
304
		{
305
			$this->_browsers['is_ie_mobi'] = true;
306
			$this->_is_mobile = true;
307
		}
308
309
		// And some throwbacks to a bygone era, deposited here like cholesterol in your arteries
310
		$this->_browsers += array(
311
			'is_ie4' => !empty($this->_browsers['is_ie4']) && !$this->_browsers['is_web_tv'],
312
			'is_mac_ie' => strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 5.') !== false && strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') !== false
313
		);
314
315
		// Before IE8 we need to fix IE... lots!
316
		$this->_browsers['ie_standards_fix'] = (($this->_browsers['is_ie6'] === true) || ($this->_browsers['is_ie7'] === true)) ? true : false;
317
318
		// We may even need a size fix...
319
		$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'];
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: $this->_browsers['needs_..._browsers['is_mac_ie']), Probably Intended Meaning: $this->_browsers['needs_..._browsers['is_mac_ie'])
Loading history...
320
	}
321
322
	/**
323
	 * Additional firefox checks.
324
	 * - Gets the version of the FF browser in use
325
	 * - Considers all FF variants as FF including IceWeasel, IceCat, Shiretoko and Minefiled
326
	 */
327
	private function setupFirefox()
328
	{
329
		if (preg_match('~(?:Firefox|Ice[wW]easel|IceCat|Shiretoko|Minefield)[\/ \(]([^ ;\)]+)~', $_SERVER['HTTP_USER_AGENT'], $match) === 1)
330
			$this->_browsers['is_firefox' . (int) $match[1]] = true;
331
	}
332
333
	/**
334
	 * More Opera checks if we are opera.
335
	 *  - checks for the version of Opera in use
336
	 *  - uses checks for 10 first and falls through to <9
337
	 */
338
	private function setupOpera()
339
	{
340
		// Opera 10+ uses the version tag at the end of the string
341
		if (preg_match('~\sVersion/([0-9]+)\.[0-9]+(?:\s*|$)~', $_SERVER['HTTP_USER_AGENT'], $match))
342
			$this->_browsers['is_opera' . (int) $match[1]] = true;
343
		// Opera pre 10 is supposed to uses the Opera tag alone, as do some spoofers
344
		elseif (preg_match('~Opera[ /]([0-9]+)(?!\\.[89])~', $_SERVER['HTTP_USER_AGENT'], $match))
345
			$this->_browsers['is_opera' . (int) $match[1]] = true;
346
347
		// Needs size fix?
348
		$this->_browsers['needs_size_fix'] = !empty($this->_browsers['is_opera6']);
349
	}
350
351
	/**
352
	 * Sets the version number for MS edge.
353
	 */
354
	private function setupEdge()
355
	{
356
		if (preg_match('~Edge[\/]([0-9][0-9]?[\.][0-9][0-9])~i', $_SERVER['HTTP_USER_AGENT'], $match) === 1)
357
			$this->_browsers['is_edge' . (int) $match[1]] = true;
358
	}
359
360
	/**
361
	 * Get the browser name that we will use in the <body id="this_browser">
362
	 *  - The order of each browser in $browser_priority is important
363
	 *  - if you want to have id='ie6' and not id='ie' then it must appear first in the list of ie browsers
364
	 *  - only sets browsers that may need some help via css for compatibility
365
	 */
366
	private function setupBrowserPriority()
367
	{
368
		global $context;
369
370
		if ($this->_is_mobile)
371
			$context['browser_body_id'] = 'mobile';
372
		else
373
		{
374
			// add in any specific detection conversions here if you want a special body id e.g. 'is_opera9' => 'opera9'
375
			$browser_priority = array(
376
				'is_ie6' => 'ie6',
377
				'is_ie7' => 'ie7',
378
				'is_ie8' => 'ie8',
379
				'is_ie9' => 'ie9',
380
				'is_ie10' => 'ie10',
381
				'is_ie11' => 'ie11',
382
				'is_ie' => 'ie',
383
				'is_edge' => 'edge',
384
				'is_firefox' => 'firefox',
385
				'is_chrome' => 'chrome',
386
				'is_safari' => 'safari',
387
				'is_opera10' => 'opera10',
388
				'is_opera11' => 'opera11',
389
				'is_opera12' => 'opera12',
390
				'is_opera' => 'opera',
391
				'is_konqueror' => 'konqueror',
392
			);
393
394
			$context['browser_body_id'] = 'smf';
395
			$active = array_reverse(array_keys($this->_browsers, true));
396
			foreach ($active as $browser)
397
			{
398
				if (array_key_exists($browser, $browser_priority))
399
				{
400
					$context['browser_body_id'] = $browser_priority[$browser];
401
					break;
402
				}
403
			}
404
		}
405
	}
406
407
	/**
408
	 * Fill out the historical array
409
	 *  - needed to support old mods that don't use isBrowser
410
	 */
411
	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...
412
	{
413
		$this->_browsers += array(
414
			'is_opera' => false,
415
			'is_opera6' => false,
416
			'is_opera7' => false,
417
			'is_opera8' => false,
418
			'is_opera9' => false,
419
			'is_opera10' => false,
420
			'is_webkit' => false,
421
			'is_mac_ie' => false,
422
			'is_web_tv' => false,
423
			'is_konqueror' => false,
424
			'is_firefox' => false,
425
			'is_firefox1' => false,
426
			'is_firefox2' => false,
427
			'is_firefox3' => false,
428
			'is_iphone' => false,
429
			'is_android' => false,
430
			'is_chrome' => false,
431
			'is_safari' => false,
432
			'is_gecko'  => false,
433
			'is_edge' => false,
434
			'is_ie8' => false,
435
			'is_ie7' => false,
436
			'is_ie6' => false,
437
			'is_ie5.5' => false,
438
			'is_ie5' => false,
439
			'is_ie' => false,
440
			'is_ie4' => false,
441
			'ie_standards_fix' => false,
442
			'needs_size_fix' => false,
443
			'possibly_robot' => false,
444
		);
445
	}
446
}
447
448
?>