CI/CD for PHP Projects: GitHub Actions Advanced Level

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

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-dist


Note 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/phpunit


This 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/

Blogs

Book Recommendations