• 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

79.55
/src/Parser/Header/UseragentEngineVersion.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\Engine;
17
use BrowserDetector\Version\ForcedNullVersion;
18
use BrowserDetector\Version\NullVersion;
19
use BrowserDetector\Version\VersionInterface;
20
use Override;
21
use UaData\EngineInterface;
22
use UaLoader\EngineLoaderInterface;
23
use UaNormalizer\Normalizer\Exception\Exception;
24
use UaNormalizer\Normalizer\NormalizerInterface;
25
use UaParser\EngineParserInterface;
26
use UaParser\EngineVersionInterface;
27
use UnexpectedValueException;
28

29
use function array_filter;
30
use function array_map;
31
use function mb_strtolower;
32
use function preg_match;
33
use function reset;
34

35
final readonly class UseragentEngineVersion implements EngineVersionInterface
36
{
37
    use SetVersionTrait;
38

39
    /** @throws void */
40
    public function __construct(
25✔
41
        private EngineParserInterface $engineParser,
42
        private EngineLoaderInterface $engineLoader,
43
        private NormalizerInterface $normalizer,
44
    ) {
45
        // nothing to do
46
    }
25✔
47

48
    /**
49
     * @throws void
50
     *
51
     * @phpcs:disable SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
52
     */
53
    #[Override]
25✔
54
    public function hasEngineVersion(string $value): bool
55
    {
56
        return true;
25✔
57
    }
58

59
    /** @throws void */
60
    #[Override]
25✔
61
    public function getEngineVersionWithEngine(string $value, EngineInterface $engine): VersionInterface
62
    {
63
        return $this->getVersion($value, $engine);
25✔
64
    }
65

66
    /** @throws void */
67
    private function getVersion(string $value, EngineInterface $engine): VersionInterface
25✔
68
    {
69
        try {
70
            $normalizedValue = $this->normalizer->normalize($value);
25✔
UNCOV
71
        } catch (Exception) {
×
UNCOV
72
            return new ForcedNullVersion();
×
73
        }
74

75
        if ($normalizedValue === '' || $normalizedValue === null) {
25✔
76
            return new ForcedNullVersion();
×
77
        }
78

79
        $regexes = [
25✔
80
            '/(?<!o)re\([^\/]+\/(?P<version>[\d.]+)/i',
25✔
81
            '/mozilla\/[\d.]+ \(mobile; [^;]+(?:;android)?; rv:[^)]+\) gecko\/(?P<version>[\d.]+) firefox\/[\d.]+ kaios\/[\d.]+/i',
25✔
82
        ];
25✔
83

84
        $filtered = array_filter(
25✔
85
            $regexes,
25✔
86
            static fn (string $regex): bool => (bool) preg_match($regex, $normalizedValue),
25✔
87
        );
25✔
88

89
        $results = array_map(
25✔
90
            static function (string $regex) use ($normalizedValue): string {
25✔
91
                $matches = [];
10✔
92

93
                preg_match($regex, $normalizedValue, $matches);
10✔
94

95
                return mb_strtolower($matches['version'] ?? '');
10✔
96
            },
25✔
97
            $filtered,
25✔
98
        );
25✔
99

100
        $detectedVersion = reset($results);
25✔
101

102
        if ($detectedVersion !== null && $detectedVersion !== false && $detectedVersion !== '') {
25✔
103
            return $this->setVersion($detectedVersion);
10✔
104
        }
105

106
        if ($engine === Engine::unknown) {
15✔
107
            $engine = $this->engineParser->parse($normalizedValue);
15✔
108

109
            if ($engine === Engine::unknown) {
15✔
UNCOV
110
                return new ForcedNullVersion();
×
111
            }
112
        }
113

114
        try {
115
            $engineObj = $this->engineLoader->loadFromEngine($engine, $normalizedValue);
15✔
UNCOV
116
        } catch (UnexpectedValueException) {
×
UNCOV
117
            return new NullVersion();
×
118
        }
119

120
        try {
121
            $version = $engineObj->getVersion()->getVersion();
15✔
UNCOV
122
        } catch (UnexpectedValueException) {
×
123
            return new NullVersion();
×
124
        }
125

126
        if ($version === '' || $version === null) {
15✔
UNCOV
127
            return new NullVersion();
×
128
        }
129

130
        return $this->setVersion($version);
15✔
131
    }
132
}
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