PHP 完全指南 / 第 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' => 'alice@test.com'])
->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 章 — 最佳实践