先说结论:中文参数直接拼进URL,十有八九会乱码,严重的直接报错。但更尴尬的是,函数用对了,编码也编了,一解码还是满屏的锟斤拷——问题出在哪?

中文参数直接拼进URL会出错,必须编码;但用错函数或漏设UTF-8,解码后照样是乱码。
urlencode() 和 rawurlencode() 选哪个?
两个函数都能处理中文,但区别主要在空格上:urlencode() 把空格转成 +,rawurlencode() 转成 %20。好消息是,浏览器和PHP的 $_GET 都能正确识别 + 为空格,所以日常构造查询参数,用 urlencode() 更省心。
但如果要拼URL路径,比如 /api/v1/users/张三,就得换 rawurlencode() 上场——它不碰 /、? 这类保留字符,而 urlencode() 会把路径里的斜杠也一并处理掉,导致路由解析失败。
- GET 参数值(
?q=搜索关键词)→ 用urlencode() - URL 路径段(
/user/姓名)→ 用rawurlencode() - 调用第三方 REST API 时,看文档要求:要
%20就用rawurlencode(),要兼容表单提交就用urlencode()
为什么 urldecode() 有时解不出中文?
问题不在函数本身,而是整个编码链路没对齐。PHP 的 urldecode() 只负责把 %E4%BD%A0 这种字节还原出来,至于这串字节该按什么编码显示,它不管。如果页面响应头没设 Content-Type: text/html; charset=utf-8,或者 HTML 里缺 ,浏览器就会用默认编码(比如GBK)去解读UTF-8字节,结果就是乱码。
- 确保 PHP 文件本身是 UTF-8 无 BOM 格式保存
- 脚本开头加
header('Content-Type: text/html; charset=utf-8'); - HTML 中写
- 数据库连接也要设 UTF-8(如
$mysqli->set_charset('utf8mb4'))
多个参数一起拼,别手写 urlencode()
手动拼 "?name=" . urlencode($name) . "&city=" . urlencode($city) 容易漏、难维护,还容易忘掉编码。直接用 http_build_query() 更省心:
$params = ['name' => '李四', 'city' => '上海', 'tag' => ['php', 'web']]; $url = 'https://example.com/search?' . http_build_query($params);
它自动对每个值调用 urlencode(),支持嵌套数组,生成的格式标准(tag[0]=php&tag[1]=web)。注意:键名不会被编码,所以自己要保证键名是合法 ASCII 字符。
接收端不用手动 urldecode()
PHP 在收到 GET 请求时,已经把 $_GET 里的所有值用 urldecode() 处理过了。直接取 $_GET['name'] 就是原始中文字符串,再调一次 urldecode() 反而会出错(比如把 % 当作编码前缀二次解码)。
唯一要手动解码的场景是:从请求体、HTTP 头、或自定义字段里拿到的是已编码的字符串(比如某个 API 返回的 "encoded_name": "%E5%BC%A0%E4%B8%89"),这时才需要 urldecode() 或 rawurldecode()。
最常被忽略的一点:编码函数只管字节转换,不管语义。如果原始字符串里混了不同编码(比如GBK和UTF-8拼在一起),urlencode() 照样编码,但解出来还是乱码——得先用 mb_convert_encoding() 统一转成 UTF-8 再处理。