getenv() 更可靠,$_env 默认为空——除非显式配置 variables_order 含 e;.env 文件需用 vlucas/phpdotenv 注入环境变量,且必须在入口文件最顶部加载。

PHP 读取 .env 文件要用 getenv() 还是 $_ENV?
直接说结论:getenv() 更可靠,$_ENV 默认为空——除非你显式开启 variables_order 配置。PHP 启动时不会自动把系统环境变量塞进 $_ENV,这是很多人的认知盲区。
常见错误现象:在命令行里 export APP_ENV=prod,然后 PHP 脚本里 var_dump($_ENV['APP_ENV']) 报 Undefined index;但换成 getenv('APP_ENV') 就能拿到。
-
getenv()直接读操作系统层的环境变量,不依赖 PHP 配置 -
$_ENV是一个超全局数组,是否填充取决于php.ini中variables_order = "EGPCS"是否含E(Environment),默认值通常是"GPCS"(不含E) - 线上部署时,Web 服务器(如 Nginx + PHP-FPM)往往不继承 shell 的
export变量,得靠env指令或fastcgi_param显式透传
用 vlucas/phpdotenv 加载 .env 文件要注意什么?
它不是“读取”而是“注入”:把 .env 里的键值对写进 PHP 的环境变量空间(即让后续 getenv() 能拿到),不是覆盖 $_ENV,也不修改 $_SERVER。
使用场景:本地开发、CI/CD 构建阶段、Docker 容器启动前——不适合直接用于生产 Web 请求入口(有性能和安全顾虑)。
立即学习“PHP免费学习笔记(深入)”;
- 必须在所有业务代码之前加载,通常放在
index.php或框架入口最顶部 - 不要提交
.env到 Git,加到.gitignore;生产环境应改用系统级环境变量,而非文件 - 如果
.env里有空格或特殊字符(如DB_PASSWORD="p@ss word!"),记得用双引号包裹,否则phpdotenv解析会截断 - 版本差异:v5+ 默认不递归加载
.env.local等扩展文件,要手动调用load()或启用setImmutable(false)
PHP-FPM 下设置环境变量为什么 export 不生效?
因为 PHP-FPM 是守护进程,启动后就脱离 shell 环境,export 只影响当前 shell 及其子进程,对已运行的 FPM worker 无效。
正确做法是通过 PHP-FPM 配置注入,而不是靠 shell 命令。
- 在
www.conf(或对应 pool 配置)里加:env[APP_ENV] = production,重启 PHP-FPM 才生效 - Nginx 配置中用
fastcgi_param APP_ENV production;也可以,但只对当前 location 生效,且不能覆盖 FPM 层已设的同名变量(FPM 的env[]优先级更高) - Docker 用户注意:
docker run -e APP_ENV=staging会传给容器环境,但 PHP-FPM 默认不自动导入,仍需在www.conf中用env[]显式声明 - 验证是否生效:写个
info.php,里面var_dump(getenv('APP_ENV'));,别信phpinfo()里 Environment 表格——它只显示启动时的快照,不一定反映运行时真实值
敏感配置(如数据库密码)能不能写进 $_SERVER?
能,但不推荐。虽然 $_SERVER 在 Web 请求中可被 getenv() 读取(PHP 会把环境变量复制一份进 $_SERVER),但它本质是请求上下文的一部分,可能被日志、调试工具、异常堆栈意外泄露。
更安全的做法是:只用 getenv() 读取,避免赋值给任何用户可控或可打印的变量;同时确保 display_errors=Off、log_errors=On,防止密码出现在 HTML 错误页里。
- 不要写
$_SERVER['DB_PASSWORD'] = getenv('DB_PASSWORD');—— 这等于主动把它塞进易泄露的全局数组 - 不要在
error_log()或var_dump()中直接打印getenv('DB_PASSWORD'),哪怕只是调试 - 如果用 Laravel/Symfony,它们的
config/database.php是闭包函数,变量作用域隔离更好,比裸写getenv()更稳妥 - 云平台(如 AWS Elastic Beanstalk、Heroku)会把环境变量注入为系统级变量,此时
getenv()最简最稳,无需额外适配
getenv(),设的时候绕过 shell 直接怼到服务配置里。其它全是围绕这两条打转。











