• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mimmi20 / browser-detector / 21621132528

03 Feb 2026 05:25AM UTC coverage: 95.958% (+0.7%) from 95.301%
21621132528

push

github

web-flow
Merge pull request #1030 from mimmi20/updates

add new clients and devices

693 of 698 new or added lines in 20 files covered. (99.28%)

42 existing lines in 4 files now uncovered.

12298 of 12816 relevant lines covered (95.96%)

60.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.15
/src/Parser/Header/UseragentPlatformCode.php
1
<?php
2

3
/**
4
 * This file is part of the browser-detector package.
5
 *
6
 * Copyright (c) 2012-2026, Thomas Mueller <mimmi20@live.de>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types = 1);
13

14
namespace BrowserDetector\Parser\Header;
15

16
use BrowserDetector\Data\Os;
17
use Override;
18
use UaData\OsInterface;
19
use UaNormalizer\Normalizer\Exception\Exception;
20
use UaNormalizer\Normalizer\NormalizerInterface;
21
use UaParser\PlatformCodeInterface;
22
use UaParser\PlatformParserInterface;
23
use UnexpectedValueException;
24

25
use function array_filter;
26
use function array_first;
27
use function array_key_exists;
28
use function array_map;
29
use function mb_strtolower;
30
use function preg_match;
31

32
final readonly class UseragentPlatformCode implements PlatformCodeInterface
33
{
34
    /** @throws void */
35
    public function __construct(
27✔
36
        private PlatformParserInterface $platformParser,
37
        private NormalizerInterface $normalizer,
38
    ) {
39
        // nothing to do
40
    }
27✔
41

42
    /**
43
     * @throws void
44
     *
45
     * @phpcs:disable SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
46
     */
47
    #[Override]
27✔
48
    public function hasPlatformCode(string $value): bool
49
    {
50
        return true;
27✔
51
    }
52

53
    /**
54
     * @throws void
55
     *
56
     * @phpcs:disable SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
57
     */
58
    #[Override]
27✔
59
    public function getPlatformCode(string $value, string | null $derivate = null): OsInterface
60
    {
61
        try {
62
            $normalizedValue = $this->normalizer->normalize($value);
27✔
63
        } catch (Exception) {
×
UNCOV
64
            return Os::unknown;
×
65
        }
66

67
        if ($normalizedValue === '' || $normalizedValue === null) {
27✔
UNCOV
68
            return Os::unknown;
×
69
        }
70

71
        $matches = [];
27✔
72

73
        if (preg_match('/ov\((?P<platform>wds|android) [\d_.]+\);/i', $normalizedValue, $matches)) {
27✔
74
            $code = mb_strtolower($matches['platform']);
2✔
75

76
            return match ($code) {
2✔
77
                'wds' => Os::windowsphone,
1✔
78
                'android' => Os::android,
1✔
79
                default => Os::unknown,
2✔
80
            };
2✔
81
        }
82

83
        if (preg_match('/pf\((?P<platform>[^)]+)\);/', $normalizedValue, $matches)) {
25✔
84
            $code = mb_strtolower($matches['platform']);
8✔
85

86
            return match ($code) {
8✔
87
                'symbian' => Os::symbianOs,
1✔
88
                'java' => Os::javaos,
1✔
89
                'windows' => Os::windowsphone,
1✔
90
                '42', '44' => Os::ios,
2✔
91
                'linux' => Os::android,
1✔
92
                default => Os::unknown,
8✔
93
            };
8✔
94
        }
95

96
        $matches = [];
17✔
97

98
        if (
99
            preg_match('/^WhatsApp\/[0-9.]+ (?P<code>[AW])$/', $normalizedValue, $matches)
17✔
100
            && array_key_exists('code', $matches)
17✔
101
        ) {
102
            return match ($matches['code']) {
2✔
103
                'W' => Os::windows,
2✔
104
                default => Os::android,
2✔
105
            };
2✔
106
        }
107

108
        $regexes = [
15✔
109
            '/instagram [\d.]+ (?P<platform>android) \([\d.]+\/[\d.]+; \d+dpi; \d+x\d+; [a-z\/]+; [^);\/]+;/i',
15✔
110
            '/icq_android\/[\d.]+ \((?P<platform>android); \d+; [\d.]+/i',
15✔
111
            '/gg-android\/[\d.]+ \(os;(?P<platform>android);\d+\) \([^);\/]+;[^);\/]+;[^);\/]+;[\d.]+/i',
15✔
112
            '/^(?P<platform>android) \d+ - /i',
15✔
113
            '/news republic\/[\d.]+ \(linux; (?P<platform>android) \d+/i',
15✔
114
            '/^app : mozilla\/[\d.]+ \(linux; (?P<platform>android) \d+ ; \w+ \)/i',
15✔
115
            '/mozilla\/[\d.]+ \(linux; (?P<platform>android) [\d.]+ ios;/i',
15✔
116
            '/ \/ (?P<platform>android) \d+$/i',
15✔
117
            '/wnyc app\/[\d.]+ (?P<platform>android)\/\d+ /i',
15✔
118
            '/mozilla\/[\d.]+ \(mobile; [^;]+(?:;android)?; rv:[^)]+\) gecko\/[\d.]+ firefox\/[\d.]+ (?P<platform>kaios)\/[\d.]+/i',
15✔
119
            '/virgin radio\/[\d.]+ \/ \(linux; (?P<platform>android) [\d.]+\) exoplayerlib\/[\d.]+ \/ samsung \(/i',
15✔
120
            '/pugpigbolt [\d.]+ \([^);\/,]+, (?P<platform>android|ios) [\d.]+\) on phone \(model [^)]+\)/i',
15✔
121
            '/nrc audio\/[\d.]+ \(nl\.nrc\.audio; build:[\d.]+; (?P<platform>android) [\d.]+; sdk:[\d.]+; manufacturer:samsung; model: [^)]+\) okhttp\/[\d.]+/i',
15✔
122
            '/luminary\/[\d.]+ \((?P<platform>android) [\d.]+; [^);\/]+; /i',
15✔
123
            '/(lbc|heart)\/[\d.]+ (?P<platform>android) [\d.]+\/[^);\/]+/i',
15✔
124
            '/emaudioplayer [\d.]+ \([\d.]+\) \/ (?P<platform>android) [\d.]+ \/ [^);\/]+/i',
15✔
125
            '/tivimate\/[\d.]+ \([^);\/]+; (?P<platform>android) [\d.]+\)/i',
15✔
126
            '/classic fm\/[\d.]+ (?P<platform>android) [\d.]+\/[^);\/]+/i',
15✔
127
        ];
15✔
128

129
        $filtered = array_filter(
15✔
130
            $regexes,
15✔
131
            static fn (string $regex): bool => (bool) preg_match($regex, $normalizedValue),
15✔
132
        );
15✔
133

134
        $results = array_map(
15✔
135
            static function (string $regex) use ($normalizedValue): string {
15✔
136
                $matches = [];
14✔
137

138
                preg_match($regex, $normalizedValue, $matches);
14✔
139

140
                // @todo: need to find a solution to find android forks like mocordroid
141
                return mb_strtolower($matches['platform'] ?? '');
14✔
142
            },
15✔
143
            $filtered,
15✔
144
        );
15✔
145

146
        $code = array_first($results);
15✔
147

148
        if ($code !== null && $code !== '') {
15✔
149
            try {
150
                return Os::fromName($code);
14✔
UNCOV
151
            } catch (UnexpectedValueException) {
×
UNCOV
152
                return Os::unknown;
×
153
            }
154
        }
155

156
        return $this->platformParser->parse($normalizedValue);
1✔
157
    }
158
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc