'/^[a-z][0-9]+/u', $model)) {
$model[0] = strtoupper($model[0]);
}
return $model;
}
]);
$this->data->device->identifyModel('/SHARP[-_\/]([^\/\;]*)/u', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Sharp'
]);
$this->data->device->identifyModel('/Spice\s([^\s]+)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Spice'
]);
$this->data->device->identifyModel('/Spice\s?([A-Z][0-9]+)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Spice'
]);
$this->data->device->identifyModel('/Tecno([^\/]*)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Tecno'
]);
$this->data->device->identifyModel('/T-smart_([^\/]*)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'T-smart'
]);
$this->data->device->identifyModel('/TCL[-_ ]([^\/\;\)]*)/ui', $ua, [
'manufacturer' => 'TCL'
]);
$this->data->device->identifyModel('/Tiphone ([^\/]*)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'TiPhone'
]);
$this->data->device->identifyModel('/Toshiba[-\/]([^\/-]*)/u', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Toshiba'
]);
$this->data->device->identifyModel('/UTStar(?:com)?-([^\s\.\/;]+)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'UTStarcom'
]);
$this->data->device->identifyModel('/vk-(vk[0-9]+)/u', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'VK Mobile',
'model' => function ($model) {
return strtoupper($model);
}
]);
$this->data->device->identifyModel('/Videocon[-_ \/]([A-Z0-9\.]+)/iu', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Videocon'
]);
$this->data->device->identifyModel('/Vodafone(?:[ _-]Chat)?[ _-]?([0-9]+)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Vodafone'
]);
$this->data->device->identifyModel('/Vodafone\/[0-9.]+\/(v[0-9]+)[^\/]*\//u', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Vodafone'
]);
$this->data->device->identifyModel('/^VSUN([0-9]+[A-Z]?)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Vsun'
]);
$this->data->device->identifyModel('/Wynncom[\-\s]([A-Z0-9\s]+\+?)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Wynncom'
]);
$this->data->device->identifyModel('/^YUANDA([0-9]+[A-Z]?)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Yuanda'
]);
$this->data->device->identifyModel('/^ZEN[_\s]([A-Z0-9\s\+]+)\*?[\s_]?($|\/|-|;|Dorado|MAUI|WAP|R2AE|Q03C)/uiU', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Zen'
]);
$this->data->device->identifyModel('/^(?:Ziox[_\s])?Ziox[_\s](ZX?[0-9]+)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'Ziox'
]);
$this->data->device->identifyModel('/ZTE[-_\s]?([^\s\/\(\);,]+)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'manufacturer' => 'ZTE',
'model' => function ($model) {
return preg_match('/[A-Z]+[0-9]+/iu', $model) ? strtoupper($model) : $model;
}
]);
$this->identifyBasedOnIdentifier();
}
/* Japanese models */
private function detectJapaneseMobileModels($ua)
{
if (isset($this->data->device->manufacturer)) {
return;
}
if ($this->data->os->isFamily('Android')) {
return;
}
/* Sometimes DoCoMo UA strings are (partially) encoded */
if (preg_match('/^DoCoMo/u', $ua)) {
$ua = preg_replace_callback(
"#\\\x([0-9A-Fa-f]{2})#",
function ($m) {
return chr(hexdec($m[1]));
},
$ua
);
}
/* First identify it based on id */
$model = null;
$manufacturer = null;
$carrier = null;
$falsepositive = false;
$ids = [
'CA' => 'Casio',
'DL' => 'Dell',
'ER' => 'Ericsson',
'HT' => 'HTC',
'HW' => 'Huawei',
'IA' => 'Inventec',
'JR' => 'JRC',
'KO' => 'Kokusai',
'LC' => 'Longcheer',
'NK' => 'Nokia',
'NM' => 'Nokia',
'KE' => 'KES',
'SA' => 'Sanyo',
'SC' => 'Samsung',
'SS' => 'Samsung',
'SH' => 'Sharp',
'SE' => 'Sony Ericsson',
'SO' => 'Sony',
'ZT' => 'ZTE',
'F' => 'Fujitsu',
'D' => 'Mitsubishi',
'J' => 'JRC',
'K' => 'Kyocera',
'L' => 'LG',
'M' => 'Motorola',
'N' => 'NEC',
'P' => 'Panasonic',
'R' => 'JRC',
'T' => 'Toshiba',
'Z' => 'ZTE',
];
if (preg_match('/(?:^|[\s\/\-\(;])((' . implode('|', array_keys($ids)) . ')[0-9]{3,3}[a-z]+[A-Z]*)/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$carrier = 'DoCoMo';
}
if (preg_match('/(?:; |\()((' . implode('|', array_keys($ids)) . ')[0-9]{2,2}[A-Z][0-9]?)[\);]/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$carrier = 'DoCoMo';
}
if (preg_match('/DoCoMo\/[0-9].0 ((' . implode('|', array_keys($ids)) . ')[0-9]{2,2}[A-Z][0-9]?)(?:\s?\(|$)/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$carrier = 'DoCoMo';
}
if (preg_match('/DoCoMo\/[0-9].0[\/\s](?:MST_v_)?((' . implode('|', array_keys($ids)) . ')[1-9][0-9]{3,3}[A-Z]?)/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$carrier = 'DoCoMo';
}
if (preg_match('/[\/\(]([SHW][0-9]{2,2}(' . implode('|', array_keys($ids)) . '))[\/;]/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$carrier = 'EMOBILE';
}
if (preg_match('/\) ([SHW][0-9]{2,2}(' . implode('|', array_keys($ids)) . '))$/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$carrier = 'EMOBILE';
}
if (preg_match('/[\s\/\-\(;](J-(' . implode('|', array_keys($ids)) . ')[0-9]{2,2})/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$carrier = 'Softbank';
}
if (preg_match('/(?:^|; |\/)([0-9]{3,3}(' . implode('|', array_keys($ids)) . '))[eps]?[\/\)]/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$carrier = 'Softbank';
}
if (preg_match('/\(([0-9]{3,3}(' . implode('|', array_keys($ids)) . ')[eps]?);SoftBank/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$carrier = 'Softbank';
}
if (preg_match('/(?:^|[\s\/\(;])((V|DM|W|WS|WX)[0-9]{2,3}(' . implode('|', array_keys($ids)) . '))/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[3];
switch ($match[2]) {
case 'V':
$carrier = 'Softbank';
break;
case 'DM':
$carrier = 'Disney Mobile';
break;
case 'W':
case 'WS':
case 'WX':
$carrier = 'Willcom';
break;
}
}
if (preg_match('/(AH-(' . implode('|', array_keys($ids)) . ')[1-9][0-9]{3,3}[A-Z]?)/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$carrier = 'Willcom';
}
if (in_array($model, [ '360SE' ])) {
$falsepositive = true;
}
if (!$falsepositive && !empty($model) && !empty($manufacturer)) {
$this->data->device->reset([
'type' => Constants\DeviceType::MOBILE,
'model' => $model,
'carrier' => $carrier
]);
if (array_key_exists($manufacturer, $ids)) {
$this->data->device->manufacturer = $ids[$manufacturer];
}
$this->data->device->identified |= Constants\Id::PATTERN;
/* Set flags for MOAP */
switch ($model) {
case 'F06B':
case 'F07B':
case 'F08B':
case 'SH07B':
$this->data->os->reset([ 'family' => new Family([ 'name' => 'Symbian' ]) ]);
$this->data->device->flag = Constants\Flag::MOAPS;
break;
}
return;
}
/* Then KDDI model number */
$ids = [
'CA' => 'Casio',
'DE' => 'Denso',
'PT' => 'Pantech',
'SA' => 'Sanyo',
'ST' => 'Sanyo',
'SH' => 'Sharp',
'H' => 'Hitachi',
'K' => 'Kyocera',
'P' => 'Panasonic',
'S' => 'Sony Ericsson',
'T' => 'Toshiba'
];
if (preg_match('/(?:^|KDDI-)(W[0-9]{2,2}(' . implode('|', array_keys($ids)) . '))[;\)\s\/]/u', $ua, $match)) {
$model = $match[1];
$manufacturer = $match[2];
$this->data->device->reset([
'type' => Constants\DeviceType::MOBILE,
'model' => $model,
'carrier' => 'au'
]);
if (array_key_exists($manufacturer, $ids)) {
$this->data->device->manufacturer = $ids[$manufacturer];
}
$this->data->device->identified |= Constants\Id::PATTERN;
return;
}
/* Then identify it based on KDDI id */
$ids = [
'CA' => 'Casio',
'DN' => 'Denso',
'ER' => 'Ericsson',
'FJ' => 'Fujitsu',
'HI' => 'Hitachi',
'KC' => 'Kyocera',
'MA' => 'Panasonic',
'MI' => 'Mitsubishi',
'PT' => 'Pantech',
'SA' => 'Sanyo',
'ST' => 'Sanyo',
'SY' => 'Sanyo',
'SH' => 'Sharp',
'SN' => 'Sony Ericsson',
'TS' => 'Toshiba'
];
if (preg_match('/(?:^|KDDI-|UP\. ?Browser\/[0-9\.]+-|; )((' . implode('|', array_keys($ids)) . ')(?:[0-9][0-9]|[A-Z][0-9]|[0-9][A-Z]))($|[;\)\s])/ui', $ua, $match)) {
$model = strtoupper($match[1]);
$manufacturer = strtoupper($match[2]);
$falsepositive = false;
if (in_array($model, [ 'MAM2', 'MAM3' ])) {
$falsepositive = true;
}
if (!$falsepositive) {
$this->data->device->reset([
'type' => Constants\DeviceType::MOBILE,
'model' => $model,
'carrier' => 'au'
]);
if (array_key_exists($manufacturer, $ids)) {
$this->data->device->manufacturer = $ids[$manufacturer];
$device = Data\DeviceModels::identify('kddi', $model);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$device->carrier = 'au';
$this->data->device = $device;
}
}
$this->data->device->identified |= Constants\Id::PATTERN;
return;
}
}
/* Finally identify it based on carrier */
$this->data->device->identifyModel('/\(([A-Z]+[0-9]+[A-Z])[^;]*; ?FOMA/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'carrier' => 'DoCoMo'
]);
$this->data->device->identifyModel('/\(FOMA ([^;]+)+;/u', $ua, [
'type' => Constants\DeviceType::MOBILE,
'carrier' => 'DoCoMo'
]);
$this->data->device->identifyModel('/DoCoMo\/[0-9].0[\/\s]([0-9A-Z]+)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'carrier' => 'DoCoMo'
]);
$this->data->device->identifyModel('/NTTDoCoMo ([0-9A-Z]+)/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'carrier' => 'DoCoMo'
]);
$this->data->device->identifyModel('/J-PHONE\/[^\/]+\/([^\/_]+)/u', $ua, [
'type' => Constants\DeviceType::MOBILE,
'carrier' => 'Softbank'
]);
$this->data->device->identifyModel('/SoftBank\/[^\/]+\/([^\/]+)\//u', $ua, [
'type' => Constants\DeviceType::MOBILE,
'carrier' => 'Softbank'
]);
$this->data->device->identifyModel('/Vodafone\/[0-9.]+\/V([0-9]+[A-Z]+)[^\/]*\//ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'carrier' => 'Softbank'
]);
$this->data->device->identifyModel('/(KDDI-[^\s\)\.;]{4,})/ui', $ua, [
'type' => Constants\DeviceType::MOBILE,
'carrier' => 'au'
]);
if (!empty($this->data->device->model)) {
$this->identifyBasedOnId($this->data->device->model);
}
}
/* Device models not identified by a prefix */
private function detectGenericMobileLocations($ua)
{
if ($this->data->device->identified & Constants\Id::PATTERN) {
return;
}
$candidates = [];
if (preg_match('/^([a-z0-9\.\_\+\/ ]+)_TD\//iu', $ua, $match)) {
array_push($candidates, $match[1]);
}
if (preg_match('/^([a-z0-9\_]+)/iu', $ua, $match)) {
array_push($candidates, $match[1]);
}
if (preg_match('/[; ]\(?([^\s\)\/;]+)[^\s;]*$/u', $ua, $match)) {
array_push($candidates, $match[1]);
}
if (preg_match('/^([^\/\)]+)/u', $ua, $match)) {
array_push($candidates, $match[1]);
}
if (preg_match('/MobilePhone ([^\/\)]+)/u', $ua, $match)) {
array_push($candidates, $match[1]);
}
$candidates = array_diff($candidates, [
'Mobile', 'Safari', 'Version', 'GoogleTV', 'WebKit', 'NetFront',
'Microsoft', 'ZuneWP7', 'Firefox', 'UCBrowser', 'IEMobile', 'Touch',
'Fennec', 'Minimo', 'Gecko', 'TizenBrowser', 'Browser', 'sdk',
'Mini', 'Fennec', 'Darwin', 'Puffin', 'Tanggula', 'Edge',
'QHBrowser', 'BonEcho', 'Iceweasel', 'Midori', 'BeOS', 'UBrowser',
'SeaMonkey', 'Model', 'Silk-Accelerated=true', 'Configuration',
'UNTRUSTED', 'OSRE', 'Dolfin', 'Surf', 'Epiphany', 'Konqueror',
'Presto', 'OWB', 'PmWFx', 'Netscape', 'Netscape6', 'Navigator',
'Opera', 'Mozilla', 'BrightSign', 'Motorola', 'UCWEB',
'NativeOperaMini', 'OperaMini', 'SogouMobileBrowser', 'iLunascape',
'Sleipnir', 'MobileSafari', 'MQQBrowser', 'BREW', '?',
'Maxthon', '360%20Browser', 'OPR', 'CFNetwork', 'JUC', 'Skyfire',
'UP.Browser', 'DolphinHDCN', 'NintendoBrowser', 'NCSA',
'NCSA Mosaic', 'NCSA_Mosaic', 'U', 'NetFrontNX', 'QtWebKit',
'HtmlRenderer', 'HbbTV', 'WebAppManager', 'SmartTV', 'UPLUSTVBROWSER',
'LG Browser', 'LG', 'LGSmartTV', 'OBIGO-T10', 'Linux', 'DLNADOC',
'Aplix_SANYO_browser', 'Japanese', 'WebBrowser', 'Freetime',
'OreganMediaBrowser', 'NETRANGEMMH', 'http:', 'bxapi', 'Kodi',
'XBMC', 'KreaTVWebKit', 'MachBlue', 'Espial', 'TouchPad',
'sharp', 'sharp wd browser', 'sharp pda browser', 'browser',
'Palmscape', 'CorePlayer', 'Xiino', 'SONY', 'WorldTALK', 'TOPS',
'Windows', 'Microsoft Pocket Internet Explorer', 'Explorer',
'CE', 'Desktop', 'Maemo Browser', 'Maemo', 'baidubrowser',
'Mercury', 'BREW-Applet', 'ucweb-squid', 'iSurf', '3gpp-gba',
'InfoPath.2', 'UC', 'J2ME', 'IUC', 'AveFront', 'MMP', 'BaiduHD',
'360%20Lite', '360', 'AppleWebKit', 'Instagram', 'FBOP',
'Nuanti', 'NuantiMeta', 'Silk', 'VTE', 'DreamKey', 'DreamPassport',
'Aplix_SEGASATURN_browser', 'NWF', 'Bunjalloo', 'libwww',
'Inferno', 'NEXT', 'I', 'Microsoft Internet Explorer', 'MAM3',
'MAM2', '360SE', 'Ziepod', 'Vista', 'XP', 'Links', 'Syllable',
'sun4m', 'sun4c', 'sun4u', 'i86pc', 'X11', 'NaenaraBrowser',
'QuickTime', 'IBM', 'QQBrowser', 'x86_64', 'i686', 'i386', 'Chrome',
'TenFourFox', 'Swing', 'NetFrontBrowserNX', 'Mac_PowerPC',
'NetCast.TV-2012', 'NetCast.TV-2011', 'NetCast.Media-2011',
'PaleMoon', 'Fedora', 'SUSE', 'iCab', 'Googlebot', 'Pixi',
'Pre', 'ELinks', 'developer', 'beta', 'BingPreview', 'IBrowse', '+http:'
]);
$candidates = array_unique($candidates);
foreach ($candidates as $i => $id) {
if (preg_match('/^[0-9\.]+$/u', $id)) {
unset($candidates[$i]);
continue;
}
if (preg_match('/^[0-9]+[xX][0-9]+$/u', $id)) {
unset($candidates[$i]);
continue;
}
if (preg_match('/^\[?[a-z]{2,2}(\-[a-z]{2,2})?\]?$/ui', $id)) {
unset($candidates[$i]);
continue;
}
if (strlen($id) < 4) {
unset($candidates[$i]);
continue;
}
}
foreach ($candidates as $i => $id) {
$this->identifyBasedOnIdUsingOs($id);
if ($this->data->device->identified & Constants\Id::MATCH_UA) {
return;
}
}
}
private function identifyBasedOnIdentifier()
{
if ($this->data->device->identified & Constants\Id::MATCH_UA) {
return;
}
$ids = [];
if (!empty($this->data->device->identifier)) {
$ids[] = $this->data->device->identifier;
}
if (!empty($this->data->device->model)) {
$ids[] = $this->data->device->model;
}
foreach ($ids as $i => $id) {
$this->identifyBasedOnIdUsingOs($id);
if ($this->data->device->identified & Constants\Id::MATCH_UA) {
return;
}
}
foreach ($ids as $i => $id) {
$this->identifyBasedOnId($id);
if ($this->data->device->identified & Constants\Id::MATCH_UA) {
return;
}
}
}
private function identifyBasedOnIdUsingOs($id)
{
switch ($this->data->os->getFamily()) {
case 'Android':
$device = Data\DeviceModels::identify('android', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
}
break;
case 'Brew':
$device = Data\DeviceModels::identify('brew', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
}
break;
case 'Symbian':
$device = Data\DeviceModels::identify('symbian', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
}
break;
case 'Windows':
case 'Windows CE':
case 'Windows Mobile':
$device = Data\DeviceModels::identify('wm', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
if (!$this->data->isOs('Windows Mobile')) {
$this->data->os->reset([
'name' => 'Windows Mobile'
]);
}
}
break;
default:
$device = Data\DeviceModels::identify('feature', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
}
break;
}
}
private function identifyBasedOnId($id)
{
if ($this->data->device->type != 'mobile') {
return;
}
if (!($this->data->device->identified & Constants\Id::MATCH_UA)) {
$device = Data\DeviceModels::identify('brew', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
if (!in_array($this->data->os->name, [ 'Brew', 'Brew MP' ])) {
$this->data->os->name = 'Brew';
}
}
}
if (!($this->data->device->identified & Constants\Id::MATCH_UA)) {
$device = Data\DeviceModels::identify('bada', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
$this->data->os->name = 'Bada';
}
}
if (!($this->data->device->identified & Constants\Id::MATCH_UA)) {
$device = Data\DeviceModels::identify('touchwiz', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
$this->data->os->name = 'Touchwiz';
}
}
if (!($this->data->device->identified & Constants\Id::MATCH_UA)) {
$device = Data\DeviceModels::identify('symbian', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
$this->data->os->reset([
'family' => new Family([ 'name' => 'Symbian' ])
]);
}
}
if (!($this->data->device->identified & Constants\Id::MATCH_UA)) {
$device = Data\DeviceModels::identify('wm', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
$this->data->os->name = 'Windows Mobile';
}
}
if (!($this->data->device->identified & Constants\Id::MATCH_UA)) {
$device = Data\DeviceModels::identify('feature', $id);
if ($device->identified) {
$device->identified |= $this->data->device->identified;
$this->data->device = $device;
}
}
}
}