How to get a Dynamic PHP Version Matrix in GitHub Actions

Found a typo? Edit me

Do you want to run your tests on each PHP version you support? PHP 7.3, 7.4 and 8.0? Instead of 3 workflows with copy-paste steps, you can define just one with a matrix for PHP versions.

But PHP is released every year. The version constraints are already defined in composer.json. Hmm, how could we use this knowledge to provide a list of PHP version for a dynamic matrix?

Do you know memory locks? An "if this then that" code smell. E.g., you always have to put keys into your left pocket, after you lock your office door.

If we have tests, we want them run on all PHP versions we support. To be sure, no PHP 8 features are running on PHP 7.4.

When new PHP version once a year, then we have to:

This is utterly useless operation = double the work and double the maintenance—no gain, except the fear of control.

Memory Lock is Expensive

Could you guess how much time it took to send both commits? 2 minutes? 10 minutes?

Almost 2 hours. I had to send a new commit based on feedback in code-review. That's a hidden cost of memory lock code smell. Hidden cost in attention, work and slow feedback loop.

Trust composer.json

What PHP version should be tested? The information is already in composer.json:

    "require": {
        "php": "^7.2|^8.0"

You're right. It's this list:

So why should we duplicate this information in the GitHub Actions workflow config?

We can do better with dynamic matrix.

EasyCI to the Rescue

Symplify 9 is coming with a brand new package called EasyCI. This package helps you with CI maintenance, like in our case above:

composer require symplify/easy-ci --dev

vendor/bin/easy-ci php-json
# "[7.2, 7.3, 7.4, 8.0]"

Now we have a problem, the command to provide JSON data and GitHub Actions. Let's put them together.

Workflow with Dynamic PHP Versions

There are 2 steps in our unit tests workflow:

The First Step

    # first step
        runs-on: ubuntu-latest
            -   uses: actions/[email protected]

            -   uses: shivammathur/[email protected]
                    # this is the only place we have to use PHP to avoid the lock to bash scripting
                    php-version: 8.0

            -   run: composer install --no-progress --ansi

            # to see the provided output, just to be sure
            -   run: vendor/bin/easy-ci php-versions-json

            # here we create the json, we need the "id:" so we can use it in "outputs" bellow
                id: output_data
                run: echo "::set-output name=matrix::$(vendor/bin/easy-ci php-versions-json)"

        # here, we save the result of this 1st phase to the "outputs"
            matrix: ${{ steps.output_data.outputs.matrix }}

The Second Step

    # continue from above
        needs: provide_php_versions_json

        runs-on: ubuntu-latest

            fail-fast: false
                php: ${{ fromJson(needs.provide_php_versions_json.outputs.matrix) }}

        # continue with tests
            # ...
            -   run: vendor/bin/phpunit

This workflow is not just a theory. It's tested for last week on Symplify repository and it works :).

Here is full .github/workflows/unit_tests.yaml worfklow to explore.

Happy coding!

Have you find this post useful? Do you want more?

Follow me on Twitter, RSS or support me on GitHub Sponsors.