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; }
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) . '"> </span>'; break; case 'redirected': $download_status = '<span class="redirected" title="' . esc_attr($log->download_status_message) . '"> </span>'; break; default: $download_status = '<span class="completed" title="' . __('Download Complete', 'download-monitor') . '"> </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 . ' – ' . $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 = '–'; } 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 . ' – '; $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; } }
/** * 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()); }
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";
// 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) {
/** * 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; }
* * 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; }