예제 #1
0
 function lookup($user_agent)
 {
     if (owa_coreAPI::getSetting('base', 'cache_objects')) {
         owa_coreAPI::profile($this, __FUNCTION__, __LINE__);
         $cache_obj = $this->cache->get('browscap', $this->ua);
     }
     if (!$cache_obj) {
         $custom_db = owa_coreAPI::getSetting('base', 'ua-regexes');
         if ($custom_db) {
             $parser = new UAParser($custom_db);
         } else {
             $parser = new UAParser();
         }
         $cap = $parser->parse($this->ua);
     } else {
         $cap = $cache_obj;
     }
     if (!empty($cap)) {
         if (owa_coreAPI::getSetting('base', 'cache_objects')) {
             $family = $cap->ua->family;
             if ($family != 'Default Browser') {
                 $this->cache->set('browscap', $this->ua, $cap, $this->cacheExpiration);
             }
         }
     }
     return $cap;
 }
예제 #2
0
function track()
{
    set();
    global $dbh;
    global $database;
    $ua = $_SERVER['HTTP_USER_AGENT'];
    $parser = new UAParser();
    $result = $parser->parse($ua);
    // Add entry into track
    $_REQUEST['track_time'] = time();
    $_REQUEST['ip'] = $_SERVER['REMOTE_ADDR'];
    sql('track');
    // Add entry into user
    sql('user');
    exit;
}
 /**
  * column_default function.
  *
  * @access public
  *
  * @param mixed $log
  * @param mixed $column_name
  *
  * @return void
  */
 function column_default($log, $column_name)
 {
     switch ($column_name) {
         case 'status':
             switch ($log->download_status) {
                 case 'failed':
                     $download_status = '<span class="failed" title="' . esc_attr($log->download_status_message) . '">&nbsp;</span>';
                     break;
                 case 'redirected':
                     $download_status = '<span class="redirected" title="' . esc_attr($log->download_status_message) . '">&nbsp;</span>';
                     break;
                 default:
                     $download_status = '<span class="completed" title="' . __('Download Complete', 'download-monitor') . '">&nbsp;</span>';
                     break;
             }
             return $download_status;
             break;
         case 'date':
             return '<time title="' . date_i18n(get_option('date_format') . ' @ ' . get_option('time_format'), strtotime($log->download_date)) . '"">' . sprintf(__('%s ago', 'download-monitor'), human_time_diff(strtotime($log->download_date), current_time('timestamp'))) . '</time>';
             break;
         case 'download':
             $download = new DLM_Download($log->download_id);
             $download->set_version($log->version_id);
             if (!$download->exists()) {
                 $download_string = sprintf(__('Download #%d (no longer exists)', 'download-monitor'), $log->download_id);
             } else {
                 $download_string = '<a href="' . admin_url('post.php?post=' . $download->id . '&action=edit') . '">';
                 $download_string .= '#' . $download->id . ' &ndash; ' . $download->get_the_title();
                 $download_string .= '</a>';
             }
             if ($log->version) {
                 $download_string .= ' (' . sprintf(__('v%s', 'download-monitor'), $log->version) . ')';
             }
             return $download_string;
             break;
         case 'file':
             $download = new DLM_Download($log->download_id);
             $download->set_version($log->version_id);
             if ($download->exists() && $download->get_the_filename()) {
                 $download_string = '<code>' . $download->get_the_filename() . '</code>';
             } else {
                 $download_string = '&ndash;';
             }
             return $download_string;
             break;
         case 'user':
             if ($log->user_id) {
                 $user = get_user_by('id', $log->user_id);
             }
             if (!isset($user) || !$user) {
                 $user_string = __('Non-member', 'download-monitor');
             } else {
                 $user_string = '<a href="' . admin_url('user-edit.php?user_id=' . $user->ID) . '">';
                 $user_string .= $user->user_login . ' &ndash; ';
                 $user_string .= '<a href="mailto:' . $user->user_email . '">';
                 $user_string .= $user->user_email;
                 $user_string .= '</a>';
             }
             return $user_string;
             break;
         case 'user_ip':
             return '<a href="http://whois.arin.net/rest/ip/' . $log->user_ip . '" target="_blank">' . $log->user_ip . '</a>';
             break;
         case 'user_ua':
             $ua = $this->uaparser->parse($log->user_agent);
             return $ua->toFullString;
             break;
     }
 }
예제 #4
0
 /**
  * Gets the current user environment details by user id
  * @param unknown_type $user_id
  * @param unknown_type $create_if_empty
  * @return multitype:unknown
  */
 public static function get_user_environment_details($user_id, $create_if_empty, $data_services)
 {
     $ua = $_SERVER['HTTP_USER_AGENT'];
     $parser = new UAParser();
     $result = $parser->parse($ua);
     $browser = $result->ua->family . ' ' . $result->ua->major;
     if ($result->ua->minor) {
         $browser .= '.' . $result->ua->minor;
     }
     $device = $result->device->family;
     $os = $result->os->family . ' ' . $result->os->major;
     if ($result->os->minor) {
         $os .= '.' . $result->os->minor;
     }
     $user_environment_id = '';
     $current_time = current_time('mysql');
     // don't insert if user_id has not been provided
     if ($data_services != null && $user_id != null) {
         $params = array('user_id' => $user_id, 'create_if_empty' => $create_if_empty, 'browser' => $browser, 'os' => $os, 'device' => $device, 'current_time' => $current_time);
         $data = $data_services->custom_query('add_retrieve_user_environment_details', $params);
         $user_environment_id = $data->user_environment_id;
     }
     return array('user_environment_id' => $user_environment_id, 'os' => $os, 'device' => $device, 'browser' => $browser);
 }
function save_click_info($arr_click_info, $timeshift = 0)
{
    // User-agent parser
    require_once _TRACK_LIB_PATH . "/ua-parser/uaparser.php";
    $parser = new UAParser();
    // WURFL mobile database
    global $wurflManager;
    if ($timeshift != 0) {
        $click_date = date("Y-m-d H:i:s", strtotime($arr_click_info[0]) + $timeshift);
    } else {
        $click_date = $arr_click_info[0];
    }
    $click_day = current(explode(' ', $click_date));
    $click_hour = get_hour_by_date($click_date);
    $click_ip = $arr_click_info[1];
    // Get geo from IP
    $geo_data = get_geodata($click_ip);
    $click_country = $geo_data['country'];
    $click_state = $geo_data['state'];
    $click_city = $geo_data['city'];
    $click_region = $geo_data['region'];
    $click_isp = $geo_data['isp'];
    // Get info from user agent
    $click_user_agent = $arr_click_info[2];
    // Set empty initial values
    $is_mobile_device = false;
    $is_tablet = false;
    $is_phone = false;
    $brand_name = '';
    $model_name = '';
    $model_extra_info = '';
    $device_os = '';
    $device_os_version = '';
    $device_browser = '';
    $device_browser_version = '';
    if (extension_loaded('xmlreader')) {
        $requestingDevice = $wurflManager->getDeviceForUserAgent($click_user_agent);
        $is_wireless = $requestingDevice->getCapability('is_wireless_device') == 'true';
        $is_tablet = $requestingDevice->getCapability('is_tablet') == 'true';
        $is_mobile_device = $is_wireless || $is_tablet;
        // Use WURFL database info for mobile devices only
        if ($is_mobile_device) {
            $is_phone = $requestingDevice->getCapability('can_assign_phone_number') == 'true';
            $brand_name = $requestingDevice->getCapability('brand_name');
            $model_name = $requestingDevice->getCapability('model_name');
            $model_extra_info = $requestingDevice->getCapability('model_extra_info');
            $device_os = $requestingDevice->getCapability('device_os');
            $device_os_version = $requestingDevice->getCapability('device_os_version');
            $device_browser = $requestingDevice->getCapability('mobile_browser');
            $device_browser_version = $requestingDevice->getCapability('mobile_browser_version');
        } else {
            // Use UAParser to get click info
            $result = $parser->parse($click_user_agent);
            $device_browser = $result->ua->family;
            $device_browser_version = $result->ua->toVersionString;
            $device_os = $result->os->family;
            $device_os_version = $result->os->toVersionString;
        }
    }
    $click_referer = $arr_click_info[3];
    $click_link_name = $arr_click_info[4];
    $click_link_source = $arr_click_info[5];
    // Allow to use - as campaign/ads delimiter
    $link_ads_name = $arr_click_info[6];
    if (strpos($link_ads_name, '-') !== false) {
        $click_link_campaign = current(explode('-', $link_ads_name));
        $click_link_ads = substr($link_ads_name, strpos($link_ads_name, '-') + 1);
    } else {
        $click_link_campaign = $link_ads_name;
        $click_link_ads = '';
    }
    $click_subid = $arr_click_info[7];
    $click_subaccount = $arr_click_info[8];
    $click_rule_id = $arr_click_info[9];
    $click_out_id = $arr_click_info[10];
    $click_is_unique = $arr_click_info[11];
    $click_param1 = $arr_click_info[12];
    $click_param2 = $arr_click_info[13];
    $click_param3 = $arr_click_info[14];
    $click_param4 = $arr_click_info[15];
    $click_param5 = $arr_click_info[16];
    // Parse get string
    if (!empty($arr_click_info[17])) {
        parse_str($arr_click_info[17], $click_get_params);
    } else {
        $click_get_params = array();
    }
    if (!empty($_GET['debug'])) {
        dmp($arr_click_info);
        //die();
    }
    $sql_click_params = array();
    // Save this source params
    global $source_config;
    /*
    to_log('src', $click_link_source);
    to_log('src', $source_config);
    to_log('src', $click_get_params);
    */
    $i = 1;
    // Source config exists
    if (array_key_exists($click_link_source, $source_config) and array_key_exists('params', $source_config[$click_link_source])) {
        // Выбираем именованные параметры из того, что пришло
        foreach ($source_config[$click_link_source]['params'] as $param_name => $param_info) {
            if (empty($param_info['url'])) {
                continue;
            }
            // "виртуальный" параметр, он определяется не ссылкой, а через другие параметры, см ниже
            if (array_key_exists($param_name, $click_get_params)) {
                $param_value = $click_get_params[$param_name];
                if ($param_info['url'] == $param_value) {
                    $param_value = '';
                }
                // Пришло site_id={site_id}, то есть значение пустое
                $sql_click_params[] = "click_param_name{$i}='" . _str($param_name) . "', click_param_value{$i}='" . _str($param_value) . "'";
                // Adwords передает в одном параметре и то что это спецразмещение и то, что это реклама в сайдбаре и сразу же позицию объявления. Поэтому мы разбиваем это значение на два параметра, Размещение (если t - Спецразмещение, если s - Реклама справа, если o или что-то другое - Не определено) и Позиция (где выводим значения как есть, то есть 1t1, 1s2 и т.д.)
                // Пример "виртуального параметра"
                if ($click_link_source == 'adwords' and $param_name == 'adposition') {
                    $i++;
                    $position_type = 0;
                    if (strstr($param_value, 's') !== false) {
                        $position_type = 's';
                    }
                    if (strstr($param_value, 't') !== false) {
                        $position_type = 't';
                    }
                    $sql_click_params[] = "click_param_name{$i}='position_type', click_param_value{$i}='" . _str($position_type) . "'";
                }
                // Поисковые слова Яндекса
                if ($click_link_source == 'yadirect' and $param_name == 'ad_id') {
                    $i++;
                    // 17 - для прямых ссылок, 3 - для обычных
                    $referer = empty($arr_click_info[3]) ? $arr_click_info[17] : $arr_click_info[3];
                    $sql_click_params[] = "click_param_name{$i}='text', click_param_value{$i}='" . _str(parse_search_refer($referer)) . "'";
                }
                unset($click_get_params[$param_name]);
                // Параметр отработан, убираем его чтобы остались только пользовательские
            }
            $i++;
        }
        // Удаляем параметры из чёрной списка (для трекеров, которые шлют нам много лишнего)
        if (isset($source_config[$click_link_source]['rapams_ignore'])) {
            foreach ($source_config[$click_link_source]['rapams_ignore'] as $param_name) {
                unset($click_get_params[$param_name]);
            }
        }
        // Удаляем дополнительные параметры "прямого перехода"
        $direct_params = array('utm_source', 'rule_name', 'utm_campaign');
        foreach ($direct_params as $param_name) {
            unset($click_get_params[$param_name]);
        }
        /*
        foreach($click_get_params as $param_name => $param_value) {
        	if(!empty($source_config[$click_link_source]['params'][$param_name]['n'])) {
        		//$i = $source_config[$click_link_source]['params'][$param_name]['n'] + 5;
        		$sql_click_params[]="click_param_name{$i}='"._str($param_name)."', click_param_value{$i}='"._str($param_value)."'";
        		
        		// Adwords передает в одном параметре и то что это спецразмещение и то, что это реклама в сайдбаре и сразу же позицию объявления. Поэтому мы разбиваем это значение на два параметра, Размещение (если t - Спецразмещение, если s - Реклама справа, если o или что-то другое - Не определено) и Позиция (где выводим значения как есть, то есть 1t1, 1s2 и т.д.) 
        		
        		if($click_link_source == 'adwords' and $param_name == 'adposition') {
        			$position_type = 0;
        			if(strstr($param_value, 's') !== false) {
        				$position_type = 's';
        			}
        			
        			if(strstr($param_value, 't') !== false) {
        				$position_type = 't';
        			}
        			$sql_click_params[]="click_param_name10='position_type', click_param_value10='"._str($position_type)."'";
        		}
        		
        		unset($click_get_params[$param_name]);
        	}
        }
        */
    }
    // Пользовательские параметры
    $is_connected = false;
    $connected_subid = '';
    foreach ($click_get_params as $param_name => $param_value) {
        if ($param_name == '_subid') {
            $pattern = '/\\d{14}x\\d{5}/';
            preg_match_all($pattern, $param_value, $subids);
            foreach ($subids[0] as $t_key => $t_subid) {
                if ($t_subid != '') {
                    $is_connected = true;
                    $connected_subid = $t_subid;
                }
                break;
            }
            continue;
        }
        $sql_click_params[] = "click_param_name{$i}='" . _str($param_name) . "', click_param_value{$i}='" . _str($param_value) . "'";
        $i++;
        // Maximum 15 get parameters allowed
        if ($i > 15) {
            break;
        }
    }
    $sql_click_params = implode(', ', $sql_click_params);
    if (strlen($sql_click_params) > 0) {
        $sql_click_params = ", {$sql_click_params}";
    }
    // Click from landing page
    if ($is_connected) {
        // Get parent click id
        $sql = "select id from tbl_clicks where subid='" . _str($connected_subid) . "' limit 1";
        $result = mysql_query($sql);
        $row = mysql_fetch_assoc($result);
        if ($row['id'] > 0) {
            $parent_id = $row['id'];
            $sql = "update tbl_clicks set is_parent=1 where id='" . _str($parent_id) . "'";
            mysql_query($sql);
        } else {
            $parent_id = 0;
        }
    }
    $sql = "insert ignore into tbl_clicks SET\n\t\t\t\tdate_add='" . _str($click_date) . "', \n\t\t\t\tdate_add_day='" . _str($click_day) . "', \n\t\t\t\tdate_add_hour='" . _str($click_hour) . "', \n\t\t\t\tuser_ip='" . _str($click_ip) . "', \n\t\t\t\tuser_agent='" . _str($click_user_agent) . "', \n\t\t\t\tuser_os='" . _str($device_os) . "', \n\t\t\t\tuser_os_version='" . _str($device_os_version) . "', \t\t\t\t\n\t\t\t\tuser_platform='" . _str($brand_name) . "', \n\t\t\t\tuser_platform_info='" . _str($model_name) . "', \t\t\n\t\t\t\tuser_platform_info_extra='" . _str($model_extra_info) . "',\t\t\t\n\t\t\t\tuser_browser='" . _str($device_browser) . "', \n\t\t\t\tuser_browser_version='" . _str($device_browser_version) . "',\t\t\t\t\t\n\t\t\t\tis_mobile_device='" . _str($is_mobile_device) . "', \n\t\t\t\tis_phone='" . _str($is_phone) . "', \t\t\n\t\t\t\tis_tablet='" . _str($is_tablet) . "', \t\t\t\t\t\n\t\t\t\tcountry='" . _str($click_country) . "', \n\t\t\t\tstate='" . _str($click_state) . "', \n\t\t\t\tcity='" . _str($click_city) . "', \n\t\t\t\tregion='" . _str($click_region) . "', \n\t\t\t\tisp='" . _str($click_isp) . "', \n\t\t\t\trule_id='" . _str($click_rule_id) . "', \n\t\t\t\tout_id='" . _str($click_out_id) . "', \n\t\t\t\tsubid='" . _str($click_subid) . "', \n\t\t\t\tis_connected='" . _str($is_connected) . "', \n\t\t\t\tis_unique='" . _str($click_is_unique) . "', \n\t\t\t\tparent_id='" . _str($parent_id) . "', \n\t\t\t\tsubaccount='" . _str($click_subaccount) . "', \n\t\t\t\tsource_name='" . _str($click_link_source) . "', \n\t\t\t\tcampaign_name='" . _str($click_link_campaign) . "', \n\t\t\t\tads_name='" . _str($click_link_ads) . "', \n\t\t\t\treferer='" . _str($click_referer) . "', \n\t\t\t\tsearch_string='', \n\t\t\t\tcampaign_param1='" . _str($click_param1) . "', \n\t\t\t\tcampaign_param2='" . _str($click_param2) . "', \n\t\t\t\tcampaign_param3='" . _str($click_param3) . "', \n\t\t\t\tcampaign_param4='" . _str($click_param4) . "', \n\t\t\t\tcampaign_param5='" . _str($click_param5) . "'\n\t\t\t\t{$sql_click_params}";
    //echo $sql . '<br />';
    echo $click_subid . ' ';
    mysql_query($sql);
    // or die($sql . '<br >' . mysql_error());
}
예제 #6
0
 if (isset($args["j"]) && $args["j"]) {
     /* Parse the supplied UA from the command line and kick it out as JSON */
     // load the parser
     $parser = new UAParser();
     // parse and encode the results
     if (version_compare(PHP_VERSION, '5.4.0', '>=') && isset($args["p"])) {
         print json_encode($parser->parse($args["j"]), JSON_PRETTY_PRINT);
     } else {
         print json_encode($parser->parse($args["j"]));
     }
     print PHP_EOL;
 } else {
     if (isset($argv[1]) && ($argv[1] != "-j" && $argv[1] != "-p" && $argv[1] != "-l" && $argv[1] != "-s" && $argv[1] != "-n")) {
         /* Parse the supplied UA from the command line and kick it out as JSON */
         // load the parser
         $parser = new UAParser();
         // parse and print the results
         $result = $parser->parse($argv[1]);
         print "  ua-parser results for \"" . $argv[1] . "\"\n";
         foreach ($result as $key => $value) {
             if (gettype($value) == "object") {
                 foreach ($value as $key2 => $value2) {
                     print "    " . $key . "->" . $key2 . ": " . $value2 . "\n";
                 }
             } else {
                 print "    " . $key . ": " . $value . "\n";
             }
         }
     } else {
         /* Print usage information */
         print "\n";
예제 #7
0
// Subaccount
$str .= $subid . "\t";
// Apply rules and get out id for current click
$arr_rules = get_rules($link_name);
if (count($arr_rules) == 0) {
    exit('Rule not found');
} else {
    $user_params = array();
    $user_params['agent'] = $_SERVER['HTTP_USER_AGENT'];
    if ($requestingDevice && ($requestingDevice->getCapability('is_wireless_device') == 'true' || $requestingDevice->getCapability('is_tablet') == 'true')) {
        $user_params['os'] = $requestingDevice->getCapability('device_os');
        $user_params['device'] = $requestingDevice->getCapability('brand_name') . '; ' . $requestingDevice->getCapability('model_name');
        $user_params['platform'] = $requestingDevice->getCapability('brand_name');
        $user_params['browser'] = $requestingDevice->getCapability('mobile_browser');
    } else {
        $parser = new UAParser();
        $result = $parser->parse($user_params['agent']);
        $user_params['browser'] = $result->ua->family;
        $user_params['os'] = $result->os->family;
        $user_params['device'] = '';
        $user_params['platform'] = '';
    }
    $user_params['ip'] = $ip;
    $user_params['city'] = $cur_city;
    $user_params['region'] = $cur_state;
    $user_params['provider'] = $isp;
    $user_params['lang'] = $user_lang;
    $user_params['referer'] = $_SERVER['HTTP_REFERER'];
    $user_params['geo_country'] = $cur_country;
    $relevant_params = array();
    foreach ($arr_rules['geo_country'] as $key => $value) {
예제 #8
0
 /**
  * Create a new BrowserInfo object for the given user agent string.
  * Instances may not be created directly, use the static newFromContext method instead.
  *
  * @param string $userAgent
  */
 protected function parseUserAgent($userAgent)
 {
     /**
      * A ua-parser object looks like this (simplified version of the actual object)
      * @source https://github.com/tobie/ua-parser
      *
      *     ua->family: Chrome
      *     ua->major: 24
      *     ua->minor: 0
      *     ua->patch: 1312
      *     os->family: Mac OS X
      *     os->major: 10
      *     os->minor: 8
      *     os->patch: 2
      *     device->family:
      *     toFullString: Chrome 24.0.1312/Mac OS X 10.8.2
      */
     $UAParserInstance = new UAParser();
     $parsed = $UAParserInstance->parse($userAgent);
     $uaData = new stdClass();
     $uaData->browserFamily = $parsed->ua->family;
     $uaData->browserMajor = $parsed->ua->major;
     $uaData->browserMinor = $parsed->ua->minor;
     $uaData->browserPatch = $parsed->ua->patch;
     $uaData->osFamily = $parsed->os->family;
     $uaData->osMajor = $parsed->os->major;
     $uaData->osMinor = $parsed->os->minor;
     $uaData->osPatch = $parsed->os->patch;
     $uaData->deviceFamily = $parsed->device->family;
     $uaData->deviceMajor = null;
     // deprecated
     $uaData->deviceMinor = null;
     // deprecated
     $uaData->displayInfo = self::getDisplayInfo($uaData);
     $this->rawUserAgent = $userAgent;
     $this->uaData = $uaData;
     return $this;
 }
예제 #9
0
 *
 * This is the test suit for ua-parser-php to make sure it matches
 * the standards set forth for ua-parser libraries.
 *
 * IMPORTANT: This test suite skips the Chrome Frame tests because
 *            the PHP lib doesn't support that feature.
 *
 */
// define the base path for the file
$basePath = dirname(__FILE__) . DIRECTORY_SEPARATOR;
// include the YAML library
require_once $basePath . "lib/spyc-0.5/spyc.php";
// include UAParser.php
require_once $basePath . "uaparser.php";
// set-up the parser
$parser = new UAParser();
/**
 * assert that the actual result and the expected result match
 * NOTE: the lib calls for certain attributes to be set to null but the test cases are empty strings
 * @param  any      the actual result of the parsing
 * @param  any      the expected result of the test
 * @return boolean  the result of the test
 */
function assertEqual($actual, $expected)
{
    $actual = $actual == null ? '' : $actual;
    $result = $actual === $expected ? true : false;
    return $result;
}
/**
 * reports the mismatch between a test and what was returned from uaparser.php
 /**
  * Main method - get all the source data about given user agent and parse it to create an object
  * with as much good and acurrate information as possible.
  * 
  * @param string $user_agent user agent string
  * 
  * @return UserAgentInfo
  */
 protected static function parse($user_agent)
 {
     // 1. >>> retrieve the data from sources
     $bc = self::$browscap_wrapper->getInfo($user_agent);
     self::$mobile_detect->setUserAgent($user_agent);
     $md = self::$mobile_detect;
     $uap = self::$uaparser->parse($user_agent);
     // 2. >>> parse the data
     // Mobile_Detect class is ONLY used for mobile devices, the data it returns for other devices is of much worse quality than the two other classes
     $md_is_mobile = $md->isMobile();
     $md_browser = $md_is_mobile ? self::parseBrowserMd($md) : false;
     $is_mobile = $md_is_mobile || $bc['isMobileDevice'];
     if (false === strpos($user_agent, 'WSCommand')) {
         // uaparser returns good quality results for browser detection because it's based on generic substrings
         $browser = self::parseBrowserUap($uap);
         // for mobile browsers we alternatively try Mobile_Detect
         if (!$browser) {
             $browser = $md_browser;
         }
         // if nothing worked we try browscap, this is very useful for legacy and exotic browsers
         if (!$browser) {
             $browser = self::parseBrowserBc($bc);
         }
     } else {
         // this is one very specific case - incorrect identification of WSCommand bot
         $browser = self::parseBrowserBc($bc);
     }
     // returning 'Android' for both browser and os is a bad idea
     if ('Android' == @$browser['name']) {
         $browser['name'] = 'Android Browser';
     }
     list($device['family'], $device['version']) = self::parseDevice($md, $uap);
     // uaparser is usually good here too, for the same reasons as with browsers
     $os = self::parseOsUap($uap);
     // sometimes uaparser tries to guess just the OS name and fails badly ...
     if (!isset($os['major']) && $md_is_mobile) {
         $os = self::parseOsMd($md);
     }
     if (!$os) {
         // if nothing was found revert to browscap, good with really old user agents
         $os = self::parseOsBc($bc);
     } elseif (self::UAPARSER_GENERIC_WINDOWS == $os['name']) {
         // if the OS name is 'Windows' (without version) then we try to override that data using browscap,
         // because if it returns anything it's always very specific
         $tmp_os = self::parseOsBc($bc);
         if ($tmp_os) {
             $os = $tmp_os;
         }
     }
     $architecture = self::parseArchitecture($user_agent);
     // trying to mark as many bots as possible
     $is_bot = $bc['Crawler'] || self::UAPARSER_BOT_NAME == $uap->device->family || $md->is(self::MOBILE_DETECT_BOT_NAME) || $md->is(self::MOBILE_DETECT_MOBILE_BOT_NAME);
     // that's an interesting feature, so why not include it
     $mobile_grade = $md_is_mobile && !empty($md_browser['name']) ? $md->mobileGrade() : '';
     // 3. >>> set the user agent identification level
     if ($is_mobile && !$is_bot && !empty($browser['name']) && !empty($os['name']) && (!empty($device['family']) || !empty($device['version']))) {
         // this is a mobile user (not a mobile bot) and was nicely and specifically identified by Mobile_Detect or uaparser
         $id_level = self::ID_LEVEL_FULL;
     } elseif (BrowscapWrapper::DEFAULT_NAME != $bc['Browser']) {
         // it's not a mobile user but it was identified by browscap, that's enough to provide full information
         $id_level = self::ID_LEVEL_FULL;
     } elseif (!empty($browser['name']) || $is_mobile || $is_bot) {
         // browser name is the basic data that we need and that all the parsers should provide;
         // if browser name is not given then mobile and bot checks are also very important
         $id_level = self::ID_LEVEL_PARTIAL;
         if (empty($browser['name'])) {
             // we don't want to leave the browser name empty
             $browser['name'] = trim('generic ' . ($is_mobile ? 'mobile ' : '') . ($is_bot ? 'bot ' : ''));
         }
     } else {
         // not identfied at all (some data may be present, but nothing useful)
         $id_level = self::ID_LEVEL_NONE;
     }
     // $browser['name'] is always filled if $id_level != self::ID_LEVEL_NONE
     $ua_info = new UserAgentInfo($user_agent, $id_level, self::$data_version, array('browser' => (string) @$browser['name'], 'browser_major' => (string) @$browser['major'], 'browser_minor' => (string) @$browser['minor'], 'browser_patch' => (string) @$browser['patch'], 'device_family' => $device['family'], 'device_version' => $device['version'], 'os' => (string) @$os['name'], 'os_major' => (string) @$os['major'], 'os_minor' => (string) @$os['minor'], 'os_patch' => (string) @$os['patch'], 'is_banned' => (bool) $bc['isBanned'], 'is_mobile' => $is_mobile, 'is_mobile_tablet' => $is_mobile && $md->isTablet(), 'is_bot' => $is_bot, 'is_bot_reader' => $is_bot && $bc['isSyndicationReader'], 'is_64_bit_os' => 64 == @$architecture['os'], 'is_64_bit_browser' => 64 == @$architecture['browser'], 'mobile_grade' => $mobile_grade));
     return $ua_info;
 }