4、环境一致性:开发/测试/生产环境差异、环境变量管理、.env文件最佳实践

环境不一致,是PHP项目部署中最常见的坑。

我见过太多团队,开发环境跑得好好的,一上测试就报错。到了生产环境,直接崩了。说白了,就是环境差异在作祟。今天咱们就聊聊怎么搞定这件事。

4.1 环境差异到底差在哪?

先说说三个环境的核心差异。我个人习惯把它们分成三类:

差异类型 开发环境 测试环境 生产环境
数据库配置 本地MySQL,root无密码 独立数据库,有测试账号 主从集群,只读/写入分离
缓存服务 Redis单机,无密码 Redis单机,有密码 Redis哨兵集群
调试模式 开启,显示错误详情 开启,但记录日志 关闭,只记录错误日志
第三方API 沙箱环境 沙箱环境 正式环境

你想想看,如果把这些配置硬编码在代码里,每次部署都要手动改一遍。改漏一个,就是事故。

⚠️ 我曾经踩过的坑: 有一次上线前,同事忘了把调试模式关掉。结果生产环境直接暴露了数据库密码。嗯,从那以后,我再也不敢把配置写死在代码里了。

4.2 环境变量管理:从硬编码到动态配置

解决环境差异的核心思路,就是「把配置从代码中抽离出来」。环境变量就是最好的方案。

PHP里获取环境变量很简单:

<?php
// 获取环境变量
$dbHost = getenv('DB_HOST');
$dbName = getenv('DB_NAME');
$dbUser = getenv('DB_USER');
$dbPass = getenv('DB_PASS');

// 设置默认值
$dbHost = getenv('DB_HOST') ?: 'localhost';
?>

但这里有个问题:开发环境怎么设置这些变量?

我建议用三种方式配合:

  1. 系统环境变量:生产环境用,通过运维工具统一管理
  2. PHP-FPM 配置:在 pool.d/www.conf 里设置 env[KEY] = value
  3. .env 文件:开发环境用,方便本地调试

4.3 .env 文件最佳实践

.env 文件说白了就是个「本地配置管家」。我见过很多团队用不好它,这里分享几个经验。

4.3.1 文件结构规范

# .env.example - 提交到版本库的模板
APP_ENV=development
APP_DEBUG=true
APP_URL=http://localhost:8080

DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=

REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=

# .env - 实际使用的文件,不提交到版本库
APP_ENV=development
APP_DEBUG=true
APP_URL=http://localhost:8080

DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=myapp_dev
DB_USERNAME=dev_user
DB_PASSWORD=dev_pass_123
💡 核心原则:
  • .env.example 提交到 Git,作为配置模板
  • .env 加入 .gitignore,永远不提交
  • 生产环境用系统环境变量,不用 .env 文件

4.3.2 加载 .env 文件的正确姿势

我个人习惯用 vlucas/phpdotenv 这个库。但要注意加载顺序:

<?php
// bootstrap.php - 应用入口
require_once __DIR__ . '/vendor/autoload.php';

use Dotenv\Dotenv;

// 1. 先加载 .env 文件(开发环境)
if (file_exists(__DIR__ . '/.env')) {
    $dotenv = Dotenv::createImmutable(__DIR__);
    $dotenv->load();
}

// 2. 系统环境变量优先级更高
// 如果系统已经设置了 DB_HOST,就用系统的
$dbHost = getenv('DB_HOST') ?: $_ENV['DB_HOST'] ?? 'localhost';

// 3. 验证必要变量是否存在
$dotenv->required(['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS']);
?>
📌 避坑指南: 我曾经遇到过 .env 文件被意外提交到 Git 仓库的情况。建议在 CI 流程里加一步检查:如果检测到 .env 文件被提交,直接阻断构建。

4.4 环境检测与自动适配

有时候我们需要在代码里判断当前环境。我建议用统一的环境变量来标识:

<?php
// config/app.php
return [
    'env' => getenv('APP_ENV') ?: 'production',
    
    'debug' => filter_var(
        getenv('APP_DEBUG'), 
        FILTER_VALIDATE_BOOLEAN
    ),
    
    'database' => [
        'connections' => [
            'mysql' => [
                'driver' => 'mysql',
                'host' => getenv('DB_HOST'),
                'database' => getenv('DB_DATABASE'),
                'username' => getenv('DB_USERNAME'),
                'password' => getenv('DB_PASSWORD'),
                'charset' => 'utf8mb4',
                // 生产环境用持久连接
                'persistent' => getenv('APP_ENV') === 'production',
            ],
        ],
    ],
    
    // 根据环境加载不同配置
    'cache' => [
        'default' => getenv('APP_ENV') === 'production' 
            ? 'redis' 
            : 'file',
    ],
];
?>

4.5 部署时的环境变量注入

生产环境我建议用 CI/CD 工具来注入环境变量。以 GitHub Actions 为例:

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Deploy to Production
        env:
          APP_ENV: production
          APP_DEBUG: false
          DB_HOST: ${{ secrets.DB_HOST }}
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
          REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
        run: |
          # 部署脚本
          rsync -avz --delete ./ user@server:/var/www/app
          ssh user@server "cd /var/www/app && \
            echo 'APP_ENV=$APP_ENV' > .env && \
            echo 'DB_HOST=$DB_HOST' >> .env && \
            composer install --no-dev --optimize-autoloader"
⚠️ 安全提醒: 敏感信息(数据库密码、API密钥等)一定要用 CI/CD 的 Secrets 功能,不要明文写在配置文件里。我见过有人把生产数据库密码写在 .env.example 里提交到 GitHub,结果被爬虫抓取,数据库被删了个精光。

4.6 环境一致性检查清单

最后,分享一个我每次部署前都会检查的清单:

  • PHP 版本:开发机和服务器版本是否一致?
  • 扩展列表:生产环境有没有漏装某个扩展?
  • 时区设置:date.timezone 是否统一?
  • 内存限制:memory_limit 是否足够?
  • 文件上传:upload_max_filesize 和 post_max_size 是否匹配?
  • 错误报告:生产环境是否关闭了 display_errors?
  • OPcache:生产环境是否开启了 OPcache?

嗯,环境一致性说白了就一句话:配置不写死,环境不混用,变量统一管。做到这三点,你的部署流程至少能减少一半的故障。

下一章咱们聊聊自动化部署流水线的搭建,到时候会用到今天讲的环境变量管理技巧。