强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第 26 章 — CI/CD

第 26 章 — CI/CD:GitHub Actions、PHPStan 与 CS Fixer

26.1 GitHub Actions

# .github/workflows/ci.yml
name: PHP CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        php-version: ['8.3', '8.4']

    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: testing
        ports:
          - 3306:3306
        options: >-
          --health-cmd "mysqladmin ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-version }}
          extensions: mbstring, xml, ctype, json, bcmath, pdo_mysql, redis
          coverage: xdebug

      - name: Cache Composer
        uses: actions/cache@v4
        with:
          path: vendor
          key: composer-${{ hashFiles('composer.lock') }}

      - name: Install Dependencies
        run: composer install --prefer-dist --no-interaction

      - name: Check Code Style
        run: vendor/bin/php-cs-fixer fix --dry-run --diff

      - name: Static Analysis
        run: vendor/bin/phpstan analyse --level=9

      - name: Run Tests
        run: vendor/bin/phpunit --coverage-clover=coverage.xml
        env:
          DB_HOST: 127.0.0.1
          DB_PORT: 3306
          DB_DATABASE: testing
          DB_USERNAME: root
          DB_PASSWORD: root

      - name: Upload Coverage
        if: matrix.php-version == '8.3'
        uses: codecov/codecov-action@v4
        with:
          file: coverage.xml

26.2 PHPStan 静态分析

composer require --dev phpstan/phpstan
# phpstan.neon
includes:
    - vendor/larastan/larastan/extension.neon  # Laravel 专用

parameters:
    paths:
        - src
    level: 9
    phpVersion: 80300
    ignoreErrors:
        - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder::#'
vendor/bin/phpstan analyse src --level=9

PHPStan 分析等级

等级说明
0基础检查
1方法调用检查
2方法返回类型
3参数类型
4基本类型安全
5可空类型
6严格类型
7任意类型检查
8数组形状
9最严格检查

26.3 PHP-CS-Fixer

composer require --dev friendsofphp/php-cs-fixer
<?php
// .php-cs-fixer.dist.php
$finder = PhpCsFixer\Finder::create()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/tests');

return (new PhpCsFixer\Config())
    ->setRules([
        '@PER-CS' => true,
        'array_syntax' => ['syntax' => 'short'],
        'no_unused_imports' => true,
        'ordered_imports' => ['sort_algorithm' => 'alpha'],
        'single_quote' => true,
        'trailing_comma_in_multiline' => true,
        'declare_strict_types' => true,
        'void_return' => true,
    ])
    ->setFinder($finder)
    ->setRiskyAllowed(true);
# 检查(不修改)
vendor/bin/php-cs-fixer fix --dry-run --diff

# 自动修复
vendor/bin/php-cs-fixer fix

26.4 Rector(自动重构)

composer require --dev rector/rector
<?php
// rector.php
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/src'])
    ->withSets([
        SetList::PHP_83,
        SetList::CODE_QUALITY,
        SetList::DEAD_CODE,
    ]);
vendor/bin/rector process --dry-run
vendor/bin/rector process

26.5 Pest 测试框架

composer require --dev pestphp/pest
vendor/bin/pest --init
<?php
// tests/Feature/UserTest.php
use function Pest\Laravel\{get, post};

it('returns users list', function () {
    User::factory()->count(10)->create();

    get('/api/users')
        ->assertOk()
        ->assertJsonCount(10, 'data');
});

it('creates a user', function () {
    post('/api/users', ['name' => 'Alice', 'email' => '[email protected]'])
        ->assertCreated()
        ->assertJsonFragment(['name' => 'Alice']);
});

test('email must be valid', function () {
    post('/api/users', ['name' => 'Alice', 'email' => 'invalid'])
        ->assertUnprocessable()
        ->assertJsonValidationErrors(['email']);
});

26.6 完整 CI 流程

# 完整 CI pipeline
name: Full CI Pipeline

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with: { php-version: '8.3' }
      - run: composer install --prefer-dist
      - run: vendor/bin/php-cs-fixer fix --dry-run --diff

  analyse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with: { php-version: '8.3' }
      - run: composer install --prefer-dist
      - run: vendor/bin/phpstan analyse --level=9

  test:
    runs-on: ubuntu-latest
    needs: [lint, analyse]
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with: { php-version: '8.3', coverage: xdebug }
      - run: composer install --prefer-dist
      - run: vendor/bin/phpunit --coverage-clover=coverage.xml

26.7 扩展阅读


上一章第 25 章 — Docker 部署 下一章第 27 章 — 最佳实践