PHP解析User Agent异常:设备为空时未输出预期提示
问题描述
我正在开发一个简易User Agent解析器,但当使用以下两个User Agent字符串时:
Mozilla/5.0 (Linux; Android 9 ; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36
或
Mozilla/5.0 (Linux; Android 9 ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36
浏览器始终显示$OS和$osArr[1]的值,而非预期的“Device unknown”提示。以下是我的PHP代码:
<?php $user_agent = "Mozilla/5.0 (Linux; Android 9 ; SM-577) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36"; $osStart = strpos($user_agent, "(") + 1; $osEnd = strpos($user_agent, ";", $osStart); $osInfo = substr($user_agent, $osStart, $osEnd - $osStart); $osArr = explode("; ", $osInfo); $os = $osArr[0]; // 提取设备信息 $deviceStart = strpos($user_agent, ";", $osEnd) + 2; $deviceEnd = strpos($user_agent, ")", $deviceStart); $device = substr($user_agent, $deviceStart, $deviceEnd - $deviceStart); // 在浏览器显示信息 if (empty(trim($device))) { echo "DEVICE UNKNOWN."; } else { echo $os . "<br>"; echo $osArr[1] . "<br>"; echo $device; } ?>
请问我的代码哪里出错了?
错误分析与修正
核心问题
你的代码在提取设备信息时,完全依赖固定格式的分隔符,没有处理格式异常的情况,导致逻辑失效:
strpos返回false时的逻辑崩溃:比如第二个测试用的User Agent里,$osEnd之后没有下一个;,strpos($user_agent, ";", $osEnd)会返回false,此时$deviceStart = false + 2 = 2,后续strpos找到的是最外层的),最终$device会截取到一大段无效内容,trim后不为空,自然不会触发“DEVICE UNKNOWN”。硬编码的分隔符匹配不兼容异常格式:你假设括号内的内容一定是
Linux; Android 9 ; SM-577这种三段式结构,但测试用的字符串要么少了设备字段,要么多了多余的分号,手动查找位置的方式根本适配不了这些情况。
修正方案
改用正则表达式提取括号内的内容,再拆分处理,能兼容绝大多数格式异常:
<?php $user_agent = "Mozilla/5.0 (Linux; Android 9 ; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36"; // 提取括号内的系统与设备信息 if (preg_match('/\((.*?)\)/', $user_agent, $matches)) { $innerContent = $matches[1]; // 按分号拆分,同时自动去除每个字段的首尾空格 $fields = array_map('trim', explode(';', $innerContent)); $os = $fields[0] ?? '未知操作系统'; $osVersion = $fields[1] ?? '未知版本'; $device = $fields[2] ?? ''; if (empty(trim($device))) { echo "DEVICE UNKNOWN."; } else { echo $os . "<br>"; echo $osVersion . "<br>"; echo $device; } } else { echo "无法解析User Agent"; } ?>
修正说明
- 用
preg_match精准捕获括号内的所有内容,避免手动计算位置的误差; array_map('trim', ...)会自动处理每个字段的前后空格,不管是Android 9 ;还是Android 9都能正常拆分;- 使用
??运算符处理数组下标不存在的情况,防止出现“Undefined array key”错误; - 设备字段直接取拆分后的第三个元素,不存在则为空,此时
empty(trim($device))会触发预期的提示。
内容的提问来源于stack exchange,提问作者MICHAEL

