Skip to Content
Course content

71: Continuous Integration for PHP Projects

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've noticed a recurring theme when I mentor junior and mid-level PHP developers: they often think Continuous Integration (CI) is just a fancy word for "running my tests automatically." They'll tell me, "Oh, I have a shell script that runs ./vendor/bin/phpunit before I push, so I'm basically doing CI."

That is fundamentally wrong. Running tests on your own machine is just "testing." CI is about the Integration. The goal isn't just to see if the code works; it's to prove that the code works in a clean, neutral environment where your local config files, cached database states, and "it works on my machine" quirks don't exist. If you only run tests locally, you're testing your environment as much as you're testing your code.

"It's just a script" vs. The Neutral Environment

Imagine you're working on a PHP project with a PaymentGateway class. You decide to update a method signature from process(int $amount) to process(float $amount). You update the call site in your current feature branch, run your tests, and everything passes. Great, right?

But meanwhile, a teammate has pushed a change to the SubscriptionBilling module that also calls process(). Because you're only running tests locally on your branch, you don't realize you've just broken the billing system. You push your code, and suddenly the production site starts throwing TypeError exceptions.

A real CI pipeline stops this. It triggers on every push to a shared branch, pulls the latest version of the entire project, and runs the suite in a fresh Docker container. It catches the collision between your change and your teammate's change before the code ever touches the main branch. I've seen this save teams from dozens of "emergency" hotfixes a month.

Moving Beyond PHPUnit to Static Analysis

If your CI pipeline only runs PHPUnit, you're leaving the door open for bugs that tests often miss. I always tell my students that a professional PHP pipeline needs a "layered" defense. You don't want to wait five minutes for a full test suite to run only to find out you forgot a semicolon or passed a nullable string into a function that expects a strict string.

Your pipeline should follow this order of operations:

  • Composer Install: Get the exact dependencies defined in composer.lock.
  • Linting/Static Analysis: Use tools like PHPStan or Psalm. These tools analyze your code without actually running it. They can catch that TypeError I mentioned earlier in milliseconds.
  • Unit Tests: Run PHPUnit to verify the logic of individual components.
  • Integration Tests: Run tests that hit a real (but ephemeral) database to ensure your SQL queries actually work.

By failing fast—stopping the build at the static analysis stage—you save compute credits and, more importantly, your own time.

The Psychology of the Red Build

Here is a piece of advice from the trenches: you have to treat a failing CI build as a "stop-the-world" event. I've seen teams get lazy and ignore a red build for days because "we know why it's failing, it's just that one flaky test."

Once you tolerate a red build, your CI is dead. It's no longer a safety net; it's just noise. If the build is red, nobody merges. Period. This discipline is what separates professional software engineering from "just coding." It forces the team to maintain a healthy codebase and ensures that the main branch is always deployable.




📋 Practical Task

Configuring a GitHub Actions Pipeline for the Order Management System

You have been handed a PHP project called "Order Management System" that uses Composer and PHPUnit. Currently, developers are pushing code and hoping for the best. Your task is to move them to a professional CI workflow.

Create a GitHub Actions workflow file (.github/workflows/ci.yml) that automates the following process on every push and pull_request to the main branch:

  1. Environment: Set up a job that runs on ubuntu-latest using PHP 8.2.
  2. Dependency Management: Run composer install --no-progress --prefer-dist --optimize-autoloader to ensure the environment is identical to production.
  3. Static Analysis: Execute ./vendor/bin/phpstan analyse src. The build must fail if PHPStan finds any errors.
  4. Test Suite: Execute ./vendor/bin/phpunit. The build must fail if any test fails.

Deliverable: The complete YAML configuration for the workflow that ensures no code enters the main branch without passing both static analysis and the full test suite.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.