Skip to content

Commit

Permalink
Revert "Revert "Copy over Agent functionality to allow Laravel 11 Sup…
Browse files Browse the repository at this point in the history
…port""

This reverts commit 9b8c7fd
  • Loading branch information
tomirons committed Jun 6, 2024
1 parent bef7133 commit d5c45e6
Show file tree
Hide file tree
Showing 7 changed files with 683 additions and 12 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/vendor
/.phpunit.cache
composer.lock
.phpunit.result.cache
.phpunit.cache/test-results
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
"illuminate/http": "^8.0|^9.0|^10.0|^11.0",
"illuminate/view": "^8.0|^9.0|^10.0|^11.0",
"jenssegers/agent": "^2.6",
"doctrine/dbal": "^3.0"
"jaybizzle/crawler-detect": "^1.2",
"doctrine/dbal": "^3.0",
"mobiledetect/mobiledetectlib": "^2.7.6"
},
"autoload": {
"psr-4": {
Expand Down
382 changes: 382 additions & 0 deletions src/Agent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,382 @@
<?php

namespace AndreasElia\Analytics;

use BadMethodCallException;
use Jaybizzle\CrawlerDetect\CrawlerDetect;
use Mobile_Detect;

class Agent extends Mobile_Detect
{
/** @var array */
protected static $desktopDevices = [
'Macintosh' => 'Macintosh',
];

/** @var array */
protected static $additionalOperatingSystems = [
'Windows' => 'Windows',
'Windows NT' => 'Windows NT',
'OS X' => 'Mac OS X',
'Debian' => 'Debian',
'Ubuntu' => 'Ubuntu',
'Macintosh' => 'PPC',
'OpenBSD' => 'OpenBSD',
'Linux' => 'Linux',
'ChromeOS' => 'CrOS',
];

/** @var array */
protected static $additionalBrowsers = [
'Opera Mini' => 'Opera Mini',
'Opera' => 'Opera|OPR',
'Edge' => 'Edge|Edg',
'Coc Coc' => 'coc_coc_browser',
'UCBrowser' => 'UCBrowser',
'Vivaldi' => 'Vivaldi',
'Chrome' => 'Chrome',
'Firefox' => 'Firefox',
'Safari' => 'Safari',
'IE' => 'MSIE|IEMobile|MSIEMobile|Trident/[.0-9]+',
'Netscape' => 'Netscape',
'Mozilla' => 'Mozilla',
'WeChat' => 'MicroMessenger',
];

/** @var array */
protected static $additionalProperties = [
// Operating systems
'Windows' => 'Windows NT [VER]',
'Windows NT' => 'Windows NT [VER]',
'OS X' => 'OS X [VER]',
'BlackBerryOS' => ['BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'],
'AndroidOS' => 'Android [VER]',
'ChromeOS' => 'CrOS x86_64 [VER]',

// Browsers
'Opera Mini' => 'Opera Mini/[VER]',
'Opera' => [' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]', 'Opera [VER]'],
'Netscape' => 'Netscape/[VER]',
'Mozilla' => 'rv:[VER]',
'IE' => ['IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];', 'rv:[VER]'],
'Edge' => ['Edge/[VER]', 'Edg/[VER]'],
'Vivaldi' => 'Vivaldi/[VER]',
'Coc Coc' => 'coc_coc_browser/[VER]',
];

/** @var CrawlerDetect */
protected static $crawlerDetect;

/** @return array */
public static function getDetectionRulesExtended()
{
static $rules;

if (! $rules) {
$rules = static::mergeRules(
static::$desktopDevices, // NEW
static::$phoneDevices,
static::$tabletDevices,
static::$operatingSystems,
static::$additionalOperatingSystems, // NEW
static::$browsers,
static::$additionalBrowsers, // NEW
static::$utilities
);
}

return $rules;
}

public function getRules()
{
if ($this->detectionType === static::DETECTION_TYPE_EXTENDED) {
return static::getDetectionRulesExtended();
}

return static::getMobileDetectionRules();
}

/** @return CrawlerDetect */
public function getCrawlerDetect()
{
if (static::$crawlerDetect === null) {
static::$crawlerDetect = new CrawlerDetect();
}

return static::$crawlerDetect;
}

public static function getBrowsers()
{
return static::mergeRules(
static::$additionalBrowsers,
static::$browsers
);
}

public static function getOperatingSystems()
{
return static::mergeRules(
static::$operatingSystems,
static::$additionalOperatingSystems
);
}

public static function getPlatforms()
{
return static::mergeRules(
static::$operatingSystems,
static::$additionalOperatingSystems
);
}

public static function getDesktopDevices()
{
return static::$desktopDevices;
}

public static function getProperties()
{
return static::mergeRules(
static::$additionalProperties,
static::$properties
);
}

/**
* @param string $acceptLanguage
* @return array
*/
public function languages($acceptLanguage = null)
{
if ($acceptLanguage === null) {
$acceptLanguage = $this->getHttpHeader('HTTP_ACCEPT_LANGUAGE');
}

if (! $acceptLanguage) {
return [];
}

$languages = [];

// Parse accept language string.
foreach (explode(',', $acceptLanguage) as $piece) {
$parts = explode(';', $piece);
$language = strtolower($parts[0]);
$priority = empty($parts[1]) ? 1. : floatval(str_replace('q=', '', $parts[1]));

$languages[$language] = $priority;
}

// Sort languages by priority.
arsort($languages);

return array_keys($languages);
}

/**
* @param array $rules
* @param string|null $userAgent
* @return string|bool
*/
protected function findDetectionRulesAgainstUA(array $rules, $userAgent = null)
{
// Loop given rules
foreach ($rules as $key => $regex) {
if (empty($regex)) {
continue;
}

// Check match
if ($this->match($regex, $userAgent)) {
return $key ?: reset($this->matchesArray);
}
}

return false;
}

/**
* @param string|null $userAgent
* @return string|bool
*/
public function browser($userAgent = null)
{
return $this->findDetectionRulesAgainstUA(static::getBrowsers(), $userAgent);
}

/**
* @param string|null $userAgent
* @return string|bool
*/
public function platform($userAgent = null)
{
return $this->findDetectionRulesAgainstUA(static::getPlatforms(), $userAgent);
}

/**
* @param string|null $userAgent
* @return string|bool
*/
public function device($userAgent = null)
{
$rules = static::mergeRules(
static::getDesktopDevices(),
static::getPhoneDevices(),
static::getTabletDevices(),
static::getUtilities()
);

return $this->findDetectionRulesAgainstUA($rules, $userAgent);
}

/**
* @param string|null $userAgent deprecated
* @param array $httpHeaders deprecated
* @return bool
*/
public function isDesktop($userAgent = null, $httpHeaders = null)
{
// Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
if ($this->getUserAgent() === 'Amazon CloudFront') {
$cfHeaders = $this->getCfHeaders();

if (array_key_exists('HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER', $cfHeaders)) {
return $cfHeaders['HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER'] === 'true';
}
}

return ! $this->isMobile($userAgent, $httpHeaders) && ! $this->isTablet($userAgent, $httpHeaders) && ! $this->isRobot($userAgent);
}

/**
* @param string|null $userAgent deprecated
* @param array $httpHeaders deprecated
* @return bool
*/
public function isPhone($userAgent = null, $httpHeaders = null)
{
return $this->isMobile($userAgent, $httpHeaders) && ! $this->isTablet($userAgent, $httpHeaders);
}

/**
* @param string|null $userAgent
* @return string|bool
*/
public function robot($userAgent = null)
{
if ($this->getCrawlerDetect()->isCrawler($userAgent ?: $this->userAgent)) {
return ucfirst($this->getCrawlerDetect()->getMatches());
}

return false;
}

/**
* @param string|null $userAgent
* @return bool
*/
public function isRobot($userAgent = null)
{
return $this->getCrawlerDetect()->isCrawler($userAgent ?: $this->userAgent);
}

/**
* @param null $userAgent
* @param null $httpHeaders
* @return string
*/
public function deviceType($userAgent = null, $httpHeaders = null)
{
if ($this->isDesktop($userAgent, $httpHeaders)) {
return 'desktop';
} elseif ($this->isPhone($userAgent, $httpHeaders)) {
return 'phone';
} elseif ($this->isTablet($userAgent, $httpHeaders)) {
return 'tablet';
} elseif ($this->isRobot($userAgent)) {
return 'robot';
}

return 'other';
}

public function version($propertyName, $type = self::VERSION_TYPE_STRING)
{
if (empty($propertyName)) {
return false;
}

// set the $type to the default if we don't recognize the type
if ($type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT) {
$type = self::VERSION_TYPE_STRING;
}

$properties = self::getProperties();

// Check if the property exists in the properties array.
if (true === isset($properties[$propertyName])) {
// Prepare the pattern to be matched.
// Make sure we always deal with an array (string is converted).
$properties[$propertyName] = (array) $properties[$propertyName];

foreach ($properties[$propertyName] as $propertyMatchString) {
if (is_array($propertyMatchString)) {
$propertyMatchString = implode('|', $propertyMatchString);
}

$propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);

// Identify and extract the version.
preg_match(sprintf('#%s#is', $propertyPattern), $this->userAgent, $match);

if (false === empty($match[1])) {
$version = ($type === self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1]);

return $version;
}
}
}

return false;
}

/**
* @param array $all
* @return array
*/
protected static function mergeRules(...$all)
{
$merged = [];

foreach ($all as $rules) {
foreach ($rules as $key => $value) {
if (empty($merged[$key])) {
$merged[$key] = $value;
} elseif (is_array($merged[$key])) {
$merged[$key][] = $value;
} else {
$merged[$key] .= '|'.$value;
}
}
}

return $merged;
}

/** @inheritdoc */
public function __call($name, $arguments)
{
// Make sure the name starts with 'is', otherwise
if (strpos($name, 'is') !== 0) {
throw new BadMethodCallException("No such method exists: $name");
}

$this->setDetectionType(self::DETECTION_TYPE_EXTENDED);

$key = substr($name, 2);

return $this->matchUAAgainstKey($key);
}
}
Loading

0 comments on commit d5c45e6

Please sign in to comment.