BrowserDetection   F
last analyzed

Complexity

Total Complexity 263

Size/Duplication

Total Lines 1288
Duplicated Lines 12.11 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 156
loc 1288
ccs 0
cts 802
cp 0
rs 0.8
c 0
b 0
f 0
wmc 263
lcom 1
cbo 0

61 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B __toString() 0 23 6
B compareVersions() 6 32 10
A getAolVersion() 0 4 1
A getBrowser() 0 4 1
A getIECompatibilityView() 0 8 2
A getPlatform() 0 4 1
A getUserAgent() 0 4 1
A getVersion() 0 4 1
A isAol() 0 4 1
A isChromeFrame() 0 4 1
A isInIECompatibilityView() 0 4 2
A isMobile() 0 4 1
A isRobot() 0 4 1
A setUserAgent() 0 13 5
A checkBrowserAmaya() 0 4 1
A checkBrowserAndroid() 0 5 1
A checkBrowserBingbot() 0 4 1
B checkBrowserBlackBerry() 14 25 6
A checkBrowserChrome() 0 4 1
A checkBrowserFirebird() 0 4 1
A checkBrowserFirefox() 0 13 3
A checkBrowserGaleon() 0 4 1
A checkBrowserGooglebot() 0 4 1
A checkBrowserIcab() 0 5 1
A checkBrowserIceCat() 0 4 1
A checkBrowserIceWeasel() 0 4 1
D checkBrowserInternetExplorer() 0 77 24
A checkBrowserKonqueror() 0 4 1
A checkBrowserLynx() 0 4 1
A checkBrowserMozilla() 0 4 1
A checkBrowserMsnBot() 0 4 1
A checkBrowserMsnTv() 0 4 1
A checkBrowserNetPositive() 0 4 1
C checkBrowserNetscape() 6 40 12
A checkBrowserNokia() 0 14 5
A checkBrowserOmniWeb() 0 11 4
B checkBrowserOpera() 0 25 11
A checkBrowserPhoenix() 0 4 1
D checkBrowsers() 0 40 28
B checkBrowserSafari() 18 29 10
A checkBrowserSlurp() 0 4 1
A checkBrowserUAWithVersion() 4 22 5
B checkBrowserW3CValidator() 18 27 7
A checkBrowserYahooMultimedia() 0 4 1
A checkForAol() 4 19 3
D checkPlatform() 0 62 25
B checkSimpleBrowserUA() 4 26 6
A detect() 0 6 1
A cleanVersion() 0 14 2
A parseInt() 0 4 1
A reset() 0 13 1
D safariBuildToSafariVer() 40 78 23
A setAol() 0 4 1
A setAolVersion() 0 5 1
A setBrowser() 0 4 1
A setMobile() 0 4 1
A setPlatform() 0 4 1
A setRobot() 0 4 1
A setVersion() 0 9 2
D webKitBuildToSafariVer() 42 84 24

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like BrowserDetection often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BrowserDetection, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Browser detection class file.
4
 * This file contains everything required to use the BrowserDetection class.
5
 *
6
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
7
 * Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any
8
 * later version (if any).
9
 *
10
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
11
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12
 * details at: http://www.gnu.org/licenses/lgpl.html
13
 *
14
 * @package BrowserDetection
15
 * @version 2.1.1
16
 * @last-modified September 2, 2014
17
 * @author Alexandre Valiquette
18
 * @copyright Copyright (c) 2014, Wolfcast
19
 * @link http://wolfcast.com/
20
 */
21
/**
22
 * The BrowserDetection class facilitates the identification of the user's environment such as Web browser, version,
23
 * platform or if it's a mobile device.
24
 *
25
 * Typical usage:
26
 *
27
 * $browser = new BrowserDetection();
28
 * if ($browser->getBrowser() == BrowserDetection::BROWSER_FIREFOX && $browser->getVersion() >= 5) {
29
 *     echo 'You have FireFox version 5 or greater.';
30
 * }
31
 *
32
 * The class is an updated version of Chris Schuld's Browser class version 1.9 which is unmaintained since August 20th,
33
 * 2010. Chris' class was based on the original work from Gary White.
34
 *
35
 * Updates:
36
 *
37
 * 2014-07-11:
38
 *  + Version 2.1.1. Better detection of mobile devices and platforms.
39
 *
40
 * 2014-06-04:
41
 *  + Version 2.1. Added IE 11+ support.
42
 *
43
 * 2013-05-27:
44
 *  + Version 2.0 is (almost) a complete rewrite based on Chris Schuld's Browser class version 1.9 plus changes below
45
 *  + Added support for Opera Mobile
46
 *  + Added support for the Windows Phone (formerly Windows Mobile) platform
47
 *  + Added support for BlackBerry Tablet OS and BlackBerry 10
48
 *  + Added support for the Symbian platform
49
 *  + Added support for Bingbot
50
 *  + Added support for the Yahoo! Multimedia crawler
51
 *  + Removed iPhone/iPad/iPod browsers since there are not browsers but platforms - test them with getPlatform()
52
 *  + Removed support for Shiretoko (Firefox 3.5 alpha/beta) and MSN Browser
53
 *  + Merged Nokia and Nokia S60
54
 *  + Updated some deprecated browser names
55
 *  + Many public methods are now protected
56
 *  + Documentation updated
57
 *
58
 * 2010-07-04:
59
 *  + Added detection of IE compatibility view - test with getIECompatibilityView()
60
 *  + Added support for all (deprecated) Netscape versions
61
 *  + Added support for Safari < 3.0
62
 *  + Better Firefox version parsing
63
 *  + Better Opera version parsing
64
 *  + Better Mozilla detection
65
 *
66
 * @package BrowserDetection
67
 * @version 2.1.1
68
 * @last-modified September 2, 2014
69
 * @author Alexandre Valiquette, Chris Schuld, Gary White
70
 * @copyright Copyright (c) 2014, Wolfcast
71
 * @license http://www.gnu.org/licenses/lgpl.html
72
 * @link http://wolfcast.com/
73
 * @link http://chrisschuld.com/
74
 * @link http://www.apptools.com/phptools/browser/
75
 */
76
class BrowserDetection
77
{
78
    const BROWSER_AMAYA = 'Amaya';
79
    const BROWSER_ANDROID = 'Android';
80
    const BROWSER_BINGBOT = 'Bingbot';
81
    const BROWSER_BLACKBERRY = 'BlackBerry';
82
    const BROWSER_CHROME = 'Chrome';
83
    const BROWSER_FIREBIRD = 'Firebird';
84
    const BROWSER_FIREFOX = 'Firefox';
85
    const BROWSER_GALEON = 'Galeon';
86
    const BROWSER_GOOGLEBOT = 'Googlebot';
87
    const BROWSER_ICAB = 'iCab';
88
    const BROWSER_ICECAT = 'GNU IceCat';
89
    const BROWSER_ICEWEASEL = 'GNU IceWeasel';
90
    const BROWSER_IE = 'Internet Explorer';
91
    const BROWSER_IE_MOBILE = 'Internet Explorer Mobile';
92
    const BROWSER_KONQUEROR = 'Konqueror';
93
    const BROWSER_LYNX = 'Lynx';
94
    const BROWSER_MOZILLA = 'Mozilla';
95
    const BROWSER_MSNBOT = 'MSNBot';
96
    const BROWSER_MSNTV = 'MSN TV';
97
    const BROWSER_NETPOSITIVE = 'NetPositive';
98
    const BROWSER_NETSCAPE = 'Netscape';
99
    const BROWSER_NOKIA = 'Nokia Browser';
100
    const BROWSER_OMNIWEB = 'OmniWeb';
101
    const BROWSER_OPERA = 'Opera';
102
    const BROWSER_OPERA_MINI = 'Opera Mini';
103
    const BROWSER_OPERA_MOBILE = 'Opera Mobile';
104
    const BROWSER_PHOENIX = 'Phoenix';
105
    const BROWSER_SAFARI = 'Safari';
106
    const BROWSER_SLURP = 'Yahoo! Slurp';
107
    const BROWSER_TABLET_OS = 'BlackBerry Tablet OS';
108
    const BROWSER_UNKNOWN = 'unknown';
109
    const BROWSER_W3CVALIDATOR = 'W3C Validator';
110
    const BROWSER_YAHOO_MM = 'Yahoo! Multimedia';
111
    const PLATFORM_ANDROID = 'Android';
112
    const PLATFORM_BEOS = 'BeOS';
113
    const PLATFORM_BLACKBERRY = 'BlackBerry';
114
    const PLATFORM_FREEBSD = 'FreeBSD';
115
    const PLATFORM_IPAD = 'iPad';
116
    const PLATFORM_IPHONE = 'iPhone';
117
    const PLATFORM_IPOD = 'iPod';
118
    const PLATFORM_LINUX = 'Linux';
119
    const PLATFORM_MACINTOSH = 'Macintosh';
120
    const PLATFORM_NETBSD = 'NetBSD';
121
    const PLATFORM_NOKIA = 'Nokia';
122
    const PLATFORM_OPENBSD = 'OpenBSD';
123
    const PLATFORM_OPENSOLARIS = 'OpenSolaris';
124
    const PLATFORM_OS2 = 'OS/2';
125
    const PLATFORM_SUNOS = 'SunOS';
126
    const PLATFORM_SYMBIAN = 'Symbian';
127
    const PLATFORM_UNKNOWN = 'unknown';
128
    const PLATFORM_WINDOWS = 'Windows';
129
    const PLATFORM_WINDOWS_CE = 'Windows CE';
130
    const PLATFORM_WINDOWS_PHONE = 'Windows Phone';
131
    const VERSION_UNKNOWN = 'unknown';
132
    private $_agent = '';
133
    private $_aolVersion = '';
134
    private $_browserName = '';
135
    private $_compatibilityViewName = '';
136
    private $_compatibilityViewVer = '';
137
    private $_isAol = false;
138
    private $_isMobile = false;
139
    private $_isRobot = false;
140
    private $_platform = '';
141
    private $_version = '';
142
    //--- MAGIC METHODS ------------------------------------------------------------------------------------------------
143
    /**
144
     * BrowserDetection class constructor.
145
     * @param string $useragent The user agent to work with. Leave empty for the current user agent (contained in
146
     * $_SERVER['HTTP_USER_AGENT']).
147
     */
148
    public function __construct($useragent = '')
149
    {
150
        $this->setUserAgent($useragent);
151
    }
152
    /**
153
     * Determine how the class will react when it is treated like a string.
154
     * @return string Returns an HTML formatted string with a summary of the browser informations.
155
     */
156
    public function __toString()
157
    {
158
        $result = '<strong>Browser name:</strong> ' . $this->getBrowser() . '<br />' . PHP_EOL .
159
                '<strong>Browser version:</strong> ' . $this->getVersion() . '<br />' . PHP_EOL;
160
        if ($this->isInIECompatibilityView()) {
161
            $result .= '<strong>Compatibility view mode:</strong> ' . $this->getIECompatibilityView() . '<br />' . PHP_EOL;
162
        }
163
        if ($this->isAol()) {
164
            $result .= '<strong>AOL version:</strong> ' . $this->getAolVersion() . '<br />' . PHP_EOL;
165
        }
166
        if ($this->isChromeFrame()) {
167
            $result .= '<strong>Is Google Chrome Frame:</strong> Yes<br />' . PHP_EOL;
168
        }
169
        $result .= '<strong>Platform:</strong> ' . $this->getPlatform() . '<br />' . PHP_EOL;
170
        if ($this->isRobot()) {
171
            $result .= '<strong>Is a robot:</strong> Yes<br />' . PHP_EOL;
172
        }
173
        if ($this->isMobile()) {
174
            $result .= '<strong>Is on a mobile device:</strong> Yes<br />' . PHP_EOL;
175
        }
176
        $result .= '<strong>Browser user agent:</strong> ' . $this->getUserAgent() . '<br />' . PHP_EOL;
177
        return $result;
178
    }
179
    //--- PUBLIC MEMBERS -----------------------------------------------------------------------------------------------
180
    /**
181
     * Compare two version number strings.
182
     * @param string $sourceVer The source version number.
183
     * @param string $compareVer The version number to compare with the source version number.
184
     * @return int Returns 1 if $sourceVer < $compareVer, 0 if $sourceVer == $compareVer or -1 if $sourceVer > $compareVer.
185
     */
186
    public function compareVersions($sourceVer, $compareVer)
187
    {
188
        $sourceVer = explode('.', $sourceVer);
189
        foreach ($sourceVer as $k => $v) {
190
            $sourceVer[$k] = $this->parseInt($v);
191
        }
192
        $compareVer = explode('.', $compareVer);
193
        foreach ($compareVer as $k => $v) {
194
            $compareVer[$k] = $this->parseInt($v);
195
        }
196
        if (count($sourceVer) != count($compareVer)) {
197
            if (count($sourceVer) > count($compareVer)) {
198 View Code Duplication
                for ($i = count($compareVer); $i < count($sourceVer); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
199
                    $compareVer[$i] = 0;
200
                }
201
            } else {
202 View Code Duplication
                for ($i = count($sourceVer); $i < count($compareVer); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
203
                    $sourceVer[$i] = 0;
204
                }
205
            }
206
        }
207
        foreach ($sourceVer as $i => $srcVerPart) {
208
            if ($srcVerPart > $compareVer[$i]) {
209
                return -1;
210
            } else {
211
                if ($srcVerPart < $compareVer[$i]) {
212
                    return 1;
213
                }
214
            }
215
        }
216
        return 0;
217
    }
218
    /**
219
     * Get the version of AOL (if any). AOL releases "optimized" Internet Explorer and Firefox versions. In the making
220
     * they add their version number in the user agent string of these browsers.
221
     * @return string Returns the version of AOL or an empty string if no AOL version was found.
222
     */
223
    public function getAolVersion()
224
    {
225
        return $this->_aolVersion;
226
    }
227
    /**
228
     * Get the name of the browser. All of the return values are class constants. You can compare them like this:
229
     * $myBrowserInstance->getBrowser() == BrowserDetection::BROWSER_FIREFOX.
230
     * @return string Returns the name of the browser.
231
     */
232
    public function getBrowser()
233
    {
234
        return $this->_browserName;
235
    }
236
    /**
237
     * Get the name and version of the browser emulated in the compatibility view mode (if any). Since Internet
238
     * Explorer 8, IE can be put in compatibility mode to make websites that were created for older browsers, especially
239
     * IE 6 and 7, look better in IE 8+ which renders web pages closer to the standards and thus differently from those
240
     * older versions of IE.
241
     * @param bool $asArray Determines if the return value must be an array (true) or a string (false).
242
     * @return mixed If a string was requested, the function returns the name and version of the browser emulated in the
243
     * compatibility view mode or an empty string if the browser is not in compatibility view mode. If an array was
244
     * requested, an array with the keys 'browser' and 'version' is returned.
245
     */
246
    public function getIECompatibilityView($asArray = false)
247
    {
248
        if ($asArray) {
249
            return array('browser' => $this->_compatibilityViewName, 'version' => $this->_compatibilityViewVer);
250
        } else {
251
            return trim($this->_compatibilityViewName . ' ' . $this->_compatibilityViewVer);
252
        }
253
    }
254
    /**
255
     * Get the name of the platform on which the browser is run on (such as Windows, Apple, iPhone, etc.). All of the
256
     * return values are class constants. You can compare them like this:
257
     * $myBrowserInstance->getPlatform() == BrowserDetection::PLATFORM_ANDROID.
258
     * @return string Returns the name of the platform or BrowserDetection::PLATFORM_UNKNOWN if unknown.
259
     */
260
    public function getPlatform()
261
    {
262
        return $this->_platform;
263
    }
264
    /**
265
     * Get the user agent value used by the class to determine the browser details.
266
     * @return string The user agent string.
267
     */
268
    public function getUserAgent()
269
    {
270
        return $this->_agent;
271
    }
272
    /**
273
     * Get the version of the browser.
274
     * @return string Returns the version of the browser or BrowserDetection::VERSION_UNKNOWN if unknown.
275
     */
276
    public function getVersion()
277
    {
278
        return $this->_version;
279
    }
280
    /**
281
     * Determine if the browser is from AOL. AOL releases "optimized" Internet Explorer and Firefox versions. In the
282
     * making they add their details in the user agent string of these browsers.
283
     * @return boolean Returns true if the browser is from AOL, false otherwise.
284
     */
285
    public function isAol()
286
    {
287
        return $this->_isAol;
288
    }
289
    /**
290
     * Determine if the browser runs Google Chrome Frame (it's a plug-in designed for Internet Explorer 6+ based on the
291
     * open-source Chromium project - it's like a Chrome browser within IE).
292
     * @return boolean Returns true if the browser is using Google Chrome Frame, false otherwise.
293
     */
294
    public function isChromeFrame()
295
    {
296
        return stripos($this->_agent, 'chromeframe') !== false;
297
    }
298
    /**
299
     * Determine if the browser is in compatibility view or not. Since Internet Explorer 8, IE can be put in
300
     * compatibility mode to make websites that were created for older browsers, especially IE 6 and 7, look better in
301
     * IE 8+ which renders web pages closer to the standards and thus differently from those older versions of IE.
302
     * @return boolean Returns true if the browser is in compatibility view, false otherwise.
303
     */
304
    public function isInIECompatibilityView()
305
    {
306
        return ($this->_compatibilityViewName != '') || ($this->_compatibilityViewVer != '');
307
    }
308
    /**
309
     * Determine if the browser is from a mobile device or not.
310
     * @return boolean Returns true if the browser is from a mobile device, false otherwise.
311
     */
312
    public function isMobile()
313
    {
314
        return $this->_isMobile;
315
    }
316
    /**
317
     * Determine if the browser is a robot (Googlebot, Bingbot, Yahoo! Slurp...) or not.
318
     * @return boolean Returns true if the browser is a robot, false otherwise.
319
     */
320
    public function isRobot()
321
    {
322
        return $this->_isRobot;
323
    }
324
    /**
325
     * Set the user agent to use with the class.
326
     * @param string $agentString The value of the user agent. If an empty string is sent (default),
327
     * $_SERVER['HTTP_USER_AGENT'] will be used.
328
     */
329
    public function setUserAgent($agentString = '')
330
    {
331
        if (!is_string($agentString) || trim($agentString) == '') {
332
            if (array_key_exists('HTTP_USER_AGENT', $_SERVER) && is_string($_SERVER['HTTP_USER_AGENT'])) {
333
                $agentString = $_SERVER['HTTP_USER_AGENT'];
334
            } else {
335
                $agentString = '';
336
            }
337
        }
338
        $this->reset();
339
        $this->_agent = $agentString;
340
        $this->detect();
341
    }
342
    //--- PROTECTED MEMBERS --------------------------------------------------------------------------------------------
343
    /**
344
     * Determine if the browser is the Amaya Web editor or not.
345
     * @access protected
346
     * @link http://www.w3.org/Amaya/
347
     * @return boolean Returns true if the browser is Amaya, false otherwise.
348
     */
349
    protected function checkBrowserAmaya()
350
    {
351
        return $this->checkSimpleBrowserUA('amaya', $this->_agent, self::BROWSER_AMAYA);
352
    }
353
    /**
354
     * Determine if the browser is the Android browser (based on the WebKit layout engine and coupled with Chrome's
355
     * JavaScript engine) or not.
356
     * @access protected
357
     * @return boolean Returns true if the browser is the Android browser, false otherwise.
358
     */
359
    protected function checkBrowserAndroid()
360
    {
361
        //Android don't use the standard "Android/1.0", it uses "Android 1.0;" instead
362
        return $this->checkSimpleBrowserUA('Android', $this->_agent, self::BROWSER_ANDROID, true);
363
    }
364
    /**
365
     * Determine if the browser is the Bingbot crawler or not.
366
     * @access protected
367
     * @link http://www.bing.com/webmaster/help/which-crawlers-does-bing-use-8c184ec0
368
     * @return boolean Returns true if the browser is Bingbot, false otherwise.
369
     */
370
    protected function checkBrowserBingbot()
371
    {
372
        return $this->checkSimpleBrowserUA('bingbot', $this->_agent, self::BROWSER_BINGBOT, false, true);
373
    }
374
    /**
375
     * Determine if the browser is the BlackBerry browser or not.
376
     * @access protected
377
     * @link http://supportforums.blackberry.com/t5/Web-and-WebWorks-Development/How-to-detect-the-BlackBerry-Browser/ta-p/559862
378
     * @return boolean Returns true if the browser is the BlackBerry browser, false otherwise.
379
     */
380
    protected function checkBrowserBlackBerry()
381
    {
382
        $found = false;
383
        //Tablet OS check
384
        if ($this->checkSimpleBrowserUA('RIM Tablet OS', $this->_agent, self::BROWSER_TABLET_OS, true)) {
385
            return true;
386
        }
387
        //Version 6, 7 & 10 check (versions 8 & 9 does not exists)
388 View Code Duplication
        if ($this->checkBrowserUAWithVersion(array('BlackBerry', 'BB10'), $this->_agent, self::BROWSER_BLACKBERRY, true)) {
389
            if ($this->getVersion() == self::VERSION_UNKNOWN) {
390
                $found = true;
391
            } else {
392
                return true;
393
            }
394
        }
395
        //Version 4.2 to 5.0 check
396 View Code Duplication
        if ($this->checkSimpleBrowserUA('BlackBerry', $this->_agent, self::BROWSER_BLACKBERRY, true)) {
397
            if ($this->getVersion() == self::VERSION_UNKNOWN) {
398
                $found = true;
399
            } else {
400
                return true;
401
            }
402
        }
403
        return $found;
404
    }
405
    /**
406
     * Determine if the browser is Chrome or not.
407
     * @access protected
408
     * @link http://www.google.com/chrome/
409
     * @return boolean Returns true if the browser is Chrome, false otherwise.
410
     */
411
    protected function checkBrowserChrome()
412
    {
413
        return $this->checkSimpleBrowserUA('Chrome', $this->_agent, self::BROWSER_CHROME);
414
    }
415
    /**
416
     * Determine if the browser is Firebird or not. Firebird was the name of Firefox from version 0.6 to 0.7.1.
417
     * @access protected
418
     * @return boolean Returns true if the browser is Firebird, false otherwise.
419
     */
420
    protected function checkBrowserFirebird()
421
    {
422
        return $this->checkSimpleBrowserUA('Firebird', $this->_agent, self::BROWSER_FIREBIRD);
423
    }
424
    /**
425
     * Determine if the browser is Firefox or not.
426
     * @access protected
427
     * @link http://www.mozilla.org/en-US/firefox/new/
428
     * @return boolean Returns true if the browser is Firefox, false otherwise.
429
     */
430
    protected function checkBrowserFirefox()
431
    {
432
        //Safari heavily matches with Firefox, ensure that Safari is filtered out...
433
        if (preg_match('/.*Firefox[ (\/]*([a-z0-9.-]*)/i', $this->_agent, $matches) &&
434
                stripos($this->_agent, 'Safari') === false) {
435
            $this->setBrowser(self::BROWSER_FIREFOX);
436
            $this->setVersion($matches[1]);
437
            $this->setMobile(false);
438
            $this->setRobot(false);
439
            return true;
440
        }
441
        return false;
442
    }
443
    /**
444
     * Determine if the browser is Galeon or not. The browser was discontinued on September 27, 2008.
445
     * @access protected
446
     * @link http://en.wikipedia.org/wiki/Galeon
447
     * @return boolean Returns true if the browser is Galeon, false otherwise.
448
     */
449
    protected function checkBrowserGaleon()
450
    {
451
        return $this->checkSimpleBrowserUA('Galeon', $this->_agent, self::BROWSER_GALEON);
452
    }
453
    /**
454
     * Determine if the browser is the Googlebot crawler or not.
455
     * @access protected
456
     * @return boolean Returns true if the browser is Googlebot, false otherwise.
457
     */
458
    protected function checkBrowserGooglebot()
459
    {
460
        return $this->checkSimpleBrowserUA('Googlebot', $this->_agent, self::BROWSER_GOOGLEBOT, false, true);
461
    }
462
    /**
463
     * Determine if the browser is iCab or not.
464
     * @access protected
465
     * @link http://www.icab.de/
466
     * @return boolean Returns true if the browser is iCab, false otherwise.
467
     */
468
    protected function checkBrowserIcab()
469
    {
470
        //Some (early) iCab versions don't use the standard "iCab/1.0", they uses "iCab 1.0;" instead
471
        return $this->checkSimpleBrowserUA('iCab', $this->_agent, self::BROWSER_ICAB);
472
    }
473
    /**
474
     * Determine if the browser is GNU IceCat (formerly known as GNU IceWeasel) or not.
475
     * @access protected
476
     * @link http://www.gnu.org/software/gnuzilla/
477
     * @return boolean Returns true if the browser is GNU IceCat, false otherwise.
478
     */
479
    protected function checkBrowserIceCat()
480
    {
481
        return $this->checkSimpleBrowserUA('IceCat', $this->_agent, self::BROWSER_ICECAT);
482
    }
483
    /**
484
     * Determine if the browser is GNU IceWeasel (now know as GNU IceCat) or not.
485
     * @access protected
486
     * @see checkBrowserIceCat()
487
     * @return boolean Returns true if the browser is GNU IceWeasel, false otherwise.
488
     */
489
    protected function checkBrowserIceWeasel()
490
    {
491
        return $this->checkSimpleBrowserUA('Iceweasel', $this->_agent, self::BROWSER_ICEWEASEL);
492
    }
493
    /**
494
     * Determine if the browser is Internet Explorer or not.
495
     * @access protected
496
     * @link http://www.microsoft.com/ie/
497
     * @link http://en.wikipedia.org/wiki/Internet_Explorer_Mobile
498
     * @return boolean Returns true if the browser is Internet Explorer, false otherwise.
499
     */
500
    protected function checkBrowserInternetExplorer()
501
    {
502
        //Test for Internet Explorer Mobile (formerly Pocket Internet Explorer)
503
        if ($this->checkSimpleBrowserUA(array('IEMobile', 'MSPIE'), $this->_agent, self::BROWSER_IE_MOBILE, true)) {
504
            return true;
505
        }
506
        //Several browsers uses IE compatibility UAs filter these browsers out (but after testing for IE Mobile)
507
        if (stripos($this->_agent, 'Opera') !== false ||
508
                stripos($this->_agent, 'BlackBerry') !== false ||
509
                stripos($this->_agent, 'Nokia') !== false) {
510
            return false;
511
        }
512
        //Test for Internet Explorer 1
513
        if ($this->checkSimpleBrowserUA('Microsoft Internet Explorer', $this->_agent, self::BROWSER_IE)) {
514
            if ($this->getVersion() == self::VERSION_UNKNOWN) {
515
                if (preg_match('/308|425|426|474|0b1/i', $this->_agent)) {
516
                    $this->setVersion('1.5');
517
                } else {
518
                    $this->setVersion('1.0');
519
                }
520
            }
521
            return true;
522
        }
523
        //Test for Internet Explorer 2+
524
        if (stripos($this->_agent, 'MSIE') !== false || stripos($this->_agent, 'Trident') !== false) {
525
            $version = '';
526
            if (stripos($this->_agent, 'Trident') !== false) {
527
                //Test for Internet Explorer 11+ (check the rv: string)
528
                if (stripos($this->_agent, 'rv:') !== false) {
529
                    if ($this->checkSimpleBrowserUA('Trident', $this->_agent, self::BROWSER_IE, false, false, 'rv:')) {
530
                        return true;
531
                    }
532
                } else {
533
                    //Test for Internet Explorer 8, 9 & 10 (check the Trident string)
534
                    if (preg_match('/Trident\/([\d]+)/i', $this->_agent, $foundVersion)) {
535
                        //Trident started with version 4.0 on IE 8
536
                        $verFromTrident = $this->parseInt($foundVersion[1]) + 4;
537
                        if ($verFromTrident >= 8) {
538
                            $version = $verFromTrident . '.0';
539
                        }
540
                    }
541
                }
542
                //If we have the IE version from Trident, we can check for the compatibility view mode
543
                if ($version != '') {
544
                    $emulatedVer = '';
545
                    preg_match_all('/MSIE\s*([^\s;$]+)/i', $this->_agent, $foundVersions);
546
                    foreach ($foundVersions[1] as $currVer) {
547
                        //Keep the lowest MSIE version for the emulated version (in compatibility view mode)
548
                        if ($emulatedVer == '' || $this->compareVersions($emulatedVer, $currVer) == -1) {
549
                            $emulatedVer = $currVer;
550
                        }
551
                    }
552
                    //Set the compatibility view mode if $version != $emulatedVer
553
                    if ($this->compareVersions($version, $emulatedVer) != 0) {
554
                        $this->_compatibilityViewName = self::BROWSER_IE;
555
                        $this->_compatibilityViewVer = $this->cleanVersion($emulatedVer);
556
                    }
557
                }
558
            }
559
            //Test for Internet Explorer 2-7 versions if needed
560
            if ($version == '') {
561
                preg_match_all('/MSIE\s+([^\s;$]+)/i', $this->_agent, $foundVersions);
562
                foreach ($foundVersions[1] as $currVer) {
563
                    //Keep the highest MSIE version
564
                    if ($version == '' || $this->compareVersions($version, $currVer) == 1) {
565
                        $version = $currVer;
566
                    }
567
                }
568
            }
569
            $this->setBrowser(self::BROWSER_IE);
570
            $this->setVersion($version);
571
            $this->setMobile(false);
572
            $this->setRobot(false);
573
            return true;
574
        }
575
        return false;
576
    }
577
    /**
578
     * Determine if the browser is Konqueror or not.
579
     * @access protected
580
     * @link http://www.konqueror.org/
581
     * @return boolean Returns true if the browser is Konqueror, false otherwise.
582
     */
583
    protected function checkBrowserKonqueror()
584
    {
585
        return $this->checkSimpleBrowserUA('Konqueror', $this->_agent, self::BROWSER_KONQUEROR);
586
    }
587
    /**
588
     * Determine if the browser is Lynx or not. It is the oldest web browser currently in general use and development.
589
     * It is a text-based only Web browser.
590
     * @access protected
591
     * @link http://en.wikipedia.org/wiki/Lynx
592
     * @return boolean Returns true if the browser is Lynx, false otherwise.
593
     */
594
    protected function checkBrowserLynx()
595
    {
596
        return $this->checkSimpleBrowserUA('Lynx', $this->_agent, self::BROWSER_LYNX);
597
    }
598
    /**
599
     * Determine if the browser is Mozilla or not.
600
     * @access protected
601
     * @return boolean Returns true if the browser is Mozilla, false otherwise.
602
     */
603
    protected function checkBrowserMozilla()
604
    {
605
        return $this->checkSimpleBrowserUA('Mozilla', $this->_agent, self::BROWSER_MOZILLA, false, false, 'rv:');
606
    }
607
    /**
608
     * Determine if the browser is the MSNBot crawler or not. In October 2010 it was replaced by the Bingbot robot.
609
     * @access protected
610
     * @see checkBrowserBingbot()
611
     * @return boolean Returns true if the browser is MSNBot, false otherwise.
612
     */
613
    protected function checkBrowserMsnBot()
614
    {
615
        return $this->checkSimpleBrowserUA('msnbot', $this->_agent, self::BROWSER_MSNBOT, false, true);
616
    }
617
    /**
618
     * Determine if the browser is MSN TV (formerly WebTV) or not.
619
     * @access protected
620
     * @link http://en.wikipedia.org/wiki/MSN_TV
621
     * @return boolean Returns true if the browser is WebTv, false otherwise.
622
     */
623
    protected function checkBrowserMsnTv()
624
    {
625
        return $this->checkSimpleBrowserUA('webtv', $this->_agent, self::BROWSER_MSNTV);
626
    }
627
    /**
628
     * Determine if the browser is NetPositive or not. The browser is discontinued since November 2001.
629
     * @access protected
630
     * @link http://en.wikipedia.org/wiki/NetPositive
631
     * @return boolean Returns true if the browser is NetPositive, false otherwise.
632
     */
633
    protected function checkBrowserNetPositive()
634
    {
635
        return $this->checkSimpleBrowserUA('NetPositive', $this->_agent, self::BROWSER_NETPOSITIVE);
636
    }
637
    /**
638
     * Determine if the browser is Netscape or not. Official support for this browser ended on March 1st, 2008.
639
     * @access protected
640
     * @link http://en.wikipedia.org/wiki/Netscape
641
     * @return boolean Returns true if the browser is Netscape, false otherwise.
642
     */
643
    protected function checkBrowserNetscape()
644
    {
645
        //BlackBerry & Nokia UAs can conflict with Netscape UAs
646
        if (stripos($this->_agent, 'BlackBerry') !== false || stripos($this->_agent, 'Nokia') !== false) {
647
            return false;
648
        }
649
        //Netscape v6 to v9 check
650
        if ($this->checkSimpleBrowserUA(array('Netscape', 'Navigator', 'Netscape6'), $this->_agent, self::BROWSER_NETSCAPE)) {
651
            return true;
652
        }
653
        //Netscape v1-4 (v5 don't exists)
654
        $found = false;
655
        if (stripos($this->_agent, 'Mozilla') !== false && stripos($this->_agent, 'rv:') === false) {
656
            $version = '';
657
            $verParts = explode('/', stristr($this->_agent, 'Mozilla'));
658
            if (count($verParts) > 1) {
659
                $verParts = explode(' ', $verParts[1]);
660
                $verParts = explode('.', $verParts[0]);
661
                $majorVer = $this->parseInt($verParts[0]);
662
                if ($majorVer > 0 && $majorVer < 5) {
663
                    $version = implode('.', $verParts);
664
                    $found = true;
665
                    if (strtolower(substr($version, -4)) == '-sgi') {
666
                        $version = substr($version, 0, -4);
667
                    } else {
668
                        if (strtolower(substr($version, -4)) == 'gold') {
669
                            $version = substr($version, 0, -4) . ' Gold'; //Doubles spaces (if any) will be normalized by setVersion()
670
                        }
671
                    }
672
                }
673
            }
674
        }
675 View Code Duplication
        if ($found) {
676
            $this->setBrowser(self::BROWSER_NETSCAPE);
677
            $this->setVersion($version);
0 ignored issues
show
Bug introduced by
The variable $version does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
678
            $this->setMobile(false);
679
            $this->setRobot(false);
680
        }
681
        return $found;
682
    }
683
    /**
684
     * Determine if the browser is a Nokia browser or not.
685
     * @access protected
686
     * @link http://www.developer.nokia.com/Community/Wiki/User-Agent_headers_for_Nokia_devices
687
     * @return boolean Returns true if the browser is a Nokia browser, false otherwise.
688
     */
689
    protected function checkBrowserNokia()
690
    {
691
        if (stripos($this->_agent, 'Nokia5800') !== false || stripos($this->_agent, 'Nokia5530') !== false || stripos($this->_agent, 'Nokia5230') !== false) {
692
            $this->setBrowser(self::BROWSER_NOKIA);
693
            $this->setVersion('7.0');
694
            $this->setMobile(true);
695
            $this->setRobot(false);
696
            return true;
697
        }
698
        if ($this->checkSimpleBrowserUA(array('NokiaBrowser', 'BrowserNG', 'Series60', 'S60', 'S40OviBrowser'), $this->_agent, self::BROWSER_NOKIA, true)) {
699
            return true;
700
        }
701
        return false;
702
    }
703
    /**
704
     * Determine if the browser is OmniWeb or not.
705
     * @access protected
706
     * @link http://www.omnigroup.com/products/omniweb/
707
     * @return boolean Returns true if the browser is OmniWeb, false otherwise.
708
     */
709
    protected function checkBrowserOmniWeb()
710
    {
711
        if ($this->checkSimpleBrowserUA('OmniWeb', $this->_agent, self::BROWSER_OMNIWEB)) {
712
            //Some versions of OmniWeb prefix the version number with "v"
713
            if ($this->getVersion() != self::VERSION_UNKNOWN && strtolower(substr($this->getVersion(), 0, 1)) == 'v') {
714
                $this->setVersion(substr($this->getVersion(), 1));
715
            }
716
            return true;
717
        }
718
        return false;
719
    }
720
    /**
721
     * Determine if the browser is Opera or not.
722
     * @access protected
723
     * @link http://www.opera.com/
724
     * @link http://www.opera.com/mini/
725
     * @link http://www.opera.com/mobile/
726
     * @link http://my.opera.com/community/openweb/idopera/
727
     * @return boolean Returns true if the browser is Opera, false otherwise.
728
     */
729
    protected function checkBrowserOpera()
730
    {
731
        if ($this->checkBrowserUAWithVersion('Opera Mobi', $this->_agent, self::BROWSER_OPERA_MOBILE, true)) {
732
            return true;
733
        }
734
        if ($this->checkSimpleBrowserUA('Opera Mini', $this->_agent, self::BROWSER_OPERA_MINI, true)) {
735
            return true;
736
        }
737
        $version = '';
738
        $found = $this->checkBrowserUAWithVersion('Opera', $this->_agent, self::BROWSER_OPERA);
739
        if ($found && $this->getVersion() != self::VERSION_UNKNOWN) {
740
            $version = $this->getVersion();
741
        }
742
        if (!$found || $version == '') {
743
            if ($this->checkSimpleBrowserUA('Opera', $this->_agent, self::BROWSER_OPERA)) {
744
                return true;
745
            }
746
        }
747
        if (!$found && $this->checkSimpleBrowserUA('Chrome', $this->_agent, self::BROWSER_CHROME) ) {
748
            if ($this->checkSimpleBrowserUA('OPR/', $this->_agent, self::BROWSER_OPERA)) {
749
                return true;
750
            }
751
        }
752
        return $found;
753
    }
754
    /**
755
     * Determine if the browser is Phoenix or not. Phoenix was the name of Firefox from version 0.1 to 0.5.
756
     * @access protected
757
     * @return boolean Returns true if the browser is Phoenix, false otherwise.
758
     */
759
    protected function checkBrowserPhoenix()
760
    {
761
        return $this->checkSimpleBrowserUA('Phoenix', $this->_agent, self::BROWSER_PHOENIX);
762
    }
763
    /**
764
     * Determine what is the browser used by the user.
765
     * @access protected
766
     */
767
    protected function checkBrowsers()
768
    {
769
        //Changing the check order can break the class detection results!
770
        return
771
               /* Major browsers and browsers that need to be detected in a special order */
772
               $this->checkBrowserMsnTv() ||            /* MSN TV is based on IE so we must check for MSN TV before IE */
773
               $this->checkBrowserInternetExplorer() ||
774
               $this->checkBrowserOpera() ||            /* Opera be checked before Firefox, Netscape and Chrome to avoid conflicts */
775
               $this->checkBrowserChrome() ||           /* Chrome must be checked before Netscaoe and Mozilla to avoid conflicts */
776
               $this->checkBrowserOmniWeb() ||          /* OmniWeb must be checked before Safari (on which it's based on) and Netscape (since it have Mozilla UAs) */
777
               $this->checkBrowserIcab() ||             /* Check iCab before Netscape since iCab have Mozilla UAs */
778
               $this->checkBrowserNetPositive() ||      /* Check NetPositive before Netscape since NetPositive have Mozilla UAs */
779
               $this->checkBrowserNetscape() ||         /* Must be checked before Firefox since Netscape 8-9 are based on Firefox */
780
               $this->checkBrowserIceCat() ||           /* Check IceCat and IceWeasel before Firefox since they are GNU builds of Firefox */
781
               $this->checkBrowserIceWeasel() ||
782
               $this->checkBrowserGaleon() ||           /* Galeon is based on Firefox and needs to be tested before Firefox is tested */
783
               $this->checkBrowserFirefox() ||
784
               /* Current browsers that don't need to be detected in any special order */
785
               $this->checkBrowserKonqueror() ||
786
               $this->checkBrowserLynx() ||
787
               $this->checkBrowserAmaya() ||
788
               /* Mobile */
789
               $this->checkBrowserAndroid() ||
790
               $this->checkBrowserBlackBerry() ||
791
               $this->checkBrowserNokia() ||
792
               /* Bots */
793
               $this->checkBrowserGooglebot() ||
794
               $this->checkBrowserBingbot() ||
795
               $this->checkBrowserMsnBot() ||
796
               $this->checkBrowserSlurp() ||
797
               $this->checkBrowserYahooMultimedia() ||
798
               $this->checkBrowserW3CValidator() ||
799
               /* WebKit base check (after most other checks) */
800
               $this->checkBrowserSafari() ||
801
               /* Deprecated browsers that don't need to be detected in any special order */
802
               $this->checkBrowserFirebird() ||
803
               $this->checkBrowserPhoenix() ||
804
               /* Mozilla is such an open standard that it must be checked last */
805
               $this->checkBrowserMozilla();
806
    }
807
    /**
808
     * Determine if the browser is Safari or not.
809
     * @access protected
810
     * @link http://www.apple.com/safari/
811
     * @link http://web.archive.org/web/20080514173941/http://developer.apple.com/internet/safari/uamatrix.html
812
     * @link http://en.wikipedia.org/wiki/Safari_version_history#Release_history
813
     * @return boolean Returns true if the browser is Safari, false otherwise.
814
     */
815
    protected function checkBrowserSafari()
816
    {
817
        $version = '';
818
        //Check for current versions of Safari
819
        $found = $this->checkBrowserUAWithVersion(array('Safari', 'AppleWebKit'), $this->_agent, self::BROWSER_SAFARI);
820
        if ($found && $this->getVersion() != self::VERSION_UNKNOWN) {
821
            $version = $this->getVersion();
822
        }
823
        //Safari 1-2 didn't had a "Version" string in the UA, only a WebKit build and/or Safari build, extract version from these...
824 View Code Duplication
        if (!$found || $version == '') {
825
            if (preg_match('/.*Safari[ (\/]*([a-z0-9.-]*)/i', $this->_agent, $matches)) {
826
                $version = $this->safariBuildToSafariVer($matches[1]);
827
                $found = true;
828
            }
829
        }
830 View Code Duplication
        if (!$found || $version == '') {
831
            if (preg_match('/.*AppleWebKit[ (\/]*([a-z0-9.-]*)/i', $this->_agent, $matches)) {
832
                $version = $this->webKitBuildToSafariVer($matches[1]);
833
                $found = true;
834
            }
835
        }
836 View Code Duplication
        if ($found) {
837
            $this->setBrowser(self::BROWSER_SAFARI);
838
            $this->setVersion($version);
839
            $this->setMobile(false);
840
            $this->setRobot(false);
841
        }
842
        return $found;
843
    }
844
    /**
845
     * Determine if the browser is the Yahoo! Slurp crawler or not.
846
     * @access protected
847
     * @return boolean Returns true if the browser is Yahoo! Slurp, false otherwise.
848
     */
849
    protected function checkBrowserSlurp()
850
    {
851
        return $this->checkSimpleBrowserUA('Yahoo! Slurp', $this->_agent, self::BROWSER_SLURP, false, true);
852
    }
853
    /**
854
     * Test the user agent for a specific browser that use a "Version" string (like Safari and Opera). The user agent
855
     * should look like: "Version/1.0 Browser name/123.456" or "Browser name/123.456 Version/1.0".
856
     * @access protected
857
     * @param mixed $uaNameToLookFor The string (or array of strings) representing the browser name to find in the user
858
     * agent.
859
     * @param string $userAgent The user agent string to work with.
860
     * @param string $browserName The literal browser name. Always use a class constant!
861
     * @param boolean $isMobile Determines if the browser is from a mobile device.
862
     * @param boolean $isRobot Determines if the browser is a robot or not.
863
     * @return boolean Returns true if we found the browser we were looking for, false otherwise.
864
     */
865
    protected function checkBrowserUAWithVersion($uaNameToLookFor, $userAgent, $browserName, $isMobile = false, $isRobot = false)
866
    {
867
        if (!is_array($uaNameToLookFor)) {
868
            $uaNameToLookFor = array($uaNameToLookFor);
869
        }
870
        foreach ($uaNameToLookFor as $currUANameToLookFor) {
871
            if (stripos($userAgent, $currUANameToLookFor) !== false) {
872
                $version = '';
873
                $verParts = explode('/', stristr($this->_agent, 'Version'));
874 View Code Duplication
                if (count($verParts) > 1) {
875
                    $verParts = explode(' ', $verParts[1]);
876
                    $version = $verParts[0];
877
                }
878
                $this->setBrowser($browserName);
879
                $this->setVersion($version);
880
                $this->setMobile($isMobile);
881
                $this->setRobot($isRobot);
882
                return true;
883
            }
884
        }
885
        return false;
886
    }
887
    /**
888
     * Determine if the browser is the W3C Validator or not.
889
     * @access protected
890
     * @link http://validator.w3.org/
891
     * @return boolean Returns true if the browser is the W3C Validator, false otherwise.
892
     */
893
    protected function checkBrowserW3CValidator()
894
    {
895
        //Since the W3C validates pages with different robots we will prefix our versions with the part validated on the page...
896
        //W3C Link Checker (prefixed with "Link-")
897 View Code Duplication
        if ($this->checkSimpleBrowserUA('W3C-checklink', $this->_agent, self::BROWSER_W3CVALIDATOR, false, true)) {
898
            if ($this->getVersion() != self::VERSION_UNKNOWN) {
899
                $this->setVersion('Link-' . $this->getVersion());
900
            }
901
            return true;
902
        }
903
        //W3C CSS Validation Service (prefixed with "CSS-")
904 View Code Duplication
        if ($this->checkSimpleBrowserUA('Jigsaw', $this->_agent, self::BROWSER_W3CVALIDATOR, false, true)) {
905
            if ($this->getVersion() != self::VERSION_UNKNOWN) {
906
                $this->setVersion('CSS-' . $this->getVersion());
907
            }
908
            return true;
909
        }
910
        //W3C mobileOK Checker (prefixed with "mobileOK-")
911 View Code Duplication
        if ($this->checkSimpleBrowserUA('W3C-mobileOK', $this->_agent, self::BROWSER_W3CVALIDATOR, false, true)) {
912
            if ($this->getVersion() != self::VERSION_UNKNOWN) {
913
                $this->setVersion('mobileOK-' . $this->getVersion());
914
            }
915
            return true;
916
        }
917
        //W3C Markup Validation Service (no prefix)
918
        return $this->checkSimpleBrowserUA('W3C_Validator', $this->_agent, self::BROWSER_W3CVALIDATOR, false, true);
919
    }
920
    /**
921
     * Determine if the browser is the Yahoo! multimedia crawler or not.
922
     * @access protected
923
     * @return boolean Returns true if the browser is the Yahoo! multimedia crawler, false otherwise.
924
     */
925
    protected function checkBrowserYahooMultimedia()
926
    {
927
        return $this->checkSimpleBrowserUA('Yahoo-MMCrawler', $this->_agent, self::BROWSER_YAHOO_MM, false, true);
928
    }
929
    /**
930
     * Determine if the user is using an AOL "optimized" browser or not.
931
     * @access protected
932
     * @return boolean Returns true if the browser is AOL optimized, false otherwise.
933
     */
934
    protected function checkForAol()
935
    {
936
        //AOL UAs don't use the "AOL/1.0" format, they uses "AOL 1.0; AOLBuild 100.00;"
937
        if (stripos($this->_agent, 'AOL ') !== false) {
938
            $version = '';
939
            $verParts = explode('AOL ', stristr($this->_agent, 'AOL '));
940 View Code Duplication
            if (count($verParts) > 1) {
941
                $verParts = explode(' ', $verParts[1]);
942
                $version = $verParts[0];
943
            }
944
            $this->setAol(true);
945
            $this->setAolVersion($version);
946
            return true;
947
        } else {
948
            $this->setAol(false);
949
            $this->setAolVersion('');
950
            return false;
951
        }
952
    }
953
    /**
954
     * Determine the user's platform.
955
     * @access protected
956
     */
957
    protected function checkPlatform()
958
    {
959
        /* Mobile platforms */
960
        if (stripos($this->_agent, 'Windows Phone') !== false ||     /* Check Windows Phone (formerly Windows Mobile) before Windows */
961
                stripos($this->_agent, 'IEMobile') !== false) {
962
            $this->_platform = self::PLATFORM_WINDOWS_PHONE;
963
            $this->setMobile(true);
964
        } else if (stripos($this->_agent, 'Windows CE') !== false) { /* Check Windows CE before Windows */
965
            $this->_platform = self::PLATFORM_WINDOWS_CE;
966
            $this->setMobile(true);
967
        } else if (stripos($this->_agent, 'iPhone') !== false) {     /* Check iPad/iPod/iPhone before Macintosh */
968
            $this->_platform = self::PLATFORM_IPHONE;
969
            $this->setMobile(true);
970
        } else if (stripos($this->_agent, 'iPad') !== false) {
971
            $this->_platform = self::PLATFORM_IPAD;
972
            $this->setMobile(true);
973
        } else if (stripos($this->_agent, 'iPod') !== false) {
974
            $this->_platform = self::PLATFORM_IPOD;
975
            $this->setMobile(true);
976
        } else if (stripos($this->_agent, 'Android') !== false) {
977
            $this->_platform = self::PLATFORM_ANDROID;
978
            $this->setMobile(true);
979
        } else if (stripos($this->_agent, 'Symbian') !== false) {
980
            $this->_platform = self::PLATFORM_SYMBIAN;
981
            $this->setMobile(true);
982
        } else if (stripos($this->_agent, 'BlackBerry') !== false ||
983
                stripos($this->_agent, 'BB10') !== false ||
984
                stripos($this->_agent, 'RIM Tablet OS') !== false) {
985
            $this->_platform = self::PLATFORM_BLACKBERRY;
986
            $this->setMobile(true);
987
        } else if (stripos($this->_agent, 'Nokia') !== false) {
988
            $this->_platform = self::PLATFORM_NOKIA;
989
            $this->setMobile(true);
990
        /* Desktop platforms */
991
        } else if (stripos($this->_agent, 'Windows') !== false) {
992
            $this->_platform = self::PLATFORM_WINDOWS;
993
        } else if (stripos($this->_agent, 'Macintosh') !== false) {
994
            $this->_platform = self::PLATFORM_MACINTOSH;
995
        } else if (stripos($this->_agent, 'Linux') !== false) {
996
            $this->_platform = self::PLATFORM_LINUX;
997
        } else if (stripos($this->_agent, 'FreeBSD') !== false) {
998
            $this->_platform = self::PLATFORM_FREEBSD;
999
        } else if (stripos($this->_agent, 'OpenBSD') !== false) {
1000
            $this->_platform = self::PLATFORM_OPENBSD;
1001
        } else if (stripos($this->_agent, 'NetBSD') !== false) {
1002
            $this->_platform = self::PLATFORM_NETBSD;
1003
        /* Discontinued */
1004
        } else if (stripos($this->_agent, 'OpenSolaris') !== false) {
1005
            $this->_platform = self::PLATFORM_OPENSOLARIS;
1006
        } else if (stripos($this->_agent, 'OS/2') !== false) {
1007
            $this->_platform = self::PLATFORM_OS2;
1008
        } else if (stripos($this->_agent, 'BeOS') !== false) {
1009
            $this->_platform = self::PLATFORM_BEOS;
1010
        } else if (stripos($this->_agent, 'SunOS') !== false) {
1011
            $this->_platform = self::PLATFORM_SUNOS;
1012
        /* Generic */
1013
        } else if (stripos($this->_agent, 'Win') !== false) {
1014
            $this->_platform = self::PLATFORM_WINDOWS;
1015
        } else if (stripos($this->_agent, 'Mac') !== false) {
1016
            $this->_platform = self::PLATFORM_MACINTOSH;
1017
        }
1018
    }
1019
    /**
1020
     * Test the user agent for a specific browser where the browser name is immediately followed by the version number.
1021
     * The user agent should look like: "Browser name/1.0" or "Browser 1.0;".
1022
     * @access protected
1023
     * @param mixed $uaNameToLookFor The string (or array of strings) representing the browser name to find in the user
1024
     * agent.
1025
     * @param string $userAgent The user agent string to work with.
1026
     * @param string $browserName The literal browser name. Always use a class constant!
1027
     * @param boolean $isMobile Determines if the browser is from a mobile device.
1028
     * @param boolean $isRobot Determines if the browser is a robot or not.
1029
     * @param string $separator The separator string used to split the browser name and the version number in the user
1030
     * agent.
1031
     * @return boolean Returns true if we found the browser we were looking for, false otherwise.
1032
     */
1033
    protected function checkSimpleBrowserUA($uaNameToLookFor, $userAgent, $browserName, $isMobile = false, $isRobot = false, $separator = '/')
1034
    {
1035
        if (!is_array($uaNameToLookFor)) {
1036
            $uaNameToLookFor = array($uaNameToLookFor);
1037
        }
1038
        foreach ($uaNameToLookFor as $currUANameToLookFor) {
1039
            if (stripos($userAgent, $currUANameToLookFor) !== false) {
1040
                //Many browsers don't use the standard "Browser/1.0" format, they uses "Browser 1.0;" instead
1041
                if (stripos($userAgent, $currUANameToLookFor . $separator) === false) {
1042
                    $userAgent = str_ireplace($currUANameToLookFor . ' ', $currUANameToLookFor . $separator, $this->_agent);
1043
                }
1044
                $version = '';
1045
                $verParts = explode($separator, stristr($userAgent, $currUANameToLookFor));
1046 View Code Duplication
                if (count($verParts) > 1) {
1047
                    $verParts = explode(' ', $verParts[1]);
1048
                    $version = $verParts[0];
1049
                }
1050
                $this->setBrowser($browserName);
1051
                $this->setVersion($version);
1052
                $this->setMobile($isMobile);
1053
                $this->setRobot($isRobot);
1054
                return true;
1055
            }
1056
        }
1057
        return false;
1058
    }
1059
    /**
1060
     * Detect the user environment from the details in the user agent string.
1061
     * @access protected
1062
     */
1063
    protected function detect()
1064
    {
1065
        $this->checkBrowsers();
1066
        $this->checkPlatform(); //Check the platform after the browser since some platforms can change the mobile value
1067
        $this->checkForAol();
1068
    }
1069
    /**
1070
     * Clean a version string from unwanted characters.
1071
     * @access protected
1072
     * @param string $version The version string to clean.
1073
     * @return string Returns the cleaned version number string.
1074
     */
1075
    protected function cleanVersion($version)
1076
    {
1077
        //Clear anything that is in parentheses (and the parentheses themselves) - will clear started but unclosed ones too
1078
        $cleanVer = preg_replace('/\([^)]+\)?/', '', $version);
1079
        //Replace with a space any character which is NOT an alphanumeric, dot (.), hyphen (-), underscore (_) or space
1080
        $cleanVer = preg_replace('/[^0-9.a-zA-Z_ -]/', ' ', $cleanVer);
1081
        //Remove trailing and leading spaces
1082
        $cleanVer = trim($cleanVer);
1083
        //Remove double spaces if any
1084
        while (strpos($cleanVer, '  ') !== false) {
1085
            $cleanVer = str_replace('  ', ' ', $cleanVer);
1086
        }
1087
        return $cleanVer;
1088
    }
1089
    /**
1090
     * Get the integer value of a string variable.
1091
     * @access protected
1092
     * @param string $intStr The scalar value being converted to an integer.
1093
     * @return int The integer value of $intStr on success, or 0 on failure.
1094
     */
1095
    protected function parseInt($intStr)
1096
    {
1097
        return intval($intStr, 10);
1098
    }
1099
    /**
1100
     * Reset all the properties of the class.
1101
     * @access protected
1102
     */
1103
    protected function reset()
1104
    {
1105
        $this->_agent = '';
1106
        $this->_aolVersion = '';
1107
        $this->_browserName = self::BROWSER_UNKNOWN;
1108
        $this->_compatibilityViewName = '';
1109
        $this->_compatibilityViewVer = '';
1110
        $this->_isAol = false;
1111
        $this->_isMobile = false;
1112
        $this->_isRobot = false;
1113
        $this->_platform = self::PLATFORM_UNKNOWN;
1114
        $this->_version = self::VERSION_UNKNOWN;
1115
    }
1116
    /**
1117
     * Convert a Safari build number to a Safari version number.
1118
     * @access protected
1119
     * @param string $version A string representing the version number.
1120
     * @link http://web.archive.org/web/20080514173941/http://developer.apple.com/internet/safari/uamatrix.html
1121
     * @return string Returns the Safari version string. If the version can't be determined, an empty string is
1122
     * returned.
1123
     */
1124
    protected function safariBuildToSafariVer($version)
1125
    {
1126
        $verParts = explode('.', $version);
1127
        //We need a 3 parts version (version 2 will becomes 2.0.0)
1128
        while (count($verParts) < 3) {
1129
            $verParts[] = 0;
1130
        }
1131
        foreach ($verParts as $i => $currPart) {
1132
            $verParts[$i] = $this->parseInt($currPart);
1133
        }
1134
        switch ($verParts[0]) {
1135
            case 419: $result = '2.0.4';
1136
                break;
1137
            case 417: $result = '2.0.3';
1138
                break;
1139
            case 416: $result = '2.0.2';
1140
                break;
1141 View Code Duplication
            case 412:
1142
                if ($verParts[1] >= 5) {
1143
                    $result = '2.0.1';
1144
                } else {
1145
                    $result = '2.0';
1146
                }
1147
                break;
1148 View Code Duplication
            case 312:
1149
                if ($verParts[1] >= 5) {
1150
                    $result = '1.3.2';
1151
                } else {
1152
                    if ($verParts[1] >= 3) {
1153
                        $result = '1.3.1';
1154
                    } else {
1155
                        $result = '1.3';
1156
                    }
1157
                }
1158
                break;
1159
            case 125:
1160
                if ($verParts[1] >= 11) {
1161
                    $result = '1.2.4';
1162 View Code Duplication
                } else {
1163
                    if ($verParts[1] >= 9) {
1164
                        $result = '1.2.3';
1165
                    } else {
1166
                        if ($verParts[1] >= 7) {
1167
                            $result = '1.2.2';
1168
                        } else {
1169
                            $result = '1.2';
1170
                        }
1171
                    }
1172
                }
1173
                break;
1174
            case 100:
1175
                if ($verParts[1] >= 1) {
1176
                    $result = '1.1.1';
1177
                } else {
1178
                    $result = '1.1';
1179
                }
1180
                break;
1181 View Code Duplication
            case 85:
1182
                if ($verParts[1] >= 8) {
1183
                    $result = '1.0.3';
1184
                } else {
1185
                    if ($verParts[1] >= 7) {
1186
                        $result = '1.0.2';
1187
                    } else {
1188
                        $result = '1.0';
1189
                    }
1190
                }
1191
                break;
1192
            case 73: $result = '0.9';
1193
                break;
1194
            case 51: $result = '0.8.1';
1195
                break;
1196
            case 48: $result = '0.8';
1197
                break;
1198
            default: $result = '';
1199
        }
1200
        return $result;
1201
    }
1202
    /**
1203
     * Set the browser to be from AOL or not.
1204
     * @access protected
1205
     * @param boolean $isAol Value that tells if the browser is AOL or not.
1206
     */
1207
    protected function setAol($isAol)
1208
    {
1209
        $this->_isAol = $isAol == true;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1210
    }
1211
    /**
1212
     * Set the version of AOL.
1213
     * @access protected
1214
     * @param string $version The version of AOL (will be cleaned).
1215
     */
1216
    protected function setAolVersion($version)
1217
    {
1218
        $cleanVer = $this->cleanVersion($version);
1219
        $this->_aolVersion = $cleanVer;
1220
    }
1221
    /**
1222
     * Set the name of the browser.
1223
     * @access protected
1224
     * @param string $browserName The name of the browser.
1225
     */
1226
    protected function setBrowser($browserName)
1227
    {
1228
        return $this->_browserName = $browserName;
1229
    }
1230
    /**
1231
     * Set the browser to be from a mobile device or not.
1232
     * @access protected
1233
     * @param boolean $isMobile Value that tells if the browser is on a mobile device or not.
1234
     */
1235
    protected function setMobile($isMobile = true)
1236
    {
1237
        $this->_isMobile = $isMobile == true;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1238
    }
1239
    /**
1240
     * Set the platform on which the browser is on.
1241
     * @access protected
1242
     * @param string $platform The name of the platform.
1243
     */
1244
    protected function setPlatform($platform)
1245
    {
1246
        return $this->_platform = $platform;
1247
    }
1248
    /**
1249
     * Set the browser to be a robot (crawler) or not.
1250
     * @access protected
1251
     * @param boolean $isRobot Value that tells if the browser is a robot or not.
1252
     */
1253
    protected function setRobot($isRobot = true)
1254
    {
1255
        $this->_isRobot = $isRobot == true;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1256
    }
1257
    /**
1258
     * Set the version of the browser.
1259
     * @access protected
1260
     * @param string $version The version of the browser.
1261
     */
1262
    protected function setVersion($version)
1263
    {
1264
        $cleanVer = $this->cleanVersion($version);
1265
        if ($cleanVer == '') {
1266
            $this->_version = self::VERSION_UNKNOWN;
1267
        } else {
1268
            $this->_version = $cleanVer;
1269
        }
1270
    }
1271
    /**
1272
     * Convert a WebKit build number to a Safari version number.
1273
     * @access protected
1274
     * @param string $version A string representing the version number.
1275
     * @link http://web.archive.org/web/20080514173941/http://developer.apple.com/internet/safari/uamatrix.html
1276
     * @return string Returns the Safari version string. If the version can't be determined, an empty string is
1277
     * returned.
1278
     */
1279
    protected function webKitBuildToSafariVer($version)
1280
    {
1281
        $verParts = explode('.', $version);
1282
        //We need a 3 parts version (version 2 will becomes 2.0.0)
1283
        while (count($verParts) < 3) {
1284
            $verParts[] = 0;
1285
        }
1286
        foreach ($verParts as $i => $currPart) {
1287
            $verParts[$i] = $this->parseInt($currPart);
1288
        }
1289
        switch ($verParts[0]) {
1290
            case 419: $result = '2.0.4';
1291
                break;
1292
            case 418:
1293
                if ($verParts[1] >= 8) {
1294
                    $result = '2.0.4';
1295
                } else {
1296
                    $result = '2.0.3';
1297
                }
1298
                break;
1299
            case 417: $result = '2.0.3';
1300
                break;
1301
            case 416: $result = '2.0.2';
1302
                break;
1303 View Code Duplication
            case 412:
1304
                if ($verParts[1] >= 7) {
1305
                    $result = '2.0.1';
1306
                } else {
1307
                    $result = '2.0';
1308
                }
1309
                break;
1310 View Code Duplication
            case 312:
1311
                if ($verParts[1] >= 8) {
1312
                    $result = '1.3.2';
1313
                } else {
1314
                    if ($verParts[1] >= 5) {
1315
                        $result = '1.3.1';
1316
                    } else {
1317
                        $result = '1.3';
1318
                    }
1319
                }
1320
                break;
1321
            case 125:
1322
                if ($this->compareVersions('5.4', $verParts[1] . '.' . $verParts[2]) === 1) {
1323
                    $result = '1.2.4'; //125.5.5+
1324 View Code Duplication
                } else {
1325
                    if ($verParts[1] >= 4) {
1326
                        $result = '1.2.3';
1327
                    } else {
1328
                        if ($verParts[1] >= 2) {
1329
                            $result = '1.2.2';
1330
                        } else {
1331
                            $result = '1.2';
1332
                        }
1333
                    }
1334
                }
1335
                break;
1336
            //WebKit 100 can be either Safari 1.1 (Safari build 100) or 1.1.1 (Safari build 100.1)
1337
            //for this reason, check the Safari build before the WebKit build.
1338
            case 100: $result = '1.1.1';
1339
                break;
1340 View Code Duplication
            case 85:
1341
                if ($verParts[1] >= 8) {
1342
                    $result = '1.0.3';
1343
                } else {
1344
                    if ($verParts[1] >= 7) {
1345
                        //WebKit 85.7 can be either Safari 1.0 (Safari build 85.5) or 1.0.2 (Safari build 85.7)
1346
                        //for this reason, check the Safari build before the WebKit build.
1347
                        $result = '1.0.2';
1348
                    } else {
1349
                        $result = '1.0';
1350
                    }
1351
                }
1352
                break;
1353
            case 73: $result = '0.9';
1354
                break;
1355
            case 51: $result = '0.8.1';
1356
                break;
1357
            case 48: $result = '0.8';
1358
                break;
1359
            default: $result = '';
1360
        }
1361
        return $result;
1362
    }
1363
}