From 3939ea75e513f4f2fbaebfa1212ff1027b266520 Mon Sep 17 00:00:00 2001 From: tristan Date: Wed, 24 Jun 2026 10:10:58 +0200 Subject: [PATCH] =?UTF-8?q?feat(audit)=20:=20ajoute=20UserAgentParser=20(l?= =?UTF-8?q?ibell=C3=A9=20appareil=20lisible)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Service/UserAgentParser.php | 73 ++++++++++++++++++++++++++ tests/Service/UserAgentParserTest.php | 75 +++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 src/Service/UserAgentParser.php create mode 100644 tests/Service/UserAgentParserTest.php diff --git a/src/Service/UserAgentParser.php b/src/Service/UserAgentParser.php new file mode 100644 index 0000000..a087cab --- /dev/null +++ b/src/Service/UserAgentParser.php @@ -0,0 +1,73 @@ +detectType($ua), + $this->detectOs($ua), + $this->detectBrowser($ua), + ]); + } + + private function detectType(string $ua): string + { + if (1 === preg_match('/iPad|Tablet/i', $ua)) { + return 'Tablette'; + } + + if (1 === preg_match('/Mobile|Android|iPhone|iPod/i', $ua)) { + return 'Mobile'; + } + + return 'Ordinateur'; + } + + private function detectOs(string $ua): string + { + // Order matters: iOS before macOS (iOS UAs contain "Mac OS X"), + // Android before Linux (Android UAs contain "Linux"). + return match (true) { + 1 === preg_match('/iPhone|iPad|iPod/i', $ua) => 'iOS', + 1 === preg_match('/Android/i', $ua) => 'Android', + 1 === preg_match('/Windows/i', $ua) => 'Windows', + 1 === preg_match('/Mac OS X|Macintosh/i', $ua) => 'macOS', + 1 === preg_match('/Linux/i', $ua) => 'Linux', + default => 'Autre', + }; + } + + private function detectBrowser(string $ua): string + { + // Order matters: Edge/Opera contain "Chrome" and "Safari"; + // Chrome contains "Safari". Match the most specific first. + return match (true) { + 1 === preg_match('/Edg/i', $ua) => 'Edge', + 1 === preg_match('/OPR|Opera/i', $ua) => 'Opera', + 1 === preg_match('/Firefox|FxiOS/i', $ua) => 'Firefox', + 1 === preg_match('/Chrome|CriOS/i', $ua) => 'Chrome', + 1 === preg_match('/Safari/i', $ua) => 'Safari', + default => 'Autre', + }; + } +} diff --git a/tests/Service/UserAgentParserTest.php b/tests/Service/UserAgentParserTest.php new file mode 100644 index 0000000..56a7deb --- /dev/null +++ b/tests/Service/UserAgentParserTest.php @@ -0,0 +1,75 @@ +parser = new UserAgentParser(); + } + + public function testNullAndEmptyReturnNull(): void + { + self::assertNull($this->parser->parse(null)); + self::assertNull($this->parser->parse('')); + self::assertNull($this->parser->parse(' ')); + } + + public function testChromeOnWindows(): void + { + $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; + self::assertSame('Ordinateur · Windows · Chrome', $this->parser->parse($ua)); + } + + public function testEdgeBeatsChrome(): void + { + $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0'; + self::assertSame('Ordinateur · Windows · Edge', $this->parser->parse($ua)); + } + + public function testSafariOnIphoneIsMobileIos(): void + { + $ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1'; + self::assertSame('Mobile · iOS · Safari', $this->parser->parse($ua)); + } + + public function testChromeOnAndroid(): void + { + $ua = 'Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36'; + self::assertSame('Mobile · Android · Chrome', $this->parser->parse($ua)); + } + + public function testFirefoxOnLinux(): void + { + $ua = 'Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0'; + self::assertSame('Ordinateur · Linux · Firefox', $this->parser->parse($ua)); + } + + public function testSafariOnMac(): void + { + $ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15'; + self::assertSame('Ordinateur · macOS · Safari', $this->parser->parse($ua)); + } + + public function testIpadIsTablet(): void + { + $ua = 'Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1'; + self::assertSame('Tablette · iOS · Safari', $this->parser->parse($ua)); + } + + public function testUnknownUaFallsBack(): void + { + self::assertSame('Ordinateur · Autre · Autre', $this->parser->parse('SomeRandomBot/1.0')); + } +}