2

I'm using this code for detect the user's OS:

<?
$osList = array (
/* -- WINDOWS -- */
'Windows 10 (Windows NT 10.0)' => 'windows nt 10.0',
'Windows 8.1 (Windows NT 6.3)' => 'windows nt 6.3',
'Windows 8 (Windows NT 6.2)' => 'windows nt 6.2',
'Windows 7 (Windows NT 6.1)' => 'windows nt 6.1',
'Windows Vista (Windows NT 6.0)' => 'windows nt 6.0',
'Windows Server 2003 (Windows NT 5.2)' => 'windows nt 5.2',
'Windows XP (Windows NT 5.1)' => 'windows nt 5.1',
'Windows 2000 sp1 (Windows NT 5.01)' => 'windows nt 5.01',
'Windows 2000 (Windows NT 5.0)' => 'windows nt 5.0',
'Windows NT 4.0' => 'windows nt 4.0',
'Windows Me  (Windows 9x 4.9)' => 'win 9x 4.9',
'Windows 98' => 'windows 98',
'Windows 95' => 'windows 95',
'Windows CE' => 'windows ce',
'Windows (version unknown)' => 'windows',
/* -- MAC OS X -- */
'Mac OS X Beta (Kodiak)' => 'Mac OS X beta',
'Mac OS X Cheetah' => 'Mac OS X 10.0',
'Mac OS X Puma' => 'Mac OS X 10.1',
'Mac OS X Jaguar' => 'Mac OS X 10.2',
'Mac OS X Panther' => 'Mac OS X 10.3',
'Mac OS X Tiger' => 'Mac OS X 10.4',
'Mac OS X Leopard' => 'Mac OS X 10.5',
'Mac OS X Snow Leopard' => 'Mac OS X 10.6',
'Mac OS X Lion' => 'Mac OS X 10.7',
'Mac OS X Mountain Lion' => 'Mac OS X 10.8',
'Mac OS X Mavericks' => 'Mac OS X 10.9',
'Mac OS X Yosemite' => 'Mac OS X 10.10',
'Mac OS X El Capitan' => 'Mac OS X 10.11',
'macOS Sierra' => 'Mac OS X 10.12',
'Mac OS X (version unknown)' => 'Mac OS X',
'Mac OS (classic)' => '(mac_powerpc)|(macintosh)',
/* -- OTHERS -- */
'OpenBSD' => 'openbsd',
'SunOS' => 'sunos',
'Ubuntu' => 'ubuntu',
'Linux (or Linux based)' => '(linux)|(x11)',
'QNX' => 'QNX',
'BeOS' => 'beos',
'OS2' => 'os/2',
'SearchBot'=>'(nuhk)|(googlebot)|(yammybot)|(openbot)|(slurp)|(msnbot)|(ask jeeves/teoma)|(ia_archiver)'
);

$useragent = htmlspecialchars($_SERVER['HTTP_USER_AGENT']);
$useragent = strtolower($useragent);

foreach($osList as $os=>$match) {
    if (preg_match('/' . $match . '/i', $useragent)) {
        break;  
    } else {
        $os = "Unknown";    
    }
}
?>

The problem here is: I'm using OS X El Capitan (10.11.5) and this code shows that I'm using OS X Puma (10.1) because 'Mac OS X El Capitan' => 'Mac OS X 10.11'.

So, how can I tell to preg_match check if $match is "X.Y(.Z)"?

Jimmy Adaro
  • 1,325
  • 15
  • 26
  • Does `strpos()` help here by any chance ? – Maximus2012 Nov 04 '16 at 16:31
  • I'm not sure. Can you provide an example of use? Thanks. – Jimmy Adaro Nov 04 '16 at 16:32
  • 2
    Does [this question](http://stackoverflow.com/q/18070154/697370) have an answer that would suit you? If so, we can close this one as a duplicate. – Jeff Lambert Nov 04 '16 at 16:35
  • The easiest thing to do would be to reorder your array so that the mac stuff is in descending order – Jaime Nov 04 '16 at 16:38
  • @JeffLambert the accepted answer for that question does not seem to be handling the case for Mac OS X versions. – Maximus2012 Nov 04 '16 at 16:40
  • @JimmyAdaro you might want to take a look here: http://stackoverflow.com/questions/14818818/regex-to-match-an-exact-string You might have to use additional condition checking for the case when there are multiple matches like the case that you mentioned. – Maximus2012 Nov 04 '16 at 16:41
  • @JimmyAdaro what is the value of the `$useragent` variable for your case ? – Maximus2012 Nov 04 '16 at 16:43

1 Answers1

3

You could add a character class after the 1 in the case of OS X Puma that will only match if the character following it is not a digit, e.g.:

'Mac OS X Puma' => 'Mac OS X 10.1[^0-9]',

This should be safe for reliably detecting version 10.1 versus current and future versions, such as Mac OS X 10.1999.42


It should be noted that the user agent strings you are currently matching against have a high probability of being different between browsers, or even editable by users from within their browser. If a high degree of success in matching is necessary, an approach other than user agent sniffing would be needed.

Jeff Lambert
  • 24,395
  • 4
  • 69
  • 96
  • Nice approach, thanks! Does `[^0-9]` consider the dot? E.g.: `10.11.5`. – Jimmy Adaro Nov 04 '16 at 16:57
  • All it says is that there _will_ be a character following the one, and it will match as long as it is _not_ a digit. The dots in your regular expressions above are not escaped, so they are, in fact, matching the _underscores_ in your user agent string. An underscore is not a digit, so it will match, e.g. `10_1_53` for revision 53 of version 10.1 (if such a revision exists). – Jeff Lambert Nov 04 '16 at 16:59
  • That means `10.1.10` can be confused with `10.11.5`? Anyways, adding that Regex solves my current problem. – Jimmy Adaro Nov 04 '16 at 17:02
  • All I suggested was fixing the 10.1 regular expression, I 'm pretty sure 10.11.5 has a digit after the 10.1 part so 10.11.5 should not be matched as OS X Puma. The rest of your regular expressions, though, could possibly use the same character class. As it stands 10.11.5 can be confused with 10.110.5 when it comes out, though that may be overkill for your needs. – Jeff Lambert Nov 04 '16 at 17:06
  • @JimmyAdaro np, glad to help. – Jeff Lambert Nov 04 '16 at 17:08
  • @JeffLambert I noticed your comment up there earlier, where I have [an answer](http://stackoverflow.com/a/18070424/1415724) in there for it and have made an additional edit referencing this Q&A. I could have closed this question, but found it wasn't an exact duplicate. I hope you keep [your comment](http://stackoverflow.com/questions/40427531/php-regex-for-os-detection#comment68103793_40427531) for it up there. – Funk Forty Niner Nov 04 '16 at 17:13
  • 1
    @Fred-ii- yeah I'll keep it. To me the question itself is close enough to be a duplicate regardless of the answers that are available, but even though I have a dupe hammer I don't really want to use it unless it's very clear that it is a duplicate. I think the answer you have is good also, it just doesn't really address in depth the different versions of OS X as OP here wanted. – Jeff Lambert Nov 04 '16 at 18:28