/** * @covers \Tivie\OS\Detector::getFamily * @covers \Tivie\OS\Detector::getType * @covers \Tivie\OS\Detector::getKernelName */ public function testGetters() { self::assertTrue(is_int($this->os->getFamily()), "getFamily() is not returning an integer (and it should)"); self::assertTrue(is_int($this->os->getType()), "getType() is not returning an integer (and it should)"); self::assertTrue(is_string($this->os->getKernelName()), "getKernelName() is not returning a string (and it should)"); return $this->os; }
/** * @depends testGetters */ public function testDetection() { $os = new Detector(); self::assertEquals(UNIX_ON_WINDOWS_FAMILY, $os->getFamily()); $haystack = array(MSYS, CYGWIN); self::assertContains($os->getType(), $haystack); self::assertContains($os->getKernelName(), $haystack, "", true); }
background-color: #efefef; background-image: none; } .featureNote { border: 0; padding: 0; margin: -5px 0 25px 4px; color: #666; font-size: 13px; } </style> <!-- My Scripts --> <?php Detector::buildFeaturesScriptLink(); ?> <script type="text/javascript" src="/js/modernizr.2.5.2.min.custom.js"></script> <script type="text/javascript" src="/js/tests.demo.js"></script> </head> <body> <div class="container"> <div class="content"> <div class="page-header"> <h1><a href="/" style="color: black;">Detector</a> <span class='label notice beta' >beta</span> <?php if (isset($ua->isMobile) && $ua->isMobile && Detector::$foundIn != "archive") { } else { print "<small>combined browser- & feature-detection for your app</small></h1>";
/** * Set triggers if empty and returns the triggers * @return array */ protected function _triggers() { if (empty(self::$_triggers)) { $triggers = array("1" => array("title" => "PageView", "detect" => function ($opts) { return true; }), "2" => array("title" => "Location", "detect" => function ($opts) { $saved = strtolower($opts['saved']); $current = strtolower($opts['user']['location']); return $saved == $current; }), "3" => array("title" => "Landing Page", "detect" => function ($opts) { $stored = strtolower($opts['saved']); $current = strtolower($opts['server']['landingPage']); return $current == $stored; }), "4" => array("title" => "Time of Visit", "detect" => function ($opts) { $range = explode("-", $opts['saved']); $start = $range[0]; $current = date('G:i'); $end = $range[1]; $start_time = strtotime($start); $current_time = strtotime($current); $end_time = strtotime($end); return $current_time > $start_time && $current_time < $end_time; }), "5" => array("title" => "Bots", "detect" => function ($opts) { $bots = explode(",", $opts['saved']); $response = false; foreach ($bots as $b) { if ($opts['user']['ua'] == trim($b)) { $response = true; break; } } if (strtolower($opts['saved']) == 'crawler' && $opts['user']['ua_info']->device->family == "Spider") { $response = true; } return $response; }), "6" => array("title" => "IP Range", "detect" => function ($opts) { if (strpos($opts['saved'], '/') == false) { $range .= '/32'; } // $range is in IP/CIDR format eg 127.0.0.1/24 list($opts['saved'], $netmask) = explode('/', $opts['saved'], 2); $range_decimal = ip2long($opts['saved']); $ip_decimal = ip2long($opts['user']['ip']); $wildcard_decimal = pow(2, 32 - $netmask) - 1; $netmask_decimal = ~$wildcard_decimal; return ($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal); }), "7" => array("title" => "User-Agent", "verify" => function ($inputs) { }, "detect" => function ($opts) { return $opts['user']['ua'] == $opts['saved']; }), "8" => array("title" => "Browser", "detect" => function ($opts) { $current = $opts['user']['ua_info']->ua->family; $saved = $opts['saved']; return stristr($current, $saved); }), "9" => array("title" => "Operating System", "detect" => function ($opts) { $current = $opts['user']['ua_info']->os->family; $saved = $opts['saved']; return stristr($current, $saved); }), "10" => array("title" => "Device Type", "detect" => function ($opts) { $saved = strtolower($opts['saved']); $check = strtolower($opts['user']['ua_info']->device->family); switch ($check) { case 'other': $result = 'desktop'; break; case 'android': $result = 'mobile'; break; default: if (stristr($check, "Smartphone")) { $result = 'mobile'; } elseif (stristr($opts['user']['ua_info']->os->family, "Android")) { $result = "mobile"; } else { $result = false; } break; } if (!$result) { if (stristr($opts['user']['ua_info']->ua->family, "Mobile")) { $result = 'mobile'; } else { $result = 'desktop'; } } return $saved == $result; }), "11" => array("title" => "Referrer", "detect" => function ($opts) { if (empty($opts['server']['referer']) && empty($opts['saved'])) { return true; } else { if (empty($opts['server']['referer']) || empty($opts['saved'])) { return false; } } $response = stristr($opts['server']['referer'], $opts['saved']); return $response !== FALSE ? true : false; }), "12" => array("title" => "Active Login", "detect" => function ($opts) { return false; }), "13" => array("title" => "Repeat Visitor", "detect" => function ($opts) { $cookie = $opts["cookies"]; return isset($cookie["__trafficMonitor"]) ? true : false; })); self::$_triggers = $triggers; } return self::$_triggers; }
/** * Tests to see if: * - see if this is a debug request with appropriately formed pid, else * - see if this browser reports being a spider, doesn't support JS or doesn't support cookies * - see if the cookie for the per user test has been set so we can record the results and add to the session * - see if a session has already been opened for the request browser, if so send the info back, else * - see if detector can find an already created profile for the browser, if so send the info back, else * - see if the cookie for the full test has been set so we can build the profile, if so build the profile & send the info back, else * - start the process for building a profile for this unknown browser * * Logic is based heavily on modernizr-server * * @return {Object} an object that contains all the properties for this particular user agent */ public static function build() { // configure detector from config.ini self::configure(); // populate some variables specific to build() $uaFileCore = __DIR__ . "/" . self::$uaDirCore . self::uaDir() . "ua." . self::$uaHash . ".json"; $uaFileExtended = __DIR__ . "/" . self::$uaDirExtended . self::uaDir() . "ua." . self::$uaHash . ".json"; $ccFile = __DIR__ . "/" . self::$uaDirCore . self::uaDir() . "cc." . self::$uaHash . ".json"; $ccFailure = __DIR__ . "/" . self::$uaDirCore . "cc-failures/cc." . self::$uaHash . "." . time() . ".json"; $uaTemplateCore = __DIR__ . "/" . self::$uaDirCore . "ua.template.json"; $uaTemplateExtended = __DIR__ . "/" . self::$uaDirExtended . "ua.template.json"; $ccTemplate = __DIR__ . "/" . self::$uaDirCore . "cc.template.json"; $pid = isset($_REQUEST['pid']) && preg_match("/[a-z0-9]{32}/", $_REQUEST['pid']) ? $_REQUEST['pid'] : false; // offer the ability to review profiles saved in the system if ($pid && self::$debug) { // where did we find this info to display... probably only need this for the demo self::$foundIn = "archive"; // decode the core data $uaJSONCore = json_decode(@file_get_contents(__DIR__ . "/" . self::$uaDirCore . self::uaDir($pid) . "ua." . $pid . ".json")); // find and decode the extended data $uaJSONExtended = json_decode(@file_get_contents(__DIR__ . "/" . self::$uaDirExtended . self::uaDir($pid) . "ua." . $pid . ".json")); // merge the data $mergedInfo = $uaJSONExtended ? (object) array_merge((array) $uaJSONCore, (array) $uaJSONExtended) : $uaJSONCore; // some general properties $mergedInfo->nojs = false; $mergedInfo->nocookies = false; // put the merged JSON info into session if (isset($_SESSION)) { $_SESSION[self::$sessionID] = $mergedInfo; } // return to the script return $mergedInfo; } else { if (self::checkSpider() || isset($_REQUEST["nojs"]) && $_REQUEST["nojs"] == "true" || isset($_REQUEST["nocookies"]) && $_REQUEST["nocookies"] == "true") { // where did we find this info to display... probably only need this for the demo self::$foundIn = "nojs"; // open the JSON template core & extended files that will be populated $jsonTemplateCore = self::openUAFile($uaTemplateCore); $jsonTemplateExtended = self::openUAFile($uaTemplateExtended); // use ua-parser-php to set-up the basic properties for this UA, populate other core properties // include the basic properties of the UA $jsonTemplateCore->ua = self::$ua; $jsonTemplateCore->uaHash = self::$uaHash; $jsonTemplateCore->coreVersion = self::$coreVersion; $jsonTemplateCore = self::createUAProperties($jsonTemplateCore); // populate extended properties $jsonTemplateExtended = !isset($jsonTemplateExtended) ? new stdClass() : $jsonTemplateExtended; $jsonTemplateExtended->ua = self::$ua; $jsonTemplateExtended->uaHash = self::$uaHash; $jsonTemplateExtended->extendedVersion = self::$extendedVersion; $mergedInfo = new stdClass(); $mergedInfo = (object) array_merge((array) $jsonTemplateCore, (array) $jsonTemplateExtended); // some general properties $mergedInfo->nojs = false; $mergedInfo->nocookies = false; // add an attribute to the object in case no js or no cookies was sent if (isset($_REQUEST["nojs"]) && $_REQUEST["nojs"] == "true") { $mergedInfo->nojs = true; } else { if (isset($_REQUEST["nocookies"]) && $_REQUEST["nocookies"] == "true") { $mergedInfo->nocookies = true; } } // try setting the session unless cookies are actively not supported if (!(isset($_REQUEST["nocookies"]) && $_REQUEST["nocookies"] == "true") && isset($_SESSION)) { $_SESSION[self::$sessionID] = $mergedInfo; } // return the collected data to the script for use in this go around return $mergedInfo; } else { if (@session_start() && isset($_SESSION) && isset($_SESSION[self::$sessionID]) && isset($_COOKIE) && isset($_COOKIE[self::$cookieID . "-ps"])) { // where did we find this info to display... probably only need this for the demo self::$foundIn = "persession"; // parse the per request cookie $cookiePerSession = new stdClass(); $cookiePerSession = self::parseCookie("ps", $cookiePerSession, true); // parse the per request cookie $cookiePerRequest = new stdClass(); $cookiePerRequest = self::parseCookie("pr", $cookiePerRequest, true); // merge the session info we already have and the info from the cookie $mergedInfo = isset($cookiePerSession) ? (object) array_merge((array) $_SESSION[self::$sessionID], (array) $cookiePerSession) : $_SESSION[self::$sessionID]; $mergedInfo = isset($cookiePerRequest) ? (object) array_merge((array) $mergedInfo, (array) $cookiePerRequest) : $mergedInfo; // unset the cookies setcookie(self::$cookieID, ""); setcookie(self::$cookieID . "-ps", ""); // put the merged JSON info into session if (isset($_SESSION)) { $_SESSION[self::$sessionID] = $mergedInfo; } // send the data back to the script to be used return $mergedInfo; } else { if (@session_start() && isset($_SESSION) && isset($_SESSION[self::$sessionID])) { // where did we find this info to display... probably only need this for the demo self::$foundIn = "session"; // parse the per request cookie $cookiePerRequest = new stdClass(); $cookiePerRequest = self::parseCookie("pr", $cookiePerRequest); // merge the session info we already have and the info from the cookie $mergedInfo = isset($cookiePerRequest) ? (object) array_merge((array) $_SESSION[self::$sessionID], (array) $cookiePerRequest) : $_SESSION[self::$sessionID]; // put the merged JSON info into session if (isset($_SESSION)) { $_SESSION[self::$sessionID] = $mergedInfo; } // send the data back to the script to be used return $mergedInfo; } else { if (($uaJSONCore = json_decode(@file_get_contents($uaFileCore))) && ($uaJSONExtended = json_decode(@file_get_contents($uaFileExtended))) && isset($_SESSION) && !isset($_SESSION[self::$sessionID . "-cc"]) && !isset($_SESSION[self::$sessionID . "-b"])) { // where did we find this info to display... probably only need this for the demo self::$foundIn = "file"; // see if we should randomly re-test this UA or if the core/extended versions have changes if ($uaJSONCore->coreVersion != self::$coreVersion || $uaJSONExtended->extendedVersion != self::$extendedVersion) { self::buildTestPage(); } else { if (rand(1, self::$ccRandomRange) == 1) { $_SESSION[self::$sessionID . "-cc"] = true; self::buildTestPage(); } } // merge the data $mergedInfo = $uaJSONExtended ? (object) array_merge((array) $uaJSONCore, (array) $uaJSONExtended) : $uaJSONCore; // some general properties $mergedInfo->nojs = false; $mergedInfo->nocookies = false; // put the merged JSON info into session if (isset($_SESSION)) { $_SESSION[self::$sessionID] = $mergedInfo; } // need to build the tests for the per user if (self::readDirFiles(self::$uaFeaturesPerSession, true)) { self::persession(); } // return to the script return $mergedInfo; } else { if (isset($_COOKIE) && isset($_COOKIE[self::$cookieID])) { // to be clear, this section means that a UA was unknown, was profiled with modernizr & now we're saving that data to build a new profile // where did we find this info to display... probably only need this for the demo self::$foundIn = "cookie"; // open the JSON template core & extended files that will be populated $jsonTemplateCore = self::openUAFile($uaTemplateCore); $jsonTemplateExtended = self::openUAFile($uaTemplateExtended); // use ua-parser-php to set-up the basic properties for this UA, populate other core properties $jsonTemplateCore->ua = self::$ua; $jsonTemplateCore->uaHash = self::$uaHash; $jsonTemplateCore->coreVersion = self::$coreVersion; $jsonTemplateCore = self::createUAProperties($jsonTemplateCore); // populate extended properties $jsonTemplateExtended = !isset($jsonTemplateExtended) ? new stdClass() : $jsonTemplateExtended; $jsonTemplateExtended->ua = self::$ua; $jsonTemplateExtended->uaHash = self::$uaHash; $jsonTemplateExtended->extendedVersion = self::$extendedVersion; // create objects to hold any of the per user or per request data. it shouldn't be saved to file but it should be added to the session $cookiePerSession = new stdClass(); $cookiePerRequest = new stdClass(); // push features into the same level as the general device information // change 1/0 to true/false. why? 'cause that's what i like to read ;) $jsonTemplateCore = self::parseCookie("core", $jsonTemplateCore, true); $jsonTemplateExtended = self::parseCookie("extended", $jsonTemplateExtended, true); $cookiePerSession = self::parseCookie("ps", $cookiePerSession, true); $cookiePerRequest = self::parseCookie("pr", $cookiePerRequest, true); // merge the data for future requests $mergedInfo = new stdClass(); $mergedInfo = $jsonTemplateExtended ? (object) array_merge((array) $jsonTemplateCore, (array) $jsonTemplateExtended) : $jsonTemplateCore; $mergedInfo = $cookiePerSession ? (object) array_merge((array) $mergedInfo, (array) $cookiePerSession) : $mergedInfo; $mergedInfo = $cookiePerRequest ? (object) array_merge((array) $mergedInfo, (array) $cookiePerRequest) : $mergedInfo; // some general properties $mergedInfo->nojs = false; $mergedInfo->nocookies = false; // write out to disk for future requests that might have the same UA $jsonTemplateCore = json_encode($jsonTemplateCore); $jsonTemplateExtended = json_encode($jsonTemplateExtended); if (@session_start() && isset($_SESSION) && isset($_SESSION['confidenceCheck']) && $_SESSION['confidenceCheck'] == true) { self::confidenceCheck($jsonTemplateCore, $ccFile, $ccFailure); } else { self::writeUAFile($jsonTemplateCore, $uaFileCore); self::writeUAFile($jsonTemplateExtended, $uaFileExtended); self::writeCCFile(md5($jsonTemplateCore), $ccTemplate, $ccFile); } // add the user agent & hash to a list of already saved user agents // this isn't really useful for anything beyond detector.dmolsen.com // self::addToUAList(); // unset the cookie that held the vast amount of test data setcookie(self::$cookieID, "", time() - 3600); unset($jsonTemplateCore); unset($jsonTemplateExtended); unset($cookiePerSession); unset($cookiePerRequest); unset($_SESSION[self::$sessionID . "-b"]); // add our collected data to the session for use in future requests, also add the per request data if (isset($_SESSION)) { $_SESSION[self::$sessionID] = $mergedInfo; } // return the collected data to the script for use in this go around return $mergedInfo; } else { // didn't recognize that the user had been here before nor the UA string. self::buildTestPage(); } } } } } } }
public function __construct() { $criterion = new Criterion\Collection(Criterion\Collection::STRATEGY_ANY, array(new Criterion\SymfonyKernel(), new Criterion\SymfonySubmodule())); parent::__construct($criterion); }
<?php // require detector to get the family, autoloads the $ua var require_once "../../../lib/Detector/Detector.php"; ?> <html class="<?php echo Detector::createHTMLList($ua, "isMobile,geolocation,cssanimations,cssgradients,indexeddb", true); ?> "> <head> <title>Demo of Including Detector Features in the HTML Tag</title> </head> <body> View the source and you'll see the HTML tag is modified with the following attributes select attributes:<br /> <br /> <!-- by using true as the last object you're saying you want select UA attributes also shared --> <?php echo Detector::createHTMLList($ua, "isMobile,geolocation,cssanimations,cssgradients,indexeddb", true); ?> </body> </html>
public function __construct() { $criterion = new Criterion\RepoNameRegExp('/^(.+)Bundle$/'); parent::__construct($criterion); }
<?php $p = true; // turn off the build function require "../../lib/Detector/Detector.php"; header("content-type: application/x-javascript"); Detector::perrequest();
<?php // require detector to get the family, autoloads the $ua var require_once "../../../lib/Detector/Detector.php"; ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JavaScript Listing</title> <?php echo Detector::createJavaScriptObj($ua, "browserFull,isMobile,geolocation,cssanimations,cssgradients,indexeddb"); ?> </head> <body> <p>An example of listing Detector properties via JS:</p> <p> <script type="text/javascript"> var m=Detector; for(var f in m){ if(f[0]=='_'){continue;} var t=typeof m[f]; if(t=='function'){continue;} document.write(f+'='+m[f]); if(t=='object'){ document.write('-><br />'); for(var s in m[f]){ if (typeof m[f][s]=='boolean') { document.write(' '+s+'<br />'); }
<?php require 'config.php'; require 'vendor/autoload.php'; require 'autoloader.php'; require 'tracker.php'; require 'detector.php'; $detector = new Detector(); $detector->execute();