Skip to content

Commit c2e0b42

Browse files
committed
Ready to release 4.3.0.
1 parent b0ff48c commit c2e0b42

28 files changed

+4629
-2356
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ All notable changes to **Device Detector** are documented in this *changelog*.
33

44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and **Device Detector** adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
55

6-
## [4.3.0] - Not Yet Released
6+
## [4.3.0] - 2025-04-14
77

88
### Added
99
- Compatibility with WordPress 6.8.
1010

1111
### Changed
12-
-
12+
- Upgraded UDD from version 6.4.1 to version 6.4.5: dozens of added and improved detections.
1313

1414
### Fixed
1515
- Plugin update process may be confused when it founds error in release file.

device-detector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* Plugin Name: Device Detector
1111
* Plugin URI: https://perfops.one/device-detector
1212
* Description: Full featured analytics reporting and management tool that detects all devices accessing your WordPress site.
13-
* Version: 4.2.1
13+
* Version: 4.3.0
1414
* Requires at least: 6.2
1515
* Requires PHP: 8.1
1616
* Author: Pierre Lannoy / PerfOps One

includes/libraries/udd/ClientHints.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ public static function factory(array $headers): ClientHints
259259
$formFactors = [];
260260

261261
foreach ($headers as $name => $value) {
262+
if (empty($value)) {
263+
continue;
264+
}
265+
262266
switch (\str_replace('_', '-', \strtolower((string) $name))) {
263267
case 'http-sec-ch-ua-arch':
264268
case 'sec-ch-ua-arch':

includes/libraries/udd/DeviceDetector.php

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class DeviceDetector
6868
/**
6969
* Current version number of DeviceDetector
7070
*/
71-
public const VERSION = '6.4.1';
71+
public const VERSION = '6.4.5';
7272

7373
/**
7474
* Constant used as value for unknown browser / os
@@ -776,7 +776,7 @@ protected function getOsAttribute(string $attr): string
776776
*/
777777
protected function hasAndroidTableFragment(): bool
778778
{
779-
$regex = 'Android( [\.0-9]+)?; Tablet;|Tablet(?! PC)|.*\-tablet$';
779+
$regex = 'Android( [.0-9]+)?; Tablet;|Tablet(?! PC)|.*\-tablet$';
780780

781781
return !!$this->matchUserAgent($regex);
782782
}
@@ -788,7 +788,7 @@ protected function hasAndroidTableFragment(): bool
788788
*/
789789
protected function hasAndroidMobileFragment(): bool
790790
{
791-
$regex = 'Android( [\.0-9]+)?; Mobile;|.*\-mobile$';
791+
$regex = 'Android( [.0-9]+)?; Mobile;|.*\-mobile$';
792792

793793
return !!$this->matchUserAgent($regex);
794794
}
@@ -800,7 +800,7 @@ protected function hasAndroidMobileFragment(): bool
800800
*/
801801
protected function hasAndroidVRFragment(): bool
802802
{
803-
$regex = 'Android( [\.0-9]+)?; Mobile VR;| VR ';
803+
$regex = 'Android( [.0-9]+)?; Mobile VR;| VR ';
804804

805805
return !!$this->matchUserAgent($regex);
806806
}
@@ -959,7 +959,7 @@ protected function parseDevice(): void
959959
* a detected browser, but can still be detected. So we check the useragent for Chrome instead.
960960
*/
961961
if (null === $this->device && 'Android' === $osFamily
962-
&& $this->matchUserAgent('Chrome/[\.0-9]*')
962+
&& $this->matchUserAgent('Chrome/[.0-9]*')
963963
) {
964964
if ($this->matchUserAgent('(?:Mobile|eliboM)')) {
965965
$this->device = AbstractDeviceParser::DEVICE_TYPE_SMARTPHONE;
@@ -1017,12 +1017,19 @@ protected function parseDevice(): void
10171017
}
10181018

10191019
/**
1020-
* All unknown devices under running Java ME are more likely a features phones
1020+
* All unknown devices under running Java ME are more likely features phones
10211021
*/
10221022
if ('Java ME' === $osName && null === $this->device) {
10231023
$this->device = AbstractDeviceParser::DEVICE_TYPE_FEATURE_PHONE;
10241024
}
10251025

1026+
/**
1027+
* All devices running KaiOS are more likely features phones
1028+
*/
1029+
if ('KaiOS' === $osName) {
1030+
$this->device = AbstractDeviceParser::DEVICE_TYPE_FEATURE_PHONE;
1031+
}
1032+
10261033
/**
10271034
* According to http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
10281035
* Internet Explorer 10 introduces the "Touch" UA string token. If this token is present at the end of the
@@ -1067,10 +1074,22 @@ protected function parseDevice(): void
10671074
$this->device = AbstractDeviceParser::DEVICE_TYPE_TV;
10681075
}
10691076

1077+
/**
1078+
* All devices running Coolita OS are assumed to be a tv
1079+
*/
1080+
if ('Coolita OS' === $osName) {
1081+
$this->device = AbstractDeviceParser::DEVICE_TYPE_TV;
1082+
}
1083+
10701084
/**
10711085
* All devices that contain Andr0id in string are assumed to be a tv
10721086
*/
1073-
if ($this->matchUserAgent('Andr0id|(?:Android(?: UHD)?|Google) TV|\(lite\) TV|BRAVIA| TV$')) {
1087+
$hasDeviceTvType = false === \in_array($this->device, [
1088+
AbstractDeviceParser::DEVICE_TYPE_TV,
1089+
AbstractDeviceParser::DEVICE_TYPE_PERIPHERAL,
1090+
]) && $this->matchUserAgent('Andr0id|(?:Android(?: UHD)?|Google) TV|\(lite\) TV|BRAVIA| TV$');
1091+
1092+
if ($hasDeviceTvType) {
10741093
$this->device = AbstractDeviceParser::DEVICE_TYPE_TV;
10751094
}
10761095

includes/libraries/udd/Parser/AbstractParser.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,43 @@ public function __construct(string $ua = '', ?ClientHints $clientHints = null)
138138
$this->setClientHints($clientHints);
139139
}
140140

141+
/**
142+
* @inheritdoc
143+
*/
144+
public function restoreUserAgentFromClientHints(): void
145+
{
146+
if (null === $this->clientHints) {
147+
return;
148+
}
149+
150+
$deviceModel = $this->clientHints->getModel();
151+
152+
if ('' === $deviceModel) {
153+
return;
154+
}
155+
156+
// Restore Android User Agent
157+
if ($this->hasUserAgentClientHintsFragment()) {
158+
$osVersion = $this->clientHints->getOperatingSystemVersion();
159+
$this->setUserAgent((string) \preg_replace(
160+
'(Android (?:10[.\d]*; K|1[1-5]))',
161+
\sprintf('Android %s; %s', '' !== $osVersion ? $osVersion : '10', $deviceModel),
162+
$this->userAgent
163+
));
164+
}
165+
166+
// Restore Desktop User Agent
167+
if (!$this->hasDesktopFragment()) {
168+
return;
169+
}
170+
171+
$this->setUserAgent((string) \preg_replace(
172+
'(X11; Linux x86_64)',
173+
\sprintf('X11; Linux x86_64; %s', $deviceModel),
174+
$this->userAgent
175+
));
176+
}
177+
141178
/**
142179
* Set how DeviceDetector should return versions
143180
* @param int $type Any of the VERSION_TRUNCATION_* constants
@@ -298,6 +335,34 @@ protected function getRegexesDirectory(): string
298335
return \dirname(__DIR__);
299336
}
300337

338+
/**
339+
* Returns if the parsed UA contains the 'Windows NT;' or 'X11; Linux x86_64' fragments
340+
*
341+
* @return bool
342+
*/
343+
protected function hasDesktopFragment(): bool
344+
{
345+
$regexExcludeDesktopFragment = \implode('|', [
346+
'CE-HTML',
347+
' Mozilla/|Andr[o0]id|Tablet|Mobile|iPhone|Windows Phone|ricoh|OculusBrowser',
348+
'PicoBrowser|Lenovo|compatible; MSIE|Trident/|Tesla/|XBOX|FBMD/|ARM; ?([^)]+)',
349+
]);
350+
351+
return
352+
$this->matchUserAgent('(?:Windows (?:NT|IoT)|X11; Linux x86_64)') &&
353+
!$this->matchUserAgent($regexExcludeDesktopFragment);
354+
}
355+
356+
/**
357+
* Returns if the parsed UA contains the 'Android 10 K;' or Android 10 K Build/` fragment
358+
*
359+
* @return bool
360+
*/
361+
protected function hasUserAgentClientHintsFragment(): bool
362+
{
363+
return (bool) \preg_match('~Android (?:10[.\d]*; K(?: Build/|[;)])|1[1-5]\)) AppleWebKit~i', $this->userAgent);
364+
}
365+
301366
/**
302367
* Matches the useragent against the given regex
303368
*

includes/libraries/udd/Parser/Client/AbstractClientParser.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,27 @@ public static function getAvailableClients(): array
7878
$names = [];
7979

8080
foreach ($regexes as $regex) {
81-
if ('$1' === $regex['name']) {
81+
if (false !== \strpos($regex['name'], '$1')) {
8282
continue;
8383
}
8484

8585
$names[] = $regex['name'];
8686
}
8787

88+
if (static::class === MobileApp::class) {
89+
$names = \array_merge($names, [
90+
// Microsoft Office $1
91+
'Microsoft Office Access', 'Microsoft Office Excel', 'Microsoft Office OneDrive for Business',
92+
'Microsoft Office OneNote', 'Microsoft Office PowerPoint', 'Microsoft Office Project',
93+
'Microsoft Office Publisher', 'Microsoft Office Visio', 'Microsoft Office Word',
94+
// Podkicker$1
95+
'Podkicker', 'Podkicker Pro', 'Podkicker Classic',
96+
// radio.$1
97+
'radio.at', 'radio.de', 'radio.dk', 'radio.es', 'radio.fr',
98+
'radio.it', 'radio.pl', 'radio.pt', 'radio.se', 'radio.net',
99+
]);
100+
}
101+
88102
\natcasesort($names);
89103

90104
return \array_unique($names);

includes/libraries/udd/Parser/Client/Browser.php

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class Browser extends AbstractClientParser
8989
'AL' => 'Aloha Browser',
9090
'AH' => 'Aloha Browser Lite',
9191
'A8' => 'ALVA',
92+
'9A' => 'AltiBrowser',
9293
'AM' => 'Amaya',
9394
'A3' => 'Amaze Browser',
9495
'A5' => 'Amerigo',
@@ -108,7 +109,7 @@ class Browser extends AbstractClientParser
108109
'AW' => 'Amiga Aweb',
109110
'PN' => 'APN Browser',
110111
'6A' => 'Arachne',
111-
'RA' => 'Arc',
112+
'RA' => 'Arc Search',
112113
'R5' => 'Armorfly Browser',
113114
'AI' => 'Arvin',
114115
'AK' => 'Ask.com',
@@ -300,6 +301,7 @@ class Browser extends AbstractClientParser
300301
'HA' => 'Hawk Turbo Browser',
301302
'HQ' => 'Hawk Quick Browser',
302303
'HE' => 'Helio',
304+
'HN' => 'Herond Browser',
303305
'HX' => 'Hexa Web Browser',
304306
'HI' => 'Hi Browser',
305307
'HO' => 'hola! Browser',
@@ -390,6 +392,7 @@ class Browser extends AbstractClientParser
390392
'LX' => 'Lynx',
391393
'L2' => 'Lynket Browser',
392394
'MD' => 'Mandarin',
395+
'MP' => 'Maple',
393396
'M5' => 'MarsLab Web Browser',
394397
'M7' => 'MaxBrowser',
395398
'M1' => 'mCent',
@@ -577,6 +580,7 @@ class Browser extends AbstractClientParser
577580
'K1' => 'Sidekick',
578581
'S1' => 'SimpleBrowser',
579582
'3S' => 'SilverMob US',
583+
'ZB' => 'Singlebox',
580584
'SY' => 'Sizzy',
581585
'K3' => 'Skye',
582586
'SK' => 'Skyfire',
@@ -684,6 +688,7 @@ class Browser extends AbstractClientParser
684688
'WB' => 'Wave Browser',
685689
'WA' => 'Wavebox',
686690
'WH' => 'Whale Browser',
691+
'W2' => 'Whale TV Browser',
687692
'WO' => 'wOSBrowser',
688693
'3W' => 'w3m',
689694
'WT' => 'WeTab Browser',
@@ -767,15 +772,16 @@ class Browser extends AbstractClientParser
767772
'K4', 'WK', 'T3', 'K5', 'MU', '9P', 'K6', 'VR', 'N9',
768773
'M9', 'F9', '0P', '0A', 'JR', 'D3', 'TK', 'BP', '2F',
769774
'2M', 'K7', '1N', '8A', 'H7', 'X3', 'T4', 'X4', '5O',
770-
'8C', '3M', '6I', '2P', 'PU', '7I', 'X5',
775+
'8C', '3M', '6I', '2P', 'PU', '7I', 'X5', 'AL', '3P',
776+
'W2', 'ZB', 'HN',
771777
],
772778
'Firefox' => [
773779
'FF', 'BI', 'BF', 'BH', 'BN', 'C0', 'CU', 'EI', 'F1',
774-
'FB', 'FE', 'AX', 'FM', 'FR', 'FY', 'GZ', 'I4', 'IF',
780+
'FB', 'FE', 'AX', 'FM', 'FR', 'FY', 'I4', 'IF', '8P',
775781
'IW', 'LH', 'LY', 'MB', 'MN', 'MO', 'MY', 'OA', 'OS',
776782
'PI', 'PX', 'QA', 'S5', 'SX', 'TF', 'TO', 'WF', 'ZV',
777-
'FP', 'AD', 'WL', '2I', 'P9', 'KJ', 'WY', 'VK', 'W5',
778-
'7C', 'N7', 'W7', '8P',
783+
'FP', 'AD', '2I', 'P9', 'KJ', 'WY', 'VK', 'W5',
784+
'7C', 'N7', 'W7',
779785
],
780786
'Internet Explorer' => ['IE', 'CZ', 'BZ', 'IM', 'PS', '3A', '4A', 'RN'],
781787
'Konqueror' => ['KO'],
@@ -816,7 +822,7 @@ class Browser extends AbstractClientParser
816822
'DP', 'KL', 'K4', 'N6', 'KU', 'WK', 'M8', 'UP', 'ZT',
817823
'9P', 'N8', 'VR', 'N9', 'M9', 'F9', '0P', '0A', '2F',
818824
'2M', 'K7', '1N', '8A', 'H7', 'X3', 'X4', '5O', '6I',
819-
'7I', 'X5',
825+
'7I', 'X5', '3P',
820826
];
821827

822828
/**
@@ -1042,6 +1048,13 @@ public function parse(): ?array
10421048
if ('DuckDuckGo Privacy Browser' === $name) {
10431049
$version = '';
10441050
}
1051+
1052+
// In case client hints report a more detailed engine version, we try to use this instead
1053+
if ('Blink' === $engine && 'Iridium' !== $name
1054+
&& \version_compare($engineVersion, $browserFromClientHints['version'], '<')
1055+
) {
1056+
$engineVersion = $browserFromClientHints['version'];
1057+
}
10451058
} else {
10461059
$name = $browserFromUserAgent['name'];
10471060
$version = $browserFromUserAgent['version'];
@@ -1074,7 +1087,7 @@ public function parse(): ?array
10741087
}
10751088
}
10761089

1077-
if (empty($name)) {
1090+
if (empty($name) || 1 === \preg_match('/Cypress|PhantomJS/', $this->userAgent)) {
10781091
return [];
10791092
}
10801093

@@ -1097,6 +1110,14 @@ public function parse(): ?array
10971110
$engineVersion = '';
10981111
}
10991112

1113+
if ('Wolvic' === $name && 'Blink' === $engine) {
1114+
$family = 'Chrome';
1115+
}
1116+
1117+
if ('Wolvic' === $name && 'Gecko' === $engine) {
1118+
$family = 'Firefox';
1119+
}
1120+
11001121
return [
11011122
'type' => 'browser',
11021123
'name' => $name,

includes/libraries/udd/Parser/Client/Browser/Engine.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class Engine extends AbstractClientParser
5656
'EkiohFlow',
5757
'Arachne',
5858
'LibWeb',
59+
'Maple',
5960
];
6061

6162
/**

includes/libraries/udd/Parser/Client/Browser/Engine/Version.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function parse(): ?array
4949
}
5050

5151
if ('Gecko' === $this->engine || 'Clecko' === $this->engine) {
52-
$pattern = '~[ ](?:rv[: ]([0-9\.]+)).*(?:g|cl)ecko/[0-9]{8,10}~i';
52+
$pattern = '~[ ](?:rv[: ]([0-9.]+)).*(?:g|cl)ecko/[0-9]{8,10}~i';
5353

5454
if (\preg_match($pattern, $this->userAgent, $matches)) {
5555
return ['version' => \array_pop($matches)];
@@ -59,7 +59,7 @@ public function parse(): ?array
5959
$engineToken = $this->engine;
6060

6161
if ('Blink' === $this->engine) {
62-
$engineToken = 'Chr[o0]me|Cronet';
62+
$engineToken = 'Chr[o0]me|Chromium|Cronet';
6363
}
6464

6565
if ('Arachne' === $this->engine) {
@@ -71,7 +71,7 @@ public function parse(): ?array
7171
}
7272

7373
\preg_match(
74-
"~(?:{$engineToken})\s*/?\s*((?(?=\d+\.\d)\d+[.\d]*|\d{1,7}(?=(?:\D|$))))~i",
74+
"~(?:{$engineToken})\s*[/_]?\s*((?(?=\d+\.\d)\d+[.\d]*|\d{1,7}(?=(?:\D|$))))~i",
7575
$this->userAgent,
7676
$matches
7777
);

0 commit comments

Comments
 (0)