$_ENV — 环境变量
概述
$_ENV 是 PHP 中的超全局变量,用于访问当前进程的环境变量。环境变量是操作系统向运行中的程序传递配置信息的一种机制,常用于存储数据库连接信息、API 密钥、应用密钥等敏感配置。在现代 PHP 开发中,$_ENV 配合 .env 文件已经成为管理应用配置的标准做法。
前置知识
在阅读本节之前,你需要了解:
- 操作系统环境变量的概念
- PHP 配置文件
php.ini中variables_order指令 .env文件在项目配置中的作用- Composer 和 dotenv 库的基本使用
基础概念
什么是环境变量
环境变量是操作系统维护的键值对,所有运行在该系统上的进程都可以访问。在 PHP 中,环境变量可以来自以下渠道:
| 来源 | 示例 | 说明 |
|---|---|---|
| 操作系统 | PATH、HOME、USER | 系统级环境变量 |
| Web 服务器 | DB_HOST、APP_ENV | Apache/Nginx 配置中设置 |
| Shell 脚本 | export DB_PASSWORD=secret | 启动脚本中设置 |
.env 文件 | APP_KEY=base64:xxx | 项目根目录的配置文件 |
| Docker | -e MYSQL_ROOT_PASSWORD=x | 容器启动参数 |
$_ENV 与 variables_order
$_ENV 是否可用取决于 php.ini 中的 variables_order 配置:
ini
; 默认值(注意:不包含 E)
variables_order = "GPCS"
; G = GET, P = POST, C = Cookie, S = Server, E = Environment
; 要让 $_ENV 可用,必须包含 E
variables_order = "EGPCS"$_ENV 默认可能为空
在很多 PHP 安装中,variables_order 默认值为 "GPCS",不包含 E。这意味着 $_ENV 将为空数组。如果需要使用 $_ENV,需要修改 variables_order 配置,或使用 getenv() 函数(它不受此限制)。
语法与代码
基本使用
php
<?php
declare(strict_types=1);
// 检查 $_ENV 是否可用
if (empty($_ENV)) {
echo '$_ENV 为空,请检查 variables_order 配置' . PHP_EOL;
echo '当前 variables_order: ' . ini_get('variables_order') . PHP_EOL;
}
// 读取环境变量
$home = $_ENV['HOME'] ?? '/tmp';
$path = $_ENV['PATH'] ?? '';
$appEnv = $_ENV['APP_ENV'] ?? 'production';
echo "HOME: {$home}";
echo "APP_ENV: {$appEnv}";使用 getenv() 替代 $_ENV
php
<?php
declare(strict_types=1);
/**
* getenv() 不受 variables_order 限制
* 是获取环境变量的推荐方式
*/
// getenv() 使用方式
$dbHost = getenv('DB_HOST');
$dbName = getenv('DB_NAME');
$dbUser = getenv('DB_USER');
$dbPass = getenv('DB_PASSWORD');
// getenv() 默认在变量不存在时返回 false
// 第二个参数 true(PHP 7.1+)使其在变量不存在时返回空字符串
$dbHost = getenv('DB_HOST', true); // 不存在时返回 '' 而非 false
// 安全的环境变量获取函数
function env(string $key, mixed $default = null): mixed
{
$value = getenv($key);
if ($value === false) {
return $default;
}
// 自动转换布尔值
return match (strtolower($value)) {
'true', '(true)' => true,
'false', '(false)' => false,
'null', '(null)' => null,
'empty', '(empty)' => '',
default => $value,
};
}
// 使用示例
$debug = env('APP_DEBUG', false);
$env = env('APP_ENV', 'production');
$dbPort = env('DB_PORT', 3306);
$cacheEnabled = env('CACHE_ENABLED', true);
var_dump($debug); // bool(true)
var_dump($env); // string(10) "development"
var_dump($cacheEnabled); // bool(true)手动解析 .env 文件
php
<?php
declare(strict_types=1);
/**
* 简单的 .env 文件解析器
* (生产环境推荐使用 vlucas/phpdotenv 库)
*/
class DotEnv
{
public static function load(string $path): void
{
if (!file_exists($path) || !is_readable($path)) {
return;
}
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines === false) {
return;
}
foreach ($lines as $line) {
// 跳过注释
if (str_starts_with(trim($line), '#')) {
continue;
}
// 跳过无等号的行
if (!str_contains($line, '=')) {
continue;
}
// 解析键值对
[$name, $value] = explode('=', $line, 2);
$name = trim($name);
$value = trim($value);
// 移除引号
$value = trim($value, '"\'');
// 设置为环境变量
putenv("{$name}={$value}");
$_ENV[$name] = $value;
}
}
}
// 使用示例
DotEnv::load(__DIR__ . '/.env');
// 现在可以通过 getenv() 和 $_ENV 访问
$dbHost = getenv('DB_HOST', true);
echo "数据库主机: {$dbHost}";环境变量配置管理类
php
<?php
declare(strict_types=1);
/**
* 环境配置管理类
* 支持类型转换和默认值
*/
class EnvConfig
{
private static ?array $cache = null;
/**
* 获取字符串环境变量
*/
public static function string(string $key, string $default = ''): string
{
return (string)self::get($key, $default);
}
/**
* 获取整数环境变量
*/
public static function int(string $key, int $default = 0): int
{
$value = self::get($key, (string)$default);
return is_numeric($value) ? (int)$value : $default;
}
/**
* 获取布尔环境变量
*/
public static function bool(string $key, bool $default = false): bool
{
$value = self::get($key, '');
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
/**
* 获取浮点数环境变量
*/
public static function float(string $key, float $default = 0.0): float
{
$value = self::get($key, (string)$default);
return is_numeric($value) ? (float)$value : $default;
}
/**
* 获取数组环境变量(逗号分隔)
*/
public static function array(string $key, array $default = []): array
{
$value = self::get($key, '');
if ($value === '') {
return $default;
}
return array_map('trim', explode(',', $value));
}
/**
* 检查环境变量是否存在
*/
public static function has(string $key): bool
{
return getenv($key) !== false;
}
private static function get(string $key, string $default): string
{
if (self::$cache === null) {
self::$cache = [];
}
if (array_key_exists($key, self::$cache)) {
return self::$cache[$key];
}
$value = getenv($key, true);
if ($value === false || $value === '') {
return $default;
}
self::$cache[$key] = $value;
return $value;
}
}
// 使用示例
$dbHost = EnvConfig::string('DB_HOST', 'localhost');
$dbPort = EnvConfig::int('DB_PORT', 3306);
$dbName = EnvConfig::string('DB_NAME', 'myapp');
$debug = EnvConfig::bool('APP_DEBUG', false);
$allowedOrigins = EnvConfig::array('CORS_ALLOWED_ORIGINS', ['http://localhost']);详细说明
.env 文件格式
ini
# 应用配置
APP_NAME=MyApp
APP_ENV=development
APP_DEBUG=true
APP_KEY=base64:random_string_here
# 数据库配置
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=secret
# 缓存配置
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
# 邮件配置
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=user@gmail.com
MAIL_PASSWORD=app_password
# 多值(逗号分隔)
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080
# 带引号的值(可选)
SITE_NAME="My Awesome Site"
LOGO_PATH='/assets/logo.png'$_ENV vs getenv() 对比
| 特性 | $_ENV | getenv() |
|---|---|---|
variables_order 限制 | 受限(需包含 E) | 不受限 |
| 默认返回值 | 不存在时触发警告 | false(PHP 7.1+ 可返回 '') |
| 性能 | 直接数组访问,更快 | 函数调用,略慢 |
| 可写性 | 可直接赋值 | 需要 putenv() |
| 推荐程度 | 需配合配置修改 | 推荐 |
推荐 getenv()
在大多数情况下,推荐使用 getenv() 而非 $_ENV,因为 getenv() 不受 variables_order 配置的限制,更加可靠。
在不同环境中的环境变量设置
Apache 虚拟主机配置:
apache
<VirtualHost *:80>
ServerName example.com
SetEnv APP_ENV production
SetEnv DB_HOST 192.168.1.100
</VirtualHost>Nginx + PHP-FPM 配置:
nginx
server {
listen 80;
server_name example.com;
location ~ \.php$ {
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param APP_ENV production;
fastcgi_param DB_HOST 192.168.1.100;
include fastcgi_params;
}
}Docker Compose 配置:
yaml
services:
app:
image: php:8.2-fpm
environment:
- APP_ENV=production
- DB_HOST=mysql
- DB_PASSWORD=secret
env_file:
- .env.production系统环境变量(Linux):
bash
# 临时设置(仅当前 Shell)
export DB_HOST=localhost
# 持久设置(写入配置文件)
echo 'export DB_HOST=localhost' >> ~/.bashrc
# systemd 服务中设置
# /etc/systemd/system/php-app.service
[Service]
Environment="APP_ENV=production"
EnvironmentFile=/etc/php-app/env注意事项
1. 不要将 .env 文件提交到版本控制
gitignore
# .gitignore
.env
.env.local
.env.*.local2. .env 文件中的敏感信息
.env 文件通常包含数据库密码、API 密钥等敏感信息,需要确保:
- 不提交到 Git
- 服务器文件权限设置为仅应用进程可读(
chmod 600 .env) - 日志中不输出环境变量值
3. 进程间环境变量隔离
PHP-FPM 的不同进程池有独立的环境变量。修改 .env 文件后,需要重启 PHP-FPM 才能生效(如果使用 putenv() 加载的话)。
4. 环境变量的大小限制
不同操作系统对环境变量有大小限制:
- Linux:单个变量约 128KB,总量约 32MB
- Windows:单个变量约 32KB
最佳实践
- 使用 getenv():不受
variables_order限制 - 使用 dotenv 库:生产环境推荐使用
vlucas/phpdotenv - 环境变量命名规范:使用大写字母和下划线(如
DB_HOST) - 类型转换:对环境变量值进行适当的类型转换
- .env 文件模板:提供
.env.example作为模板,不包含真实密钥 - 不同环境分离:开发、测试、生产使用不同的
.env文件 - 永远不输出密钥:日志和错误信息中不要输出环境变量的值
php
<?php
declare(strict_types=1);
// .env.example(提交到版本控制)
// APP_NAME=MyApp
// APP_ENV=development
// APP_DEBUG=true
// DB_HOST=127.0.0.1
// DB_PASSWORD=your_password_here
// .env(不提交,包含真实值)
// APP_NAME=MyApp
// APP_ENV=production
// APP_DEBUG=false
// DB_HOST=192.168.1.100
// DB_PASSWORD=actual_secret_password下一节
下一节将详细介绍 $_COOKIE 超全局变量,了解 Cookie 的工作原理、作用域和有效期管理。