Skip to content

Instantly share code, notes, and snippets.

@luigifab
Last active December 9, 2023 08:50
Show Gist options
  • Save luigifab/4cb373e75f3cd2f342ca6bc25504b149 to your computer and use it in GitHub Desktop.
Save luigifab/4cb373e75f3cd2f342ca6bc25504b149 to your computer and use it in GitHub Desktop.
v1.8.0-fork2 (tested with php 7.2 / 7.3 / 7.4 / 8.0 / 8.1 / 8.2 / 8.3)
<?php
/**
* Copyright 2013-2023 | Jesse G. Donat <donatj~gmail~com>
* https://github.com/donatj/PhpUserAgent
*
* Copyright 2019-2023 | Fabrice Creuzot (luigifab) <code~luigifab~fr>
* https://gist.github.com/luigifab/4cb373e75f3cd2f342ca6bc25504b149 (1.8.0-fork2)
*
* Parses a user agent string into its important parts
* Licensed under the MIT License
*/
class Useragentparser {
public function parse($userAgent = null) {
if (empty($userAgent)) $userAgent = getenv('HTTP_USER_AGENT');
$platform = null;
$browser = null;
$version = null;
$empty = ['platform' => $platform, 'browser' => $browser, 'version' => $version];
$priority = ['Xbox One', 'Xbox', 'Windows Phone', 'Tizen', 'Android', 'FreeBSD', 'NetBSD', 'OpenBSD', 'CrOS', 'X11', 'Sailfish'];
if (empty($userAgent)) return $empty;
if (preg_match('/\((.*?)\)/m', $userAgent, $parentMatches)) {
preg_match_all('/(?P<platform>BB\d+;|Android|Adr|Symbian|Sailfish|CrOS|Tizen|iPhone|iPad|iPod|Linux|(?:Open|Net|Free)BSD|Macintosh|Windows(?:\ Phone)?|Silk|linux-gnu|BlackBerry|PlayBook|X11|(?:New\ )?Nintendo\ (?:WiiU?|3?DS|Switch)|Xbox(?:\ One)?) (?:\ [^;]*)? (?:;|$)/imx',
$parentMatches[1], $result);
$result['platform'] = array_unique($result['platform']);
if (count($result['platform']) > 1) {
if (!empty($keys = array_intersect($priority, $result['platform'])))
$platform = reset($keys);
else
$platform = $result['platform'][0];
}
else if (isset($result['platform'][0])) {
$platform = $result['platform'][0];
}
}
if ($platform == 'linux-gnu' || $platform == 'X11')
$platform = 'Linux';
else if ($platform == 'CrOS')
$platform = 'Chrome OS';
else if ($platform == 'Adr')
$platform = 'Android';
else if (($platform === null) && preg_match_all('%(?P<platform>Android)[:/ ]%ix', $userAgent, $result))
$platform = $result['platform'][0];
preg_match_all( // ["browser" => ["Firefox"...], "version" => ["45.0"...]]
'%(?P<browser>Camino|Kindle(\ Fire)?|Firefox|Thunderbird|Iceweasel|IceCat|Safari|MSIE|Trident|AppleWebKit|TizenBrowser|(?:Headless)?Chrome|YaBrowser|Vivaldi|IEMobile|Opera|OPR|Silk|Midori|(?-i:Edge)|EdgA?|CriOS|UCBrowser|Puffin|OculusBrowser|SamsungBrowser|SailfishBrowser|XiaoMi/MiuiBrowser|YaApp_Android|Baiduspider|Applebot|Facebot|Googlebot|YandexBot|bingbot|Lynx|Version|Wget|curl|Valve\ Steam\ Tenfoot|NintendoBrowser|PLAYSTATION\ (?:\d|Vita)+)\)?;?(?:[:/ ](?P<version>[\dA-Z.]+)|/[A-Z]*)%ix',
$userAgent, $result);
// If nothing matched, return null (to avoid undefined index errors)
if (!isset($result['browser'][0], $result['version'][0])) {
if (preg_match('%^(?!Mozilla)(?P<browser>[A-Z\d\-]+)([/ :](?P<version>[\dA-Z.]+))?%ix', $userAgent, $result)) {
return [
'platform' => $platform ?: null,
'browser' => $result['browser'],
'version' => empty($result['version']) ? null : $result['version'],
];
}
return $empty;
}
if (preg_match('/rv:(?P<version>[\dA-Z.]+)/i', $userAgent, $rv_result)) {
$rv_result = $rv_result['version'];
}
$browser = $result['browser'][0];
$version = $result['version'][0];
$lowerBrowser = array_map('strtolower', $result['browser']);
$key = 0;
$val = '';
if ($this->findT($lowerBrowser, ['OPR' => 'Opera', 'Facebot' => 'iMessageBot', 'UCBrowser' => 'UC Browser', 'YaBrowser' => 'Yandex', 'YaApp_Android' => 'Yandex', 'Iceweasel' => 'Firefox', 'Icecat' => 'Firefox', 'CriOS' => 'Chrome', 'Edg' => 'Edge', 'EdgA' => 'Edge', 'XiaoMi/MiuiBrowser' => 'MiuiBrowser'], $key, $browser)) {
$version = is_numeric(substr($result['version'][$key], 0, 1)) ? $result['version'][$key] : null;
}
else if ($this->find($lowerBrowser, 'Playstation Vita', $key, $platform)) {
$platform = 'PlayStation Vita';
$browser = 'Browser';
}
else if ($this->find($lowerBrowser, ['Kindle Fire', 'Silk'], $key, $val)) {
$browser = $val == 'Silk' ? 'Silk' : 'Kindle';
$platform = 'Kindle Fire';
if (!($version = $result['version'][$key]) || !is_numeric($version[0])) {
$version = $result['version'][array_search('Version', $result['browser'])];
}
}
else if ($platform == 'Nintendo 3DS' || $this->find($lowerBrowser, 'NintendoBrowser', $key)) {
$browser = 'NintendoBrowser';
$version = $result['version'][$key];
}
else if ($this->find($lowerBrowser, 'Kindle', $key, $platform)) {
$browser = $result['browser'][$key];
$version = $result['version'][$key];
}
else if ($this->find($lowerBrowser, 'Opera', $key, $browser)) {
$this->find($lowerBrowser, 'Version', $key);
$version = $result['version'][$key];
}
else if ($this->find($lowerBrowser, 'Puffin', $key, $browser)) {
$version = $result['version'][$key];
if (strlen($version) > 3) {
$part = substr($version, -2);
if (ctype_upper($part)) {
$version = substr($version, 0, -2);
$flags = ['IP' => 'iPhone', 'IT' => 'iPad', 'AP' => 'Android', 'AT' => 'Android', 'WP' => 'Windows Phone', 'WT' => 'Windows'];
if (is_string($part) && array_key_exists($part, $flags)) {
$platform = $flags[$part];
}
}
}
}
else if ($this->find($lowerBrowser, ['Applebot', 'IEMobile', 'Edge', 'Midori', 'Vivaldi', 'OculusBrowser', 'SamsungBrowser', 'Valve Steam Tenfoot', 'Chrome', 'HeadlessChrome', 'SailfishBrowser'], $key, $browser)) {
$version = $result['version'][$key];
}
else if ($rv_result && $this->find($lowerBrowser, 'Trident')) {
$browser = 'MSIE';
$version = $rv_result;
}
else if ($browser == 'AppleWebKit') {
if ($platform == 'Android') {
$browser = 'Android Browser';
}
else if (strncmp((string) $platform, 'BB', 2) === 0) {
$browser = 'BlackBerry Browser';
$platform = 'BlackBerry';
}
else if ($platform == 'BlackBerry' || $platform == 'PlayBook') {
$browser = 'BlackBerry Browser';
}
else {
$this->find($lowerBrowser, 'Safari', $key, $browser) || $this->find($lowerBrowser, 'TizenBrowser', $key, $browser);
}
$this->find($lowerBrowser, 'Version', $key);
$version = $result['version'][$key];
}
else if (!empty($pKey = preg_grep('/playstation \d/i', $result['browser']))) {
$pKey = reset($pKey);
$platform = 'PlayStation '.preg_replace('/\D/', '', $pKey);
$browser = 'NetFront';
}
return ['platform' => $platform ?: null, 'browser' => $browser ?: null, 'version' => $version ?: null];
}
private function find($lowerBrowser, $search, &$key = null, &$value = null) {
$search = (array) $search;
foreach ($search as $val) {
$xkey = array_search(strtolower($val), $lowerBrowser);
if ($xkey !== false) {
$value = $val;
$key = $xkey;
return true;
}
}
return false;
}
private function findT($lowerBrowser, array $search, &$key = null, &$value = null) {
$value2 = null;
if ($this->find($lowerBrowser, array_keys($search), $key, $value2)) {
$value = $search[$value2];
return true;
}
return false;
}
}
@luigifab
Copy link
Author

luigifab commented Aug 17, 2019

Test:

require_once('Useragentparser.php');

$bob = new Luigifab_Apijs_Model_Useragentparser();
print_r($bob->parse("Mozilla/5.0 (Windows NT 10.0; rv:115.0) Gecko/20100101 Thunderbird/115.5.0"));
print_r($bob->parse("Mozilla/5.0 (X11; U; CrOS i686 0.9.128; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.339"));
print_r($bob->parse("Mozilla/5.0 (X11; Linux x86_64; rv:45.0) (X12; Limux x86; rv:46.0) Gecko/20100101 Firefox/45.0"));

$data = file_get_contents('https://raw.githubusercontent.com/donatj/PhpUserAgent/master/tests/user_agents.dist.json');
$data = json_decode($data, true);
foreach ($data as $key => $res) {
	if ($bob->parse($key) != $res) {
		echo $key,"\n";
		exit;
	}
}
echo "ok\n";
echo PHP_VERSION,"\n";

@luigifab
Copy link
Author

luigifab commented Aug 17, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment