Browsershot 向浏览器实例传递环境变量:setEnvironmentOptions 用法与底层原理
Browsershot 向浏览器实例传递环境变量:setEnvironmentOptions 用法与底层原理
本文围绕 Spatie Browsershot 的 setEnvironmentOptions() 方法展开,讲解如何在调用 Chromium(Chrome)渲染网页、截图或生成 PDF 时,把自定义环境变量(如时区 TZ)注入浏览器进程。读完你将掌握该 API 的标准写法、它与 setNodeEnv() 的本质区别,以及从 PHP 到 Puppeteer 的完整变量传递链路,可用于解决时区错乱、区域设置、证书代理等真实渲染场景。
一、为什么需要向浏览器传递环境变量
Chromium 是独立于 PHP 进程的浏览器实例,它读取的是自身进程的环境变量。当页面脚本依赖某些环境变量来决定渲染结果时(例如 JavaScript 中的 Intl.DateTimeFormat 需要正确的 TZ 时区,或页面需要读取特定代理、语言环境配置),PHP 侧设置 putenv() 并不会影响浏览器。
Browsershot 为此提供了两个层面的环境变量注入入口:
setEnvironmentOptions(array $options):注入到 Chromium 浏览器进程,影响页面渲染本身;setNodeEnv(array $envVars):注入到 Node(Puppeteer)进程,影响桥接脚本运行环境(见 src/Browsershot.php)。
两者互不替代,本文重点讲解前者。
二、核心用法:setEnvironmentOptions 标准示例
官方文档给出的最小示例即是最常见场景——为浏览器设置时区:
use Spatie\Browsershot\Browsershot;
Browsershot::url('https://example.com')
->setEnvironmentOptions(['TZ' => 'Pacific/Auckland'])
->save('example.png');
方法接受一个「键值对数组」,键为环境变量名,值为字符串,调用后返回 self,因此可以继续链式追加其他配置:
Browsershot::url('https://example.com')
->setEnvironmentOptions([
'TZ' => 'Asia/Shanghai',
'LANG' => 'zh_CN.UTF-8',
'HTTP_PROXY' => 'http://127.0.0.1:7890',
])
->windowSize(1920, 1080)
->save('example.pdf');
实现上,该方法等价于调用 setOption('env', $options),把数组原样放入命令的 options.env 字段:
public function setEnvironmentOptions(array $options = []): self
{
return $this->setOption('env', $options);
}
见 src/Browsershot.php。传空数组时等价于不设置该选项。
三、底层链路:环境变量如何从 PHP 到达 Chromium
整条链路分三段,每一段都有明确的源码依据:
1. PHP 侧组装命令:createCommand() 将 url、action、options 组装成数组,env 作为 options 的子键被 JSON 序列化后通过命令行传给 bin/browser.cjs(见 src/Browsershot.php 与 getFullCommand)。
2. 桥接脚本接收并合并:bin/browser.cjs 在调用 puppeteer.launch() 时,把自定义变量与 Node 进程自身环境合并后传给 Chromium:
browser = await puppet.launch({
headless: options.newHeadless ? true : 'shell',
executablePath: options.executablePath,
args: options.args || [],
pipe: options.pipe || false,
env: {
...(options.env || {}),
...process.env
},
...
});
见 bin/browser.cjs。这意味着传入的环境变量并非替换浏览器整个环境,而是与 Node 进程当前环境合并后一并生效——Chromium 仍能拿到 PATH、HOME 等系统变量。
3. Chromium 进程生效:Puppeteer 将合并后的 env 作为 Chromium 子进程的环境变量,页面内运行的 JavaScript(如 Intl、Date)随即感知到 TZ 等变量带来的行为差异。
四、setEnvironmentOptions 与 setNodeEnv 的区别
两者名称相近但作用对象完全不同,容易混淆:
| 方法 | 注入目标 | 底层机制 | 典型用途 |
|---|---|---|---|
setEnvironmentOptions() |
Chromium 浏览器进程 | options.env → puppeteer.launch({ env }) |
时区、语言、页面可见的环境变量 |
setNodeEnv() |
Node(Puppeteer 桥接)进程 | nodeEnvVars → shell 前缀 KEY='value' |
NODE_ENV 等影响 Node 脚本自身的变量 |
setNodeEnv 的实现是把变量拼进命令行前缀(KEY='value' node browser.cjs ...),由 buildEnvVarsCommand() 生成,见 src/Browsershot.php。测试用例 tests/BrowsershotTest.php 通过 setNodeEnv(['NODE_ENV' => 'production']) 生成截图并断言输出文件存在,验证了该路径可用。
五、测试佐证:命令结构可验证
仓库测试 tests/BrowsershotTest.php 精确断言了 setEnvironmentOptions 最终产出的命令结构:
it('will allow passing environment variables', function () {
$instance = Browsershot::url('https://example.com')
->setEnvironmentOptions([
'TZ' => 'Pacific/Auckland',
]);
$this->assertEquals([
'url' => 'https://example.com',
'action' => 'screenshot',
'options' => [
'path' => 'screenshot.png',
'viewport' => ['width' => 800, 'height' => 600],
'args' => [],
'type' => 'png',
'env' => ['TZ' => 'Pacific/Auckland'],
],
], $instance->createScreenshotCommand('screenshot.png'));
});
测试确认了三点事实:env 键位于 options 下;setEnvironmentOptions 不影响 args(Chromium 启动参数,由 --no-sandbox、--proxy-server 等另行生成);该 API 对截图、PDF、HTML 等所有 action 均通用。
六、使用注意事项
- 生效时机:环境变量只在浏览器启动时注入,若在链式调用后手动
setOption('env', ...)覆盖同名键,以最后一次赋值为准; - 合并规则:自定义变量与 Node 进程环境合并,因此不要用
setEnvironmentOptions覆盖PATH等关键系统变量,以免破坏浏览器启动; - 敏感信息:环境变量会随命令 JSON 传递,避免放入密钥等敏感内容;若需减少命令行长度,可参考 写入选项文件 的
-f方式; - 时区场景:
TZ是浏览器进程读取时区最直接的通道,使用 IANA 时区名(如Pacific/Auckland、Asia/Shanghai)而非缩写,避免夏令时误判; - Windows 差异:在 Windows 上环境变量合并同样生效,但 Node 进程自身的
setNodeEnv路径在 getFullCommand 中走数组参数交由 Symfony Process 处理,行为与类 Unix 略有差异。
七、延伸阅读
- 设置用户代理:影响页面识别,与
TZ同为渲染级配置; - 指定代理服务器:若需代理,优先使用
--proxy-server参数而非环境变量HTTP_PROXY; - 更改浏览器语言:与
LANG环境变量互补的另一种区域化手段; - 核心实现:src/Browsershot.php、桥接脚本 bin/browser.cjs、测试 tests/BrowsershotTest.php。