Symfony Request 的 headers 属性是 HeaderBag 实例,需调用 all() 获取小写键名的多值数组;自定义请求头测试时须加 HTTP_ 前缀;无请求上下文时 getCurrentRequest() 可能为 null,需判空;get() 方法大小写不敏感,all() 返回键名全小写。

用 headers->all() 获取完整请求头数组
Symfony 的 Request 对象把所有 HTTP 头封装在 headers 属性里,它不是普通数组,而是 Symfony\Component\HttpFoundation\HeaderBag 实例。直接 print_r($request->headers) 看不到结构,必须调用 all() 方法才能拿到可遍历的关联数组。
返回结果中键名全是小写(如 'user-agent'、'content-type'),值是字符串数组 —— 因为同一个头可能被多次设置(比如多个 Set-Cookie)。常见处理方式:
- 要取单个头的首个值:
$request->headers->get('x-api-key') - 要合并多值头为字符串(如把多个
Accept值拼成逗号分隔):implode(', ', $request->headers->get('accept', [])) - 要保留原始多值结构并逐项处理:
foreach ($request->headers->get('set-cookie', []) as $cookie) { ... }
测试环境里设置请求头必须加 HTTP_ 前缀
在 PHPUnit 或 functional test 中用 $client->request() 模拟请求时,$server 参数里的头名必须带 HTTP_ 前缀,否则 Symfony 不识别。这不是约定,是底层 SAPI 规范决定的。
例如想传 X-Auth-Token: abc123,得这么写:
$server = ['HTTP_X_AUTH_TOKEN' => 'abc123'];
$client->request('GET', '/api/user', [], [], $server);
漏掉 HTTP_、写成 X_AUTH_TOKEN 或 x-auth-token 都会导致 $request->headers->get('x-auth-token') 返回 null。这个前缀规则对所有自定义头生效,但标准头如 Content-Type 也一样 —— 必须写 HTTP_CONTENT_TYPE。
在无请求上下文时调用会抛出 LogicException
如果在命令行、事件监听器或服务初始化阶段直接访问 RequestStack 的当前请求,而此时没有活跃的 HTTP 请求(比如 CLI 脚本或异步任务),$requestStack->getCurrentRequest() 会返回 null。紧接着调用 ->headers->all() 就会触发 Call to a member function all() on null。
安全做法是始终判空:
$request = $requestStack->getCurrentRequest();
if (!$request) {
throw new \LogicException('No active request available');
}
$headers = $request->headers->all();
或者更保守地用 ?? [] 提供默认值,但要注意这会掩盖上下文缺失问题 —— 很多逻辑错误其实源于误以为“有请求”。
get() 和 has() 对大小写不敏感,但键名存储是小写的
$request->headers->get('X-Requested-With') 和 $request->headers->get('x-requested-with') 效果一样,内部做了 strtolower。但如果你用 array_key_exists() 直接查 $request->headers->all() 的结果,就必须用小写键名,否则查不到。
所以别混用两种访问方式。统一走 get() 最省心;非要遍历 all() 结果,就记住键永远是小写,且值是数组 —— 即使头只出现一次,$headers['user-agent'][0] 才是真实值,$headers['user-agent'] 是数组本身。


















