一、为什么需要持续集成

想象一下,你正在开发一个PHP项目,每次修改代码后都要手动运行测试、检查代码风格、打包部署……这些重复劳动不仅浪费时间,还容易出错。持续集成(CI)就是来解决这个问题的——它像是个勤劳的助手,每次代码变动时自动帮你完成这些琐事。

举个例子:

// 技术栈:PHP + GitLab CI  
// 一个简单的PHP单元测试示例  
class Calculator {
    public function add(int $a, int $b): int {
        return $a + $b;
    }
}

// 对应的测试用例  
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase {
    public function testAdd() {
        $calc = new Calculator();
        $this->assertEquals(5, $calc->add(2, 3)); // 断言2+3=5
    }
}

如果没有CI,每次修改add()方法后,你都得手动跑测试。而有了CI,提交代码时这些测试会自动运行。

二、搭建基础自动化流程

1. 选择CI工具

Jenkins、GitLab CI、GitHub Actions都不错。这里以GitLab CI为例,因为它和代码仓库天然集成。

2. 编写配置文件

在项目根目录创建.gitlab-ci.yml

# 技术栈:GitLab CI  
stages:
  - test
  - deploy

unit_test:
  stage: test
  image: php:8.1  # 使用PHP 8.1的Docker镜像
  script:
    - apt-get update && apt-get install -y git unzip
    - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
    - composer install
    - ./vendor/bin/phpunit tests/  # 运行所有测试

deploy_prod:
  stage: deploy
  only:
    - main  # 仅main分支触发部署
  script:
    - rsync -avz ./ user@server:/var/www/html/  # 简化版部署脚本

3. 关键点解析

  • stages定义了流水线的阶段顺序
  • image指定运行环境,避免"在我机器上是好的"问题
  • only限制特定分支触发任务

三、进阶实践技巧

1. 并行化加速

拆分测试任务可以大幅缩短等待时间:

# 并行运行不同测试组  
test_unit:
  script: ./vendor/bin/phpunit tests/Unit/
  
test_integration:
  script: ./vendor/bin/phpunit tests/Integration/

2. 代码质量检查

加入PHPStan静态分析:

code_quality:
  script:
    - composer require --dev phpstan/phpstan
    - vendor/bin/phpstan analyse src --level=5

3. 环境变量管理

敏感信息如数据库密码应通过CI变量注入:

// 代码中通过getenv()读取  
$dbPassword = getenv('DB_PASSWORD');

然后在GitLab的Settings > CI/CD > Variables中设置变量。

四、避坑指南

1. 缓存依赖

每次重新安装composer依赖太耗时,可以缓存vendor目录:

cache:
  paths:
    - vendor/

2. 本地复现问题

如果CI失败但本地成功,可以用相同环境调试:

docker run --rm -v $(pwd):/app -w /app php:8.1 ./vendor/bin/phpunit

3. 通知机制

添加失败提醒到Slack:

after_script:
  - |
    if [ "$CI_JOB_STATUS" == "failed" ]; then
      curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"Build $CI_PIPELINE_ID 失败了!\"}" $SLACK_WEBHOOK
    fi

五、完整案例演示

假设我们有个小型API项目,目录结构如下:

.
├── src/
│   └── UserController.php
├── tests/
│   └── UserControllerTest.php
├── composer.json
└── .gitlab-ci.yml

UserController.php内容:

class UserController {
    public function getUser(int $id): array {
        if ($id <= 0) {
            throw new InvalidArgumentException("ID必须大于0");
        }
        return ['id' => $id, 'name' => '测试用户'];
    }
}

对应的CI配置:

stages:
  - test
  - deploy

phpunit:
  stage: test
  image: php:8.1
  services:
    - mysql:5.7
  variables:
    MYSQL_ROOT_PASSWORD: root
    MYSQL_DATABASE: test_db
  script:
    - apt-get update && apt-get install -y libzip-dev mysql-client
    - docker-php-ext-install pdo_mysql zip
    - composer install
    - ./vendor/bin/phpunit --coverage-text --colors=never

deploy:
  stage: deploy
  environment: production
  script:
    - scp -r src/ user@server:/var/www/api/
  only:
    - main

六、技术选型对比

工具 优点 缺点
Jenkins 插件生态丰富 配置复杂
GitLab CI 与GitLab深度集成 免费版并发有限
GitHub Actions 原生支持GitHub仓库 对私有仓库有限制

七、应用场景分析

适合场景

  • 团队协作开发
  • 需要频繁发布的项目
  • 测试覆盖率要求高的项目

不适合场景

  • 个人一次性脚本
  • 没有测试用例的项目(CI效果大打折扣)

八、注意事项

  1. 测试隔离性:每个测试用例应该独立运行,不能依赖执行顺序
  2. 流水线时长:超过30分钟的流水线会降低开发效率
  3. 资源成本:并行任务会消耗更多CI资源
  4. 失败处理:建议设置"允许失败"的任务标记非关键检查

九、总结

通过自动化构建和测试,PHP项目可以获得:

  • 更快的错误反馈(不用等同事发现你的代码报错)
  • 更高的代码质量(每次提交都经过严格检查)
  • 更轻松的部署过程(点个按钮就能上线)

就像给项目请了个24小时值班的质检员,虽然初期搭建需要投入时间,但长期来看绝对是笔划算的投资。