Introduction: From Basic CI/CD to Production Level
A basic CI/CD pipeline that simply runs tests is good. But for a serious PHP project, it's not enough. Real-world development requires dependency caching to speed up builds, parallel tasks, matrix builds for different PHP versions, static analysis, and, of course, automatic deployment. In this article, we'll explore advanced techniques for configuring GitHub Actions for PHP projects with practical examples.
We assume you are already familiar with the basics of YAML syntax and the basic structure of .github/workflows. Now let's move on to optimization and expanding capabilities.
1. Speeding Up Builds: Composer Caching and Optimal Strategy
Each workflow run downloads dependencies from scratch. This takes 1-3 minutes, which can be reduced to 5-10 seconds using caching.
Intelligent vendor Caching
We use actions/cache with a key based on the hash of composer.lock. If the lock file hasn't changed, dependencies will be restored from the cache.
name: PHP CIon: [push, pull_request]
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.2' tools: composer:v2
- name: Cache Composer dependencies uses: actions/cache@v3 with: path: vendor key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} restore-keys: | ${{ runner.os }}-php-
- name: Install dependencies run: composer install --no-progress --prefer-distNote the restore-keys: if the exact key is not found, the last cache for this OS will be used. This provides a fallback when switching branches.
Matrix Build for Different PHP Versions
To ensure compatibility, test on multiple PHP versions in parallel:
jobs: test: strategy: matrix: php-version: ['8.1', '8.2', '8.3'] dependency-stability: [prefer-lowest, prefer-stable]
steps: - uses: actions/checkout@v4 - name: Setup PHP ${{ matrix.php-version }} uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }}
- name: Cache uses: actions/cache@v3 with: path: vendor key: ${{ runner.os }}-php${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }}
- name: Install dependencies run: | composer update --${{ matrix.dependency-stability }} --prefer-dist --no-interaction
- name: Run tests run: vendor/bin/phpunitThis approach reveals compatibility issues before merging into the main branch. You'll get 6 parallel jobs (3 versions × 2 stability strategies).
2. Advanced Stages: Static Analysis and Code Quality
Unit tests are not the only quality control tool. Let's add PHPStan (maximum analysis level) and PHP Code Sniffer.
Linting and Static Analysis
Create a separate job for analysis that runs in parallel with tests:
jobs: static-analysis: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: php-version: '8.2' coverage: none
- name: Cache uses: actions/cache@v3 with: path: vendor key: ${{ runner.os }}-php-static-${{ hashFiles('**/composer.lock') }}
- run: composer install --no-interaction --prefer-dist
- name: PHPStan run: vendor/bin/phpstan analyse --level=max src/
- name: PHPCS run: vendor/bin/phpcs --standard=PSR12 src/