feat(cloudron): add tirreno package artifacts
- Add CloudronStack/output/CloudronPackages-Artifacts/tirreno/ directory and its contents - Includes package manifest, Dockerfile, source code, documentation, and build artifacts - Add tirreno-1761840148.tar.gz as a build artifact - Add tirreno-cloudron-package-1761841304.tar.gz as the Cloudron package - Include all necessary files for the tirreno Cloudron package This adds the complete tirreno Cloudron package artifacts to the repository.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser;
|
||||
|
||||
/**
|
||||
* Class AbstractBotParser
|
||||
*
|
||||
* Abstract class for all bot parsers
|
||||
*/
|
||||
abstract class AbstractBotParser extends AbstractParser
|
||||
{
|
||||
/**
|
||||
* Enables information discarding
|
||||
*/
|
||||
abstract public function discardDetails(): void;
|
||||
}
|
||||
@@ -0,0 +1,494 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser;
|
||||
|
||||
use DeviceDetector\Cache\CacheInterface;
|
||||
use DeviceDetector\Cache\StaticCache;
|
||||
use DeviceDetector\ClientHints;
|
||||
use DeviceDetector\DeviceDetector;
|
||||
use DeviceDetector\Yaml\ParserInterface as YamlParser;
|
||||
use DeviceDetector\Yaml\Spyc;
|
||||
|
||||
/**
|
||||
* Class AbstractParser
|
||||
*/
|
||||
abstract class AbstractParser
|
||||
{
|
||||
/**
|
||||
* Holds the path to the yml file containing regexes
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile;
|
||||
|
||||
/**
|
||||
* Holds the internal name of the parser
|
||||
* Used for caching
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName;
|
||||
|
||||
/**
|
||||
* Holds the user agent to be parsed
|
||||
* @var string
|
||||
*/
|
||||
protected $userAgent;
|
||||
|
||||
/**
|
||||
* Holds the client hints to be parsed
|
||||
* @var ?ClientHints
|
||||
*/
|
||||
protected $clientHints = null;
|
||||
|
||||
/**
|
||||
* Contains a list of mappings from names we use to known client hint values
|
||||
* @var array<string, array<string>>
|
||||
*/
|
||||
protected static $clientHintMapping = [];
|
||||
|
||||
/**
|
||||
* Holds an array with method that should be available global
|
||||
* @var array
|
||||
*/
|
||||
protected $globalMethods;
|
||||
|
||||
/**
|
||||
* Holds an array with regexes to parse, if already loaded
|
||||
* @var array
|
||||
*/
|
||||
protected $regexList;
|
||||
|
||||
/**
|
||||
* Holds the concatenated regex for all items in regex list
|
||||
* @var string
|
||||
*/
|
||||
protected $overAllMatch;
|
||||
|
||||
/**
|
||||
* Indicates how deep versioning will be detected
|
||||
* if $maxMinorParts is 0 only the major version will be returned
|
||||
* @var int
|
||||
*/
|
||||
protected static $maxMinorParts = 1;
|
||||
|
||||
/**
|
||||
* Versioning constant used to set max versioning to major version only
|
||||
* Version examples are: 3, 5, 6, 200, 123, ...
|
||||
*/
|
||||
public const VERSION_TRUNCATION_MAJOR = 0;
|
||||
|
||||
/**
|
||||
* Versioning constant used to set max versioning to minor version
|
||||
* Version examples are: 3.4, 5.6, 6.234, 0.200, 1.23, ...
|
||||
*/
|
||||
public const VERSION_TRUNCATION_MINOR = 1;
|
||||
|
||||
/**
|
||||
* Versioning constant used to set max versioning to path level
|
||||
* Version examples are: 3.4.0, 5.6.344, 6.234.2, 0.200.3, 1.2.3, ...
|
||||
*/
|
||||
public const VERSION_TRUNCATION_PATCH = 2;
|
||||
|
||||
/**
|
||||
* Versioning constant used to set versioning to build number
|
||||
* Version examples are: 3.4.0.12, 5.6.334.0, 6.234.2.3, 0.200.3.1, 1.2.3.0, ...
|
||||
*/
|
||||
public const VERSION_TRUNCATION_BUILD = 3;
|
||||
|
||||
/**
|
||||
* Versioning constant used to set versioning to unlimited (no truncation)
|
||||
*/
|
||||
public const VERSION_TRUNCATION_NONE = -1;
|
||||
|
||||
/**
|
||||
* @var CacheInterface|null
|
||||
*/
|
||||
protected $cache = null;
|
||||
|
||||
/**
|
||||
* @var YamlParser|null
|
||||
*/
|
||||
protected $yamlParser = null;
|
||||
|
||||
/**
|
||||
* parses the currently set useragents and returns possible results
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
abstract public function parse(): ?array;
|
||||
|
||||
/**
|
||||
* AbstractParser constructor.
|
||||
*
|
||||
* @param string $ua
|
||||
* @param ?ClientHints $clientHints
|
||||
*/
|
||||
public function __construct(string $ua = '', ?ClientHints $clientHints = null)
|
||||
{
|
||||
$this->setUserAgent($ua);
|
||||
$this->setClientHints($clientHints);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function restoreUserAgentFromClientHints(): void
|
||||
{
|
||||
if (null === $this->clientHints) {
|
||||
return;
|
||||
}
|
||||
|
||||
$deviceModel = $this->clientHints->getModel();
|
||||
|
||||
if ('' === $deviceModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore Android User Agent
|
||||
if ($this->hasUserAgentClientHintsFragment()) {
|
||||
$osVersion = $this->clientHints->getOperatingSystemVersion();
|
||||
$this->setUserAgent((string) \preg_replace(
|
||||
'(Android (?:10[.\d]*; K|1[1-5]))',
|
||||
\sprintf('Android %s; %s', '' !== $osVersion ? $osVersion : '10', $deviceModel),
|
||||
$this->userAgent
|
||||
));
|
||||
}
|
||||
|
||||
// Restore Desktop User Agent
|
||||
if (!$this->hasDesktopFragment()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setUserAgent((string) \preg_replace(
|
||||
'(X11; Linux x86_64)',
|
||||
\sprintf('X11; Linux x86_64; %s', $deviceModel),
|
||||
$this->userAgent
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set how DeviceDetector should return versions
|
||||
* @param int $type Any of the VERSION_TRUNCATION_* constants
|
||||
*/
|
||||
public static function setVersionTruncation(int $type): void
|
||||
{
|
||||
if (!\in_array($type, [
|
||||
self::VERSION_TRUNCATION_BUILD,
|
||||
self::VERSION_TRUNCATION_NONE,
|
||||
self::VERSION_TRUNCATION_MAJOR,
|
||||
self::VERSION_TRUNCATION_MINOR,
|
||||
self::VERSION_TRUNCATION_PATCH,
|
||||
])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
static::$maxMinorParts = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user agent to parse
|
||||
*
|
||||
* @param string $ua user agent
|
||||
*/
|
||||
public function setUserAgent(string $ua): void
|
||||
{
|
||||
$this->userAgent = $ua;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client hints to parse
|
||||
*
|
||||
* @param ?ClientHints $clientHints client hints
|
||||
*/
|
||||
public function setClientHints(?ClientHints $clientHints): void
|
||||
{
|
||||
$this->clientHints = $clientHints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal name of the parser
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->parserName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Cache class
|
||||
*
|
||||
* @param CacheInterface $cache
|
||||
*/
|
||||
public function setCache(CacheInterface $cache): void
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Cache object
|
||||
*
|
||||
* @return CacheInterface
|
||||
*/
|
||||
public function getCache(): CacheInterface
|
||||
{
|
||||
if (!empty($this->cache)) {
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
return new StaticCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the YamlParser class
|
||||
*
|
||||
* @param YamlParser $yamlParser
|
||||
*/
|
||||
public function setYamlParser(YamlParser $yamlParser): void
|
||||
{
|
||||
$this->yamlParser = $yamlParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns YamlParser object
|
||||
*
|
||||
* @return YamlParser
|
||||
*/
|
||||
public function getYamlParser(): YamlParser
|
||||
{
|
||||
if (!empty($this->yamlParser)) {
|
||||
return $this->yamlParser;
|
||||
}
|
||||
|
||||
return new Spyc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of the parsed yml file defined in $fixtureFile
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getRegexes(): array
|
||||
{
|
||||
if (empty($this->regexList)) {
|
||||
$cacheKey = 'DeviceDetector-' . DeviceDetector::VERSION . 'regexes-' . $this->getName();
|
||||
$cacheKey = (string) \preg_replace('/([^a-z0-9_-]+)/i', '', $cacheKey);
|
||||
$cacheContent = $this->getCache()->fetch($cacheKey);
|
||||
|
||||
if (\is_array($cacheContent)) {
|
||||
$this->regexList = $cacheContent;
|
||||
}
|
||||
|
||||
if (empty($this->regexList)) {
|
||||
$parsedContent = $this->getYamlParser()->parseFile(
|
||||
$this->getRegexesDirectory() . DIRECTORY_SEPARATOR . $this->fixtureFile
|
||||
);
|
||||
|
||||
if (!\is_array($parsedContent)) {
|
||||
$parsedContent = [];
|
||||
}
|
||||
|
||||
$this->regexList = $parsedContent;
|
||||
$this->getCache()->save($cacheKey, $this->regexList);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->regexList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the provided name after applying client hint mappings.
|
||||
* This is used to map names provided in client hints to the names we use.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function applyClientHintMapping(string $name): string
|
||||
{
|
||||
foreach (static::$clientHintMapping as $mappedName => $clientHints) {
|
||||
foreach ($clientHints as $clientHint) {
|
||||
if (\strtolower($name) === \strtolower($clientHint)) {
|
||||
return $mappedName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getRegexesDirectory(): string
|
||||
{
|
||||
return \dirname(__DIR__);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the parsed UA contains the 'Windows NT;' or 'X11; Linux x86_64' fragments
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasDesktopFragment(): bool
|
||||
{
|
||||
$regexExcludeDesktopFragment = \implode('|', [
|
||||
'CE-HTML',
|
||||
' Mozilla/|Andr[o0]id|Tablet|Mobile|iPhone|Windows Phone|ricoh|OculusBrowser',
|
||||
'PicoBrowser|Lenovo|compatible; MSIE|Trident/|Tesla/|XBOX|FBMD/|ARM; ?([^)]+)',
|
||||
]);
|
||||
|
||||
return
|
||||
$this->matchUserAgent('(?:Windows (?:NT|IoT)|X11; Linux x86_64)') &&
|
||||
!$this->matchUserAgent($regexExcludeDesktopFragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the parsed UA contains the 'Android 10 K;' or Android 10 K Build/` fragment
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasUserAgentClientHintsFragment(): bool
|
||||
{
|
||||
return (bool) \preg_match('~Android (?:10[.\d]*; K(?: Build/|[;)])|1[1-5]\)) AppleWebKit~i', $this->userAgent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the useragent against the given regex
|
||||
*
|
||||
* @param string $regex
|
||||
*
|
||||
* @return ?array
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function matchUserAgent(string $regex): ?array
|
||||
{
|
||||
$matches = [];
|
||||
|
||||
// only match if useragent begins with given regex or there is no letter before it
|
||||
$regex = '/(?:^|[^A-Z0-9_-]|[^A-Z0-9-]_|sprd-|MZ-)(?:' . \str_replace('/', '\/', $regex) . ')/i';
|
||||
|
||||
try {
|
||||
if (\preg_match($regex, $this->userAgent, $matches)) {
|
||||
return $matches;
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
throw new \Exception(
|
||||
\sprintf("%s\nRegex: %s", $exception->getMessage(), $regex),
|
||||
$exception->getCode(),
|
||||
$exception
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $item
|
||||
* @param array $matches
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function buildByMatch(string $item, array $matches): string
|
||||
{
|
||||
$search = [];
|
||||
$replace = [];
|
||||
|
||||
for ($nb = 1; $nb <= \count($matches); $nb++) {
|
||||
$search[] = '$' . $nb;
|
||||
$replace[] = $matches[$nb] ?? '';
|
||||
}
|
||||
|
||||
return \trim(\str_replace($search, $replace, $item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the version with the given $versionString and $matches
|
||||
*
|
||||
* Example:
|
||||
* $versionString = 'v$2'
|
||||
* $matches = ['version_1_0_1', '1_0_1']
|
||||
* return value would be v1.0.1
|
||||
*
|
||||
* @param string $versionString
|
||||
* @param array $matches
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function buildVersion(string $versionString, array $matches): string
|
||||
{
|
||||
$versionString = $this->buildByMatch($versionString, $matches);
|
||||
$versionString = \str_replace('_', '.', $versionString);
|
||||
|
||||
if (self::VERSION_TRUNCATION_NONE !== static::$maxMinorParts
|
||||
&& \substr_count($versionString, '.') > static::$maxMinorParts
|
||||
) {
|
||||
$versionParts = \explode('.', $versionString);
|
||||
$versionParts = \array_slice($versionParts, 0, 1 + static::$maxMinorParts);
|
||||
$versionString = \implode('.', $versionParts);
|
||||
}
|
||||
|
||||
return \trim($versionString, ' .');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the useragent against a combination of all regexes
|
||||
*
|
||||
* All regexes returned by getRegexes() will be reversed and concatenated with '|'
|
||||
* Afterwards the big regex will be tested against the user agent
|
||||
*
|
||||
* Method can be used to speed up detections by making a big check before doing checks for every single regex
|
||||
*
|
||||
* @return ?array
|
||||
*/
|
||||
protected function preMatchOverall(): ?array
|
||||
{
|
||||
$regexes = $this->getRegexes();
|
||||
|
||||
$cacheKey = $this->parserName . DeviceDetector::VERSION . '-all';
|
||||
$cacheKey = (string) \preg_replace('/([^a-z0-9_-]+)/i', '', $cacheKey);
|
||||
|
||||
if (empty($this->overAllMatch)) {
|
||||
$overAllMatch = $this->getCache()->fetch($cacheKey);
|
||||
|
||||
if (\is_string($overAllMatch)) {
|
||||
$this->overAllMatch = $overAllMatch;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->overAllMatch)) {
|
||||
// reverse all regexes, so we have the generic one first, which already matches most patterns
|
||||
$this->overAllMatch = \array_reduce(\array_reverse($regexes), static function ($val1, $val2) {
|
||||
return !empty($val1) ? $val1 . '|' . $val2['regex'] : $val2['regex'];
|
||||
});
|
||||
$this->getCache()->save($cacheKey, $this->overAllMatch);
|
||||
}
|
||||
|
||||
return $this->matchUserAgent($this->overAllMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares if two strings equals after lowering their case and removing spaces
|
||||
*
|
||||
* @param string $value1
|
||||
* @param string $value2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function fuzzyCompare(string $value1, string $value2): bool
|
||||
{
|
||||
return \str_replace(' ', '', \strtolower($value1)) ===
|
||||
\str_replace(' ', '', \strtolower($value2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser;
|
||||
|
||||
/**
|
||||
* Class Bot
|
||||
*
|
||||
* Parses a user agent for bot information
|
||||
*
|
||||
* Detected bots are defined in regexes/bots.yml
|
||||
*/
|
||||
class Bot extends AbstractBotParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/bots.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'bot';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $discardDetails = false;
|
||||
|
||||
/**
|
||||
* Enables information discarding
|
||||
*/
|
||||
public function discardDetails(): void
|
||||
{
|
||||
$this->discardDetails = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the current UA and checks whether it contains bot information
|
||||
*
|
||||
* @see bots.yml for list of detected bots
|
||||
*
|
||||
* Step 1: Build a big regex containing all regexes and match UA against it
|
||||
* -> If no matches found: return
|
||||
* -> Otherwise:
|
||||
* Step 2: Walk through the list of regexes in bots.yml and try to match every one
|
||||
* -> Return the matched data
|
||||
*
|
||||
* If $discardDetails is set to TRUE, the Step 2 will be skipped
|
||||
* $bot will be set to TRUE instead
|
||||
*
|
||||
* NOTE: Doing the big match before matching every single regex speeds up the detection
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
$result = null;
|
||||
|
||||
if ($this->preMatchOverall()) {
|
||||
if ($this->discardDetails) {
|
||||
return [true];
|
||||
}
|
||||
|
||||
foreach ($this->getRegexes() as $regex) {
|
||||
$matches = $this->matchUserAgent($regex['regex']);
|
||||
|
||||
if ($matches) {
|
||||
unset($regex['regex']);
|
||||
$result = $regex;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Client;
|
||||
|
||||
use DeviceDetector\Parser\AbstractParser;
|
||||
|
||||
abstract class AbstractClientParser extends AbstractParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = '';
|
||||
|
||||
/**
|
||||
* Parses the current UA and checks whether it contains any client information
|
||||
*
|
||||
* @see $fixtureFile for file with list of detected clients
|
||||
*
|
||||
* Step 1: Build a big regex containing all regexes and match UA against it
|
||||
* -> If no matches found: return
|
||||
* -> Otherwise:
|
||||
* Step 2: Walk through the list of regexes in feed_readers.yml and try to match every one
|
||||
* -> Return the matched feed reader
|
||||
*
|
||||
* NOTE: Doing the big match before matching every single regex speeds up the detection
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
$result = null;
|
||||
|
||||
if ($this->preMatchOverall()) {
|
||||
foreach ($this->getRegexes() as $regex) {
|
||||
$matches = $this->matchUserAgent($regex['regex']);
|
||||
|
||||
if ($matches) {
|
||||
$result = [
|
||||
'type' => $this->parserName,
|
||||
'name' => $this->buildByMatch($regex['name'], $matches),
|
||||
'version' => $this->buildVersion((string) $regex['version'], $matches),
|
||||
];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all names defined in the regexes
|
||||
*
|
||||
* Attention: This method might not return all names of detected clients
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAvailableClients(): array
|
||||
{
|
||||
$instance = new static(); // @phpstan-ignore-line
|
||||
$regexes = $instance->getRegexes();
|
||||
$names = [];
|
||||
|
||||
foreach ($regexes as $regex) {
|
||||
if (false !== \strpos($regex['name'], '$1')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$names[] = $regex['name'];
|
||||
}
|
||||
|
||||
if (static::class === MobileApp::class) {
|
||||
$names = \array_merge($names, [
|
||||
// Microsoft Office $1
|
||||
'Microsoft Office Access', 'Microsoft Office Excel', 'Microsoft Office OneDrive for Business',
|
||||
'Microsoft Office OneNote', 'Microsoft Office PowerPoint', 'Microsoft Office Project',
|
||||
'Microsoft Office Publisher', 'Microsoft Office Visio', 'Microsoft Office Word',
|
||||
// Podkicker$1
|
||||
'Podkicker', 'Podkicker Pro', 'Podkicker Classic',
|
||||
// radio.$1
|
||||
'radio.at', 'radio.de', 'radio.dk', 'radio.es', 'radio.fr',
|
||||
'radio.it', 'radio.pl', 'radio.pt', 'radio.se', 'radio.net',
|
||||
]);
|
||||
}
|
||||
|
||||
\natcasesort($names);
|
||||
|
||||
return \array_unique($names);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Client\Browser;
|
||||
|
||||
use DeviceDetector\Parser\Client\AbstractClientParser;
|
||||
|
||||
/**
|
||||
* Class Engine
|
||||
*
|
||||
* Client parser for browser engine detection
|
||||
*/
|
||||
class Engine extends AbstractClientParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/client/browser_engine.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'browserengine';
|
||||
|
||||
/**
|
||||
* Known browser engines mapped to their internal short codes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $availableEngines = [
|
||||
'WebKit',
|
||||
'Blink',
|
||||
'Trident',
|
||||
'Text-based',
|
||||
'Dillo',
|
||||
'iCab',
|
||||
'Elektra',
|
||||
'Presto',
|
||||
'Clecko',
|
||||
'Gecko',
|
||||
'KHTML',
|
||||
'NetFront',
|
||||
'Edge',
|
||||
'NetSurf',
|
||||
'Servo',
|
||||
'Goanna',
|
||||
'EkiohFlow',
|
||||
'Arachne',
|
||||
'LibWeb',
|
||||
'Maple',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns list of all available browser engines
|
||||
* @return array
|
||||
*/
|
||||
public static function getAvailableEngines(): array
|
||||
{
|
||||
return self::$availableEngines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
$matches = false;
|
||||
|
||||
foreach ($this->getRegexes() as $regex) {
|
||||
$matches = $this->matchUserAgent($regex['regex']);
|
||||
|
||||
if ($matches) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($matches) || empty($regex)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$name = $this->buildByMatch($regex['name'], $matches);
|
||||
|
||||
foreach (self::getAvailableEngines() as $engineName) {
|
||||
if (\strtolower($name) === \strtolower($engineName)) {
|
||||
return ['engine' => $engineName];
|
||||
}
|
||||
}
|
||||
|
||||
// This Exception should never be thrown. If so a defined browser name is missing in $availableEngines
|
||||
throw new \Exception(\sprintf(
|
||||
'Detected browser engine was not found in $availableEngines. Tried to parse user agent: %s',
|
||||
$this->userAgent
|
||||
)); // @codeCoverageIgnore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Client\Browser\Engine;
|
||||
|
||||
use DeviceDetector\Parser\Client\AbstractClientParser;
|
||||
|
||||
/**
|
||||
* Class Version
|
||||
*
|
||||
* Client parser for browser engine version detection
|
||||
*/
|
||||
class Version extends AbstractClientParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $engine;
|
||||
|
||||
/**
|
||||
* Version constructor.
|
||||
*
|
||||
* @param string $ua
|
||||
* @param string $engine
|
||||
*/
|
||||
public function __construct(string $ua, string $engine)
|
||||
{
|
||||
parent::__construct($ua);
|
||||
|
||||
$this->engine = $engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
if (empty($this->engine)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ('Gecko' === $this->engine || 'Clecko' === $this->engine) {
|
||||
$pattern = '~[ ](?:rv[: ]([0-9.]+)).*(?:g|cl)ecko/[0-9]{8,10}~i';
|
||||
|
||||
if (\preg_match($pattern, $this->userAgent, $matches)) {
|
||||
return ['version' => \array_pop($matches)];
|
||||
}
|
||||
}
|
||||
|
||||
$engineToken = $this->engine;
|
||||
|
||||
if ('Blink' === $this->engine) {
|
||||
$engineToken = 'Chr[o0]me|Chromium|Cronet';
|
||||
}
|
||||
|
||||
if ('Arachne' === $this->engine) {
|
||||
$engineToken = 'Arachne\/5\.';
|
||||
}
|
||||
|
||||
if ('LibWeb' === $this->engine) {
|
||||
$engineToken = 'LibWeb\+LibJs';
|
||||
}
|
||||
|
||||
\preg_match(
|
||||
"~(?:{$engineToken})\s*[/_]?\s*((?(?=\d+\.\d)\d+[.\d]*|\d{1,7}(?=(?:\D|$))))~i",
|
||||
$this->userAgent,
|
||||
$matches
|
||||
);
|
||||
|
||||
if (!$matches) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ['version' => \array_pop($matches)];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Client;
|
||||
|
||||
/**
|
||||
* Class FeedReader
|
||||
*
|
||||
* Client parser for feed reader detection
|
||||
*/
|
||||
class FeedReader extends AbstractClientParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/client/feed_readers.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'feed reader';
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Client\Hints;
|
||||
|
||||
use DeviceDetector\Parser\AbstractParser;
|
||||
|
||||
class AppHints extends AbstractParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/client/hints/apps.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'AppHints';
|
||||
|
||||
/**
|
||||
* Get application name if is in collection
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
if (null === $this->clientHints) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$appId = $this->clientHints->getApp();
|
||||
$name = $this->getRegexes()[$appId] ?? null;
|
||||
|
||||
if ('' === (string) $name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Client\Hints;
|
||||
|
||||
use DeviceDetector\Parser\AbstractParser;
|
||||
|
||||
class BrowserHints extends AbstractParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/client/hints/browsers.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'BrowserHints';
|
||||
|
||||
/**
|
||||
* Get browser name if is in collection
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
if (null === $this->clientHints) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$appId = $this->clientHints->getApp();
|
||||
$name = $this->getRegexes()[$appId] ?? null;
|
||||
|
||||
if ('' === (string) $name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Client;
|
||||
|
||||
/**
|
||||
* Class Library
|
||||
*
|
||||
* Client parser for tool & software detection
|
||||
*/
|
||||
class Library extends AbstractClientParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/client/libraries.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'library';
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Client;
|
||||
|
||||
/**
|
||||
* Class MediaPlayer
|
||||
*
|
||||
* Client parser for mediaplayer detection
|
||||
*/
|
||||
class MediaPlayer extends AbstractClientParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/client/mediaplayers.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'mediaplayer';
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Client;
|
||||
|
||||
use DeviceDetector\Cache\CacheInterface;
|
||||
use DeviceDetector\ClientHints;
|
||||
use DeviceDetector\Parser\Client\Hints\AppHints;
|
||||
use DeviceDetector\Yaml\ParserInterface as YamlParser;
|
||||
|
||||
/**
|
||||
* Class MobileApp
|
||||
*
|
||||
* Client parser for mobile app detection
|
||||
*/
|
||||
class MobileApp extends AbstractClientParser
|
||||
{
|
||||
/**
|
||||
* @var AppHints
|
||||
*/
|
||||
private $appHints;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/client/mobile_apps.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'mobile app';
|
||||
|
||||
/**
|
||||
* MobileApp constructor.
|
||||
*
|
||||
* @param string $ua
|
||||
* @param ClientHints|null $clientHints
|
||||
*/
|
||||
public function __construct(string $ua = '', ?ClientHints $clientHints = null)
|
||||
{
|
||||
$this->appHints = new AppHints($ua, $clientHints);
|
||||
parent::__construct($ua, $clientHints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client hints to parse
|
||||
*
|
||||
* @param ?ClientHints $clientHints client hints
|
||||
*/
|
||||
public function setClientHints(?ClientHints $clientHints): void
|
||||
{
|
||||
parent::setClientHints($clientHints);
|
||||
$this->appHints->setClientHints($clientHints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user agent to parse
|
||||
*
|
||||
* @param string $ua user agent
|
||||
*/
|
||||
public function setUserAgent(string $ua): void
|
||||
{
|
||||
parent::setUserAgent($ua);
|
||||
$this->appHints->setUserAgent($ua);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Cache class
|
||||
*
|
||||
* @param CacheInterface $cache
|
||||
*/
|
||||
public function setCache(CacheInterface $cache): void
|
||||
{
|
||||
parent::setCache($cache);
|
||||
$this->appHints->setCache($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the YamlParser class
|
||||
*
|
||||
* @param YamlParser $yamlParser
|
||||
*/
|
||||
public function setYamlParser(YamlParser $yamlParser): void
|
||||
{
|
||||
parent::setYamlParser($yamlParser);
|
||||
$this->appHints->setYamlParser($this->getYamlParser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the current UA and checks whether it contains any client information
|
||||
* See parent::parse() for more details.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
$result = parent::parse();
|
||||
$name = $result['name'] ?? '';
|
||||
$version = $result['version'] ?? '';
|
||||
$appHash = $this->appHints->parse();
|
||||
|
||||
if (null !== $appHash && $appHash['name'] !== $name) {
|
||||
$name = $appHash['name'];
|
||||
$version = '';
|
||||
}
|
||||
|
||||
if (empty($name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'type' => $this->parserName,
|
||||
'name' => $name,
|
||||
'version' => $version,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Client;
|
||||
|
||||
/**
|
||||
* Class PIM
|
||||
*
|
||||
* Client parser for pim (personal information manager) detection
|
||||
*/
|
||||
class PIM extends AbstractClientParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/client/pim.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'pim';
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Device;
|
||||
|
||||
/**
|
||||
* Class Camera
|
||||
*
|
||||
* Device parser for camera detection
|
||||
*/
|
||||
class Camera extends AbstractDeviceParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/device/cameras.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'camera';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
if (!$this->preMatchOverall()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parent::parse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Device;
|
||||
|
||||
/**
|
||||
* Class CarBrowser
|
||||
*
|
||||
* Device parser for car browser detection
|
||||
*/
|
||||
class CarBrowser extends AbstractDeviceParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/device/car_browsers.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'car browser';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
if (!$this->preMatchOverall()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parent::parse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Device;
|
||||
|
||||
/**
|
||||
* Class Console
|
||||
*
|
||||
* Device parser for console detection
|
||||
*/
|
||||
class Console extends AbstractDeviceParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/device/consoles.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'console';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
if (!$this->preMatchOverall()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parent::parse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Device;
|
||||
|
||||
/**
|
||||
* Class HbbTv
|
||||
*
|
||||
* Device parser for hbbtv detection
|
||||
*/
|
||||
class HbbTv extends AbstractDeviceParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/device/televisions.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'tv';
|
||||
|
||||
/**
|
||||
* Parses the current UA and checks whether it contains HbbTv or SmartTvA information
|
||||
*
|
||||
* @see televisions.yml for list of detected televisions
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
// only parse user agents containing fragments: hbbtv or SmartTvA
|
||||
if (null === $this->isHbbTv()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
parent::parse();
|
||||
|
||||
// always set device type to tv, even if no model/brand could be found
|
||||
if (null === $this->deviceType) {
|
||||
$this->deviceType = self::DEVICE_TYPE_TV;
|
||||
}
|
||||
|
||||
return $this->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the parsed UA was identified as a HbbTV device
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function isHbbTv(): ?string
|
||||
{
|
||||
$regex = '(?:HbbTV|SmartTvA)/([1-9]{1}(?:\.[0-9]{1}){1,2})';
|
||||
$match = $this->matchUserAgent($regex);
|
||||
|
||||
return $match[1] ?? null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Device;
|
||||
|
||||
/**
|
||||
* Class Mobile
|
||||
*
|
||||
* Device parser for mobile detection
|
||||
*/
|
||||
class Mobile extends AbstractDeviceParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/device/mobiles.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'mobile';
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Device;
|
||||
|
||||
/**
|
||||
* Class Notebook
|
||||
*
|
||||
* Device parser for notebook detection in Facebook useragents
|
||||
*/
|
||||
class Notebook extends AbstractDeviceParser
|
||||
{
|
||||
protected $fixtureFile = 'regexes/device/notebooks.yml';
|
||||
|
||||
protected $parserName = 'notebook';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
if (!$this->matchUserAgent('FBMD/')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parent::parse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Device;
|
||||
|
||||
/**
|
||||
* Class PortableMediaPlayer
|
||||
*
|
||||
* Device parser for portable media player detection
|
||||
*/
|
||||
class PortableMediaPlayer extends AbstractDeviceParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/device/portable_media_player.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'portablemediaplayer';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
if (!$this->preMatchOverall()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parent::parse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser\Device;
|
||||
|
||||
/**
|
||||
* Class ShellTv
|
||||
*/
|
||||
class ShellTv extends AbstractDeviceParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/device/shell_tv.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'shelltv';
|
||||
|
||||
/**
|
||||
* Returns if the parsed UA was identified as ShellTv device
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function isShellTv(): bool
|
||||
{
|
||||
$regex = '[a-z]+[ _]Shell[ _]\w{6}|tclwebkit(\d+[.\d]*)';
|
||||
$match = $this->matchUserAgent($regex);
|
||||
|
||||
return null !== $match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the current UA and checks whether it contains ShellTv information
|
||||
*
|
||||
* @see shell_tv.yml for list of detected televisions
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
// only parse user agents containing fragments: {brand} shell
|
||||
if (false === $this->isShellTv()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
parent::parse();
|
||||
|
||||
// always set device type to tv, even if no model/brand could be found
|
||||
$this->deviceType = self::DEVICE_TYPE_TV;
|
||||
|
||||
return $this->getResult();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,756 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser;
|
||||
|
||||
use DeviceDetector\ClientHints;
|
||||
|
||||
/**
|
||||
* Class OperatingSystem
|
||||
*
|
||||
* Parses the useragent for operating system information
|
||||
*
|
||||
* Detected operating systems can be found in self::$operatingSystems and /regexes/oss.yml
|
||||
* This class also defined some operating system families and methods to get the family for a specific os
|
||||
*/
|
||||
class OperatingSystem extends AbstractParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/oss.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'os';
|
||||
|
||||
/**
|
||||
* Known operating systems mapped to their internal short codes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $operatingSystems = [
|
||||
'AIX' => 'AIX',
|
||||
'AND' => 'Android',
|
||||
'ADR' => 'Android TV',
|
||||
'ALP' => 'Alpine Linux',
|
||||
'AMZ' => 'Amazon Linux',
|
||||
'AMG' => 'AmigaOS',
|
||||
'ARM' => 'Armadillo OS',
|
||||
'ARO' => 'AROS',
|
||||
'ATV' => 'tvOS',
|
||||
'ARL' => 'Arch Linux',
|
||||
'AOS' => 'AOSC OS',
|
||||
'ASP' => 'ASPLinux',
|
||||
'AZU' => 'Azure Linux',
|
||||
'BTR' => 'BackTrack',
|
||||
'SBA' => 'Bada',
|
||||
'BYI' => 'Baidu Yi',
|
||||
'BEO' => 'BeOS',
|
||||
'BLB' => 'BlackBerry OS',
|
||||
'QNX' => 'BlackBerry Tablet OS',
|
||||
'PAN' => 'blackPanther OS',
|
||||
'BOS' => 'Bliss OS',
|
||||
'BMP' => 'Brew',
|
||||
'BSN' => 'BrightSignOS',
|
||||
'CAI' => 'Caixa Mágica',
|
||||
'CES' => 'CentOS',
|
||||
'CST' => 'CentOS Stream',
|
||||
'CLO' => 'Clear Linux OS',
|
||||
'CLR' => 'ClearOS Mobile',
|
||||
'COS' => 'Chrome OS',
|
||||
'CRS' => 'Chromium OS',
|
||||
'CHN' => 'China OS',
|
||||
'COL' => 'Coolita OS',
|
||||
'CYN' => 'CyanogenMod',
|
||||
'DEB' => 'Debian',
|
||||
'DEE' => 'Deepin',
|
||||
'DFB' => 'DragonFly',
|
||||
'DVK' => 'DVKBuntu',
|
||||
'ELE' => 'ElectroBSD',
|
||||
'EUL' => 'EulerOS',
|
||||
'FED' => 'Fedora',
|
||||
'FEN' => 'Fenix',
|
||||
'FOS' => 'Firefox OS',
|
||||
'FIR' => 'Fire OS',
|
||||
'FOR' => 'Foresight Linux',
|
||||
'FRE' => 'Freebox',
|
||||
'BSD' => 'FreeBSD',
|
||||
'FRI' => 'FRITZ!OS',
|
||||
'FYD' => 'FydeOS',
|
||||
'FUC' => 'Fuchsia',
|
||||
'GNT' => 'Gentoo',
|
||||
'GNX' => 'GENIX',
|
||||
'GEO' => 'GEOS',
|
||||
'GNS' => 'gNewSense',
|
||||
'GRI' => 'GridOS',
|
||||
'GTV' => 'Google TV',
|
||||
'HPX' => 'HP-UX',
|
||||
'HAI' => 'Haiku OS',
|
||||
'IPA' => 'iPadOS',
|
||||
'HAR' => 'HarmonyOS',
|
||||
'HAS' => 'HasCodingOS',
|
||||
'HEL' => 'HELIX OS',
|
||||
'IRI' => 'IRIX',
|
||||
'INF' => 'Inferno',
|
||||
'JME' => 'Java ME',
|
||||
'JOL' => 'Joli OS',
|
||||
'KOS' => 'KaiOS',
|
||||
'KAL' => 'Kali',
|
||||
'KAN' => 'Kanotix',
|
||||
'KIN' => 'KIN OS',
|
||||
'KOL' => 'KolibriOS',
|
||||
'KNO' => 'Knoppix',
|
||||
'KTV' => 'KreaTV',
|
||||
'KBT' => 'Kubuntu',
|
||||
'LIN' => 'GNU/Linux',
|
||||
'LEA' => 'LeafOS',
|
||||
'LND' => 'LindowsOS',
|
||||
'LNS' => 'Linspire',
|
||||
'LEN' => 'Lineage OS',
|
||||
'LIR' => 'Liri OS',
|
||||
'LOO' => 'Loongnix',
|
||||
'LBT' => 'Lubuntu',
|
||||
'LOS' => 'Lumin OS',
|
||||
'LUN' => 'LuneOS',
|
||||
'VLN' => 'VectorLinux',
|
||||
'MAC' => 'Mac',
|
||||
'MAE' => 'Maemo',
|
||||
'MAG' => 'Mageia',
|
||||
'MDR' => 'Mandriva',
|
||||
'SMG' => 'MeeGo',
|
||||
'MET' => 'Meta Horizon',
|
||||
'MCD' => 'MocorDroid',
|
||||
'MON' => 'moonOS',
|
||||
'EZX' => 'Motorola EZX',
|
||||
'MIN' => 'Mint',
|
||||
'MLD' => 'MildWild',
|
||||
'MOR' => 'MorphOS',
|
||||
'NBS' => 'NetBSD',
|
||||
'MTK' => 'MTK / Nucleus',
|
||||
'MRE' => 'MRE',
|
||||
'NXT' => 'NeXTSTEP',
|
||||
'NWS' => 'NEWS-OS',
|
||||
'WII' => 'Nintendo',
|
||||
'NDS' => 'Nintendo Mobile',
|
||||
'NOV' => 'Nova',
|
||||
'OS2' => 'OS/2',
|
||||
'T64' => 'OSF1',
|
||||
'OBS' => 'OpenBSD',
|
||||
'OVS' => 'OpenVMS',
|
||||
'OVZ' => 'OpenVZ',
|
||||
'OWR' => 'OpenWrt',
|
||||
'OTV' => 'Opera TV',
|
||||
'ORA' => 'Oracle Linux',
|
||||
'ORD' => 'Ordissimo',
|
||||
'PAR' => 'Pardus',
|
||||
'PCL' => 'PCLinuxOS',
|
||||
'PIC' => 'PICO OS',
|
||||
'PLA' => 'Plasma Mobile',
|
||||
'PSP' => 'PlayStation Portable',
|
||||
'PS3' => 'PlayStation',
|
||||
'PVE' => 'Proxmox VE',
|
||||
'PUF' => 'Puffin OS',
|
||||
'PUR' => 'PureOS',
|
||||
'QTP' => 'Qtopia',
|
||||
'PIO' => 'Raspberry Pi OS',
|
||||
'RAS' => 'Raspbian',
|
||||
'RHT' => 'Red Hat',
|
||||
'RST' => 'Red Star',
|
||||
'RED' => 'RedOS',
|
||||
'REV' => 'Revenge OS',
|
||||
'RIS' => 'risingOS',
|
||||
'ROS' => 'RISC OS',
|
||||
'ROC' => 'Rocky Linux',
|
||||
'ROK' => 'Roku OS',
|
||||
'RSO' => 'Rosa',
|
||||
'ROU' => 'RouterOS',
|
||||
'REM' => 'Remix OS',
|
||||
'RRS' => 'Resurrection Remix OS',
|
||||
'REX' => 'REX',
|
||||
'RZD' => 'RazoDroiD',
|
||||
'RXT' => 'RTOS & Next',
|
||||
'SAB' => 'Sabayon',
|
||||
'SSE' => 'SUSE',
|
||||
'SAF' => 'Sailfish OS',
|
||||
'SCI' => 'Scientific Linux',
|
||||
'SEE' => 'SeewoOS',
|
||||
'SER' => 'SerenityOS',
|
||||
'SIR' => 'Sirin OS',
|
||||
'SLW' => 'Slackware',
|
||||
'SOS' => 'Solaris',
|
||||
'SBL' => 'Star-Blade OS',
|
||||
'SYL' => 'Syllable',
|
||||
'SYM' => 'Symbian',
|
||||
'SYS' => 'Symbian OS',
|
||||
'S40' => 'Symbian OS Series 40',
|
||||
'S60' => 'Symbian OS Series 60',
|
||||
'SY3' => 'Symbian^3',
|
||||
'TEN' => 'TencentOS',
|
||||
'TDX' => 'ThreadX',
|
||||
'TIZ' => 'Tizen',
|
||||
'TIV' => 'TiVo OS',
|
||||
'TOS' => 'TmaxOS',
|
||||
'TUR' => 'Turbolinux',
|
||||
'UBT' => 'Ubuntu',
|
||||
'ULT' => 'ULTRIX',
|
||||
'UOS' => 'UOS',
|
||||
'VID' => 'VIDAA',
|
||||
'VIZ' => 'ViziOS',
|
||||
'WAS' => 'watchOS',
|
||||
'WER' => 'Wear OS',
|
||||
'WTV' => 'WebTV',
|
||||
'WHS' => 'Whale OS',
|
||||
'WIN' => 'Windows',
|
||||
'WCE' => 'Windows CE',
|
||||
'WIO' => 'Windows IoT',
|
||||
'WMO' => 'Windows Mobile',
|
||||
'WPH' => 'Windows Phone',
|
||||
'WRT' => 'Windows RT',
|
||||
'WPO' => 'WoPhone',
|
||||
'XBX' => 'Xbox',
|
||||
'XBT' => 'Xubuntu',
|
||||
'YNS' => 'YunOS',
|
||||
'ZEN' => 'Zenwalk',
|
||||
'ZOR' => 'ZorinOS',
|
||||
'IOS' => 'iOS',
|
||||
'POS' => 'palmOS',
|
||||
'WEB' => 'Webian',
|
||||
'WOS' => 'webOS',
|
||||
];
|
||||
|
||||
/**
|
||||
* Operating system families mapped to the short codes of the associated operating systems
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $osFamilies = [
|
||||
'Android' => [
|
||||
'AND', 'CYN', 'FIR', 'REM', 'RZD', 'MLD', 'MCD', 'YNS', 'GRI', 'HAR',
|
||||
'ADR', 'CLR', 'BOS', 'REV', 'LEN', 'SIR', 'RRS', 'WER', 'PIC', 'ARM',
|
||||
'HEL', 'BYI', 'RIS', 'PUF', 'LEA', 'MET',
|
||||
],
|
||||
'AmigaOS' => ['AMG', 'MOR', 'ARO'],
|
||||
'BlackBerry' => ['BLB', 'QNX'],
|
||||
'Brew' => ['BMP'],
|
||||
'BeOS' => ['BEO', 'HAI'],
|
||||
'Chrome OS' => ['COS', 'CRS', 'FYD', 'SEE'],
|
||||
'Firefox OS' => ['FOS', 'KOS'],
|
||||
'Gaming Console' => ['WII', 'PS3'],
|
||||
'Google TV' => ['GTV'],
|
||||
'IBM' => ['OS2'],
|
||||
'iOS' => ['IOS', 'ATV', 'WAS', 'IPA'],
|
||||
'RISC OS' => ['ROS'],
|
||||
'GNU/Linux' => [
|
||||
'LIN', 'ARL', 'DEB', 'KNO', 'MIN', 'UBT', 'KBT', 'XBT', 'LBT', 'FED',
|
||||
'RHT', 'VLN', 'MDR', 'GNT', 'SAB', 'SLW', 'SSE', 'CES', 'BTR', 'SAF',
|
||||
'ORD', 'TOS', 'RSO', 'DEE', 'FRE', 'MAG', 'FEN', 'CAI', 'PCL', 'HAS',
|
||||
'LOS', 'DVK', 'ROK', 'OWR', 'OTV', 'KTV', 'PUR', 'PLA', 'FUC', 'PAR',
|
||||
'FOR', 'MON', 'KAN', 'ZEN', 'LND', 'LNS', 'CHN', 'AMZ', 'TEN', 'CST',
|
||||
'NOV', 'ROU', 'ZOR', 'RED', 'KAL', 'ORA', 'VID', 'TIV', 'BSN', 'RAS',
|
||||
'UOS', 'PIO', 'FRI', 'LIR', 'WEB', 'SER', 'ASP', 'AOS', 'LOO', 'EUL',
|
||||
'SCI', 'ALP', 'CLO', 'ROC', 'OVZ', 'PVE', 'RST', 'EZX', 'GNS', 'JOL',
|
||||
'TUR', 'QTP', 'WPO', 'PAN', 'VIZ', 'AZU', 'COL',
|
||||
],
|
||||
'Mac' => ['MAC'],
|
||||
'Mobile Gaming Console' => ['PSP', 'NDS', 'XBX'],
|
||||
'OpenVMS' => ['OVS'],
|
||||
'Real-time OS' => ['MTK', 'TDX', 'MRE', 'JME', 'REX', 'RXT', 'KOL'],
|
||||
'Other Mobile' => ['WOS', 'POS', 'SBA', 'TIZ', 'SMG', 'MAE', 'LUN', 'GEO'],
|
||||
'Symbian' => ['SYM', 'SYS', 'SY3', 'S60', 'S40'],
|
||||
'Unix' => [
|
||||
'SOS', 'AIX', 'HPX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64',
|
||||
'INF', 'ELE', 'GNX', 'ULT', 'NWS', 'NXT', 'SBL',
|
||||
],
|
||||
'WebTV' => ['WTV'],
|
||||
'Windows' => ['WIN'],
|
||||
'Windows Mobile' => ['WPH', 'WMO', 'WCE', 'WRT', 'WIO', 'KIN'],
|
||||
'Other Smart TV' => ['WHS'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Contains a list of mappings from OS names we use to known client hint values
|
||||
*
|
||||
* @var array<string, array<string>>
|
||||
*/
|
||||
protected static $clientHintMapping = [
|
||||
'GNU/Linux' => ['Linux'],
|
||||
'Mac' => ['MacOS'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Operating system families that are known as desktop only
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $desktopOsArray = [
|
||||
'AmigaOS', 'IBM', 'GNU/Linux', 'Mac', 'Unix', 'Windows', 'BeOS', 'Chrome OS',
|
||||
];
|
||||
|
||||
/**
|
||||
* Fire OS version mapping
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $fireOsVersionMapping = [
|
||||
'11' => '8',
|
||||
'10' => '8',
|
||||
'9' => '7',
|
||||
'7' => '6',
|
||||
'5' => '5',
|
||||
'4.4.3' => '4.5.1',
|
||||
'4.4.2' => '4',
|
||||
'4.2.2' => '3',
|
||||
'4.0.3' => '3',
|
||||
'4.0.2' => '3',
|
||||
'4' => '2',
|
||||
'2' => '1',
|
||||
];
|
||||
|
||||
/**
|
||||
* Lineage OS version mapping
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $lineageOsVersionMapping = [
|
||||
'16' => '23',
|
||||
'15' => '22',
|
||||
'14' => '21',
|
||||
'13' => '20.0',
|
||||
'12.1' => '19.1',
|
||||
'12' => '19.0',
|
||||
'11' => '18.0',
|
||||
'10' => '17.0',
|
||||
'9' => '16.0',
|
||||
'8.1.0' => '15.1',
|
||||
'8.0.0' => '15.0',
|
||||
'7.1.2' => '14.1',
|
||||
'7.1.1' => '14.1',
|
||||
'7.0' => '14.0',
|
||||
'6.0.1' => '13.0',
|
||||
'6.0' => '13.0',
|
||||
'5.1.1' => '12.1',
|
||||
'5.0.2' => '12.0',
|
||||
'5.0' => '12.0',
|
||||
'4.4.4' => '11.0',
|
||||
'4.3' => '10.2',
|
||||
'4.2.2' => '10.1',
|
||||
'4.0.4' => '9.1.0',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns all available operating systems
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAvailableOperatingSystems(): array
|
||||
{
|
||||
return self::$operatingSystems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all available operating system families
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAvailableOperatingSystemFamilies(): array
|
||||
{
|
||||
return self::$osFamilies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the os name and shot name
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getShortOsData(string $name): array
|
||||
{
|
||||
$short = 'UNK';
|
||||
|
||||
foreach (self::$operatingSystems as $osShort => $osName) {
|
||||
if (\strtolower($name) !== \strtolower($osName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $osName;
|
||||
$short = $osShort;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return \compact('short', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
$this->restoreUserAgentFromClientHints();
|
||||
|
||||
$osFromClientHints = $this->parseOsFromClientHints();
|
||||
$osFromUserAgent = $this->parseOsFromUserAgent();
|
||||
|
||||
if (!empty($osFromClientHints['name'])) {
|
||||
$name = $osFromClientHints['name'];
|
||||
$version = $osFromClientHints['version'];
|
||||
|
||||
// use version from user agent if non was provided in client hints, but os family from useragent matches
|
||||
if (empty($version)
|
||||
&& self::getOsFamily($name) === self::getOsFamily($osFromUserAgent['name'])
|
||||
) {
|
||||
$version = $osFromUserAgent['version'];
|
||||
}
|
||||
|
||||
// On Windows, version 0.0.0 can be either 7, 8 or 8.1
|
||||
if ('Windows' === $name && '0.0.0' === $version) {
|
||||
$version = ('10' === $osFromUserAgent['version']) ? '' : $osFromUserAgent['version'];
|
||||
}
|
||||
|
||||
// If the OS name detected from client hints matches the OS family from user agent
|
||||
// but the os name is another, we use the one from user agent, as it might be more detailed
|
||||
if (self::getOsFamily($osFromUserAgent['name']) === $name && $osFromUserAgent['name'] !== $name) {
|
||||
$name = $osFromUserAgent['name'];
|
||||
|
||||
if ('LeafOS' === $name || 'HarmonyOS' === $name) {
|
||||
$version = '';
|
||||
}
|
||||
|
||||
if ('PICO OS' === $name) {
|
||||
$version = $osFromUserAgent['version'];
|
||||
}
|
||||
|
||||
if ('Fire OS' === $name && !empty($osFromClientHints['version'])) {
|
||||
$majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0');
|
||||
|
||||
$version = $this->fireOsVersionMapping[$version]
|
||||
?? $this->fireOsVersionMapping[$majorVersion] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
$short = $osFromClientHints['short_name'];
|
||||
|
||||
// Chrome OS is in some cases reported as Linux in client hints, we fix this only if the version matches
|
||||
if ('GNU/Linux' === $name
|
||||
&& 'Chrome OS' === $osFromUserAgent['name']
|
||||
&& $osFromClientHints['version'] === $osFromUserAgent['version']
|
||||
) {
|
||||
$name = $osFromUserAgent['name'];
|
||||
$short = $osFromUserAgent['short_name'];
|
||||
}
|
||||
|
||||
// Chrome OS is in some cases reported as Android in client hints
|
||||
if ('Android' === $name && 'Chrome OS' === $osFromUserAgent['name']) {
|
||||
$name = $osFromUserAgent['name'];
|
||||
$version = '';
|
||||
$short = $osFromUserAgent['short_name'];
|
||||
}
|
||||
|
||||
// Meta Horizon is reported as Linux in client hints
|
||||
if ('GNU/Linux' === $name && 'Meta Horizon' === $osFromUserAgent['name']) {
|
||||
$name = $osFromUserAgent['name'];
|
||||
$short = $osFromUserAgent['short_name'];
|
||||
}
|
||||
} elseif (!empty($osFromUserAgent['name'])) {
|
||||
$name = $osFromUserAgent['name'];
|
||||
$version = $osFromUserAgent['version'];
|
||||
$short = $osFromUserAgent['short_name'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
||||
$platform = $this->parsePlatform();
|
||||
$family = self::getOsFamily($short);
|
||||
$androidApps = [
|
||||
'com.hisense.odinbrowser', 'com.seraphic.openinet.pre', 'com.appssppa.idesktoppcbrowser',
|
||||
'every.browser.inc',
|
||||
];
|
||||
|
||||
if (null !== $this->clientHints) {
|
||||
if (\in_array($this->clientHints->getApp(), $androidApps) && 'Android' !== $name) {
|
||||
$name = 'Android';
|
||||
$family = 'Android';
|
||||
$short = 'ADR';
|
||||
$version = '';
|
||||
}
|
||||
|
||||
if ('org.lineageos.jelly' === $this->clientHints->getApp() && 'Lineage OS' !== $name) {
|
||||
$majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0');
|
||||
|
||||
$name = 'Lineage OS';
|
||||
$family = 'Android';
|
||||
$short = 'LEN';
|
||||
$version = $this->lineageOsVersionMapping[$version]
|
||||
?? $this->lineageOsVersionMapping[$majorVersion] ?? '';
|
||||
}
|
||||
|
||||
if ('org.mozilla.tv.firefox' === $this->clientHints->getApp() && 'Fire OS' !== $name) {
|
||||
$majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0');
|
||||
|
||||
$name = 'Fire OS';
|
||||
$family = 'Android';
|
||||
$short = 'FIR';
|
||||
$version = $this->fireOsVersionMapping[$version] ?? $this->fireOsVersionMapping[$majorVersion] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
$return = [
|
||||
'name' => $name,
|
||||
'short_name' => $short,
|
||||
'version' => $version,
|
||||
'platform' => $platform,
|
||||
'family' => $family,
|
||||
];
|
||||
|
||||
if (\in_array($return['name'], self::$operatingSystems)) {
|
||||
$return['short_name'] = \array_search($return['name'], self::$operatingSystems);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operating system family for the given operating system
|
||||
*
|
||||
* @param string $osLabel name or short name
|
||||
*
|
||||
* @return string|null If null, "Unknown"
|
||||
*/
|
||||
public static function getOsFamily(string $osLabel): ?string
|
||||
{
|
||||
if (\in_array($osLabel, self::$operatingSystems)) {
|
||||
$osLabel = \array_search($osLabel, self::$operatingSystems);
|
||||
}
|
||||
|
||||
foreach (self::$osFamilies as $family => $labels) {
|
||||
if (\in_array($osLabel, $labels)) {
|
||||
return (string) $family;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if OS is desktop
|
||||
*
|
||||
* @param string $osName OS short name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isDesktopOs(string $osName): bool
|
||||
{
|
||||
$osFamily = self::getOsFamily($osName);
|
||||
|
||||
return \in_array($osFamily, self::$desktopOsArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full name for the given short name
|
||||
*
|
||||
* @param string $os
|
||||
* @param string|null $ver
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public static function getNameFromId(string $os, ?string $ver = null): ?string
|
||||
{
|
||||
if (\array_key_exists($os, self::$operatingSystems)) {
|
||||
$osFullName = self::$operatingSystems[$os];
|
||||
|
||||
return \trim($osFullName . ' ' . $ver);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OS that can be safely detected from client hints
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function parseOsFromClientHints(): array
|
||||
{
|
||||
$name = $version = $short = '';
|
||||
|
||||
if ($this->clientHints instanceof ClientHints && $this->clientHints->getOperatingSystem()) {
|
||||
$hintName = $this->applyClientHintMapping($this->clientHints->getOperatingSystem());
|
||||
|
||||
foreach (self::$operatingSystems as $osShort => $osName) {
|
||||
if ($this->fuzzyCompare($hintName, $osName)) {
|
||||
$name = $osName;
|
||||
$short = $osShort;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$version = $this->clientHints->getOperatingSystemVersion();
|
||||
|
||||
if ('Windows' === $name) {
|
||||
$majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0');
|
||||
$minorVersion = (int) (\explode('.', $version, 2)[1] ?? '0');
|
||||
|
||||
if (0 === $majorVersion) {
|
||||
$minorVersionMapping = [1 => '7', 2 => '8', 3 => '8.1'];
|
||||
$version = $minorVersionMapping[$minorVersion] ?? $version;
|
||||
} elseif ($majorVersion > 0 && $majorVersion < 11) {
|
||||
$version = '10';
|
||||
} elseif ($majorVersion > 10) {
|
||||
$version = '11';
|
||||
}
|
||||
}
|
||||
|
||||
// On Windows, version 0.0.0 can be either 7, 8 or 8.1, so we return 0.0.0
|
||||
if ('Windows' !== $name && '0.0.0' !== $version && 0 === (int) $version) {
|
||||
$version = '';
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'short_name' => $short,
|
||||
'version' => $this->buildVersion($version, []),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OS that can be detected from useragent
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function parseOsFromUserAgent(): array
|
||||
{
|
||||
$osRegex = $matches = [];
|
||||
$name = $version = $short = '';
|
||||
|
||||
foreach ($this->getRegexes() as $osRegex) {
|
||||
$matches = $this->matchUserAgent($osRegex['regex']);
|
||||
|
||||
if ($matches) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($matches)) {
|
||||
$name = $this->buildByMatch($osRegex['name'], $matches);
|
||||
['name' => $name, 'short' => $short] = self::getShortOsData($name);
|
||||
|
||||
$version = \array_key_exists('version', $osRegex)
|
||||
? $this->buildVersion((string) $osRegex['version'], $matches)
|
||||
: '';
|
||||
|
||||
foreach ($osRegex['versions'] ?? [] as $regex) {
|
||||
$matches = $this->matchUserAgent($regex['regex']);
|
||||
|
||||
if (!$matches) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\array_key_exists('name', $regex)) {
|
||||
$name = $this->buildByMatch($regex['name'], $matches);
|
||||
['name' => $name, 'short' => $short] = self::getShortOsData($name);
|
||||
}
|
||||
|
||||
if (\array_key_exists('version', $regex)) {
|
||||
$version = $this->buildVersion((string) $regex['version'], $matches);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'short_name' => $short,
|
||||
'version' => $version,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse current UserAgent string for the operating system platform
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function parsePlatform(): string
|
||||
{
|
||||
// Use architecture from client hints if available
|
||||
if ($this->clientHints instanceof ClientHints && $this->clientHints->getArchitecture()) {
|
||||
$arch = \strtolower($this->clientHints->getArchitecture());
|
||||
|
||||
if (false !== \strpos($arch, 'arm')) {
|
||||
return 'ARM';
|
||||
}
|
||||
|
||||
if (false !== \strpos($arch, 'loongarch64')) {
|
||||
return 'LoongArch64';
|
||||
}
|
||||
|
||||
if (false !== \strpos($arch, 'mips')) {
|
||||
return 'MIPS';
|
||||
}
|
||||
|
||||
if (false !== \strpos($arch, 'sh4')) {
|
||||
return 'SuperH';
|
||||
}
|
||||
|
||||
if (false !== \strpos($arch, 'sparc64')) {
|
||||
return 'SPARC64';
|
||||
}
|
||||
|
||||
if (false !== \strpos($arch, 'x64')
|
||||
|| (false !== \strpos($arch, 'x86') && '64' === $this->clientHints->getBitness())
|
||||
) {
|
||||
return 'x64';
|
||||
}
|
||||
|
||||
if (false !== \strpos($arch, 'x86')) {
|
||||
return 'x86';
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->matchUserAgent('arm[ _;)ev]|.*arm$|.*arm64|aarch64|Apple ?TV|Watch ?OS|Watch1,[12]')) {
|
||||
return 'ARM';
|
||||
}
|
||||
|
||||
if ($this->matchUserAgent('loongarch64')) {
|
||||
return 'LoongArch64';
|
||||
}
|
||||
|
||||
if ($this->matchUserAgent('mips')) {
|
||||
return 'MIPS';
|
||||
}
|
||||
|
||||
if ($this->matchUserAgent('sh4')) {
|
||||
return 'SuperH';
|
||||
}
|
||||
|
||||
if ($this->matchUserAgent('sparc64')) {
|
||||
return 'SPARC64';
|
||||
}
|
||||
|
||||
if ($this->matchUserAgent('64-?bit|WOW64|(?:Intel)?x64|WINDOWS_64|win64|.*amd64|.*x86_?64')) {
|
||||
return 'x64';
|
||||
}
|
||||
|
||||
if ($this->matchUserAgent('.*32bit|.*win32|(?:i[0-9]|x)86|i86pc')) {
|
||||
return 'x86';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Device Detector - The Universal Device Detection library for parsing User Agents
|
||||
*
|
||||
* @link https://matomo.org
|
||||
*
|
||||
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DeviceDetector\Parser;
|
||||
|
||||
/**
|
||||
* Class VendorFragments
|
||||
*
|
||||
* Device parser for vendor fragment detection
|
||||
*/
|
||||
class VendorFragment extends AbstractParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fixtureFile = 'regexes/vendorfragments.yml';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parserName = 'vendorfragments';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $matchedRegex = null;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(): ?array
|
||||
{
|
||||
foreach ($this->getRegexes() as $brand => $regexes) {
|
||||
foreach ($regexes as $regex) {
|
||||
if ($this->matchUserAgent($regex . '[^a-z0-9]+')) {
|
||||
$this->matchedRegex = $regex;
|
||||
|
||||
return ['brand' => $brand];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMatchedRegex(): ?string
|
||||
{
|
||||
return $this->matchedRegex;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user