Self-hosted service
shivammathur/setup-php avatar
shivammathur/setup-php

shivammathur/setup-php: a GitHub Action that builds the PHP environment your CI actually needs

Project brief: GitHub action to set up PHP with extensions, php.ini configuration, coverage drivers, and various tools.

3,263 stars420 forksTypeScriptMIT

At a glance

What is it?
setup-php is a GitHub Action that installs a chosen PHP version with the extensions, php.ini settings, coverage driver and Composer tooling you specify, across Linux, Windows and macOS runners. It is the right tool when your PHP matrix is the hard part of CI, and the wrong one when you only need the PHP that the runner already ships.
Who is it for?
Adopt setup-php if your workflow needs a PHP version, extension set or coverage driver that the runner image does not ship, or if you maintain a matrix across Linux, Windows and macOS. Skip it if you are happy with the pre-installed PHP on the runner and need nothing beyond it, because pinning a version and a tool list adds a moving part to every job.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem setup-php solves, and who it is for

A GitHub-hosted runner arrives with one PHP version already installed. The README's OS/Platform Support table lists what that is per image: PHP 8.1 on ubuntu-22.04, PHP 8.3 on ubuntu-24.04 and ubuntu-latest, PHP 8.5 on ubuntu-26.04 and on the Windows Server images. macOS ARM64 runners are listed with no pre-installed PHP at all. That single version is rarely the whole story. A library that supports PHP 7.4 through 8.6 needs several of them in one workflow, plus the extensions its own composer.json requires, plus a coverage driver so the test step can emit a report.

The action exists to make that environment declarative. Instead of a chain of apt-get, pecl, docker-php-ext-enable and ini-file edits that behaves differently on Windows, you write inputs and the action resolves them. The audience is maintainers of PHP packages and frameworks who run a version matrix, and teams whose CI must reproduce a production extension set rather than the runner default. The repository ships 24 ready-made workflow files under examples/, including laravel.yml, symfony.yml, wordpress.yml, drupal.yml and cakephp-mysql.yml, which tells you the intended users are framework and CMS projects with database-backed test suites.

How the action resolves a version, extensions and coverage

The action is a TypeScript program compiled into dist/ with @vercel/ncc, and action.yml at the repository root is the interface GitHub reads. The runtime dependencies are deliberately thin: @actions/exec for running commands and compare-versions for version comparison. Everything else in package.json is a devDependency for linting, formatting and the Jest suite under __tests__/.

That shape matters for how you reason about it. There is no container image doing the work. The action executes platform-specific setup on the runner itself, which is why the README can claim one interface across Ubuntu, Debian, Windows and macOS. When the requested PHP version is already present, the README states the action switches to it rather than installing a second copy. When it is not present, the action installs it. Extensions are requested as a comma-separated input, coverage is requested by naming xdebug or pcov, and the php.ini changes are derived from those inputs. One consequence worth noting: because setup happens on the runner, the action is bounded by the runner's own toolchain, and the README lists operating systems outside the named Ubuntu and Debian releases as supported only on a best effort basis.

Installing setup-php and running a first matrix job

There is nothing to install locally. You add a step to a workflow file in .github/workflows/ of your own repository, and the README's Usage section is the reference for the inputs. The README's Basic Setup section shows the shape of the step: the action reference followed by the inputs you want to set. The reader should see the step resolve PHP in the job log before the test step runs.

The inputs documented in the README are the ones to use here. php-version selects the runtime, extensions takes a comma-separated list, and coverage names the driver (xdebug or pcov). A step that sets all three looks like the Basic Setup example in the README.

The realistic first use is a matrix. The README's Matrix Setup section is the pattern to copy, and the version list is the kind of spread a library supporting old and new runtimes would run. After the setup step, php -v in a following run step reports the version you asked for, and php -m lists the extensions you named. If the version you requested is not in the support table for that runner, the failure appears at the setup step, not at the test step.

Where setup-php stops being the right tool

The support matrix is the first constraint, and it is not uniform. macOS ARM64 runners are documented as supporting PHP 5.6 to 8.6, while GitHub-hosted runners generally cover PHP 5.3 to 8.6. Self-hosted runners are documented at PHP 5.6 to 8.6. If your project still tests on PHP 5.3, that combination only exists on GitHub-hosted runners and not on the macOS ARM64 images. The README is explicit that operating systems based on the listed Ubuntu and Debian releases are supported on a best effort basis, which is a soft boundary rather than a guarantee.

The second constraint is philosophical. If your project targets one modern PHP version, and the runner image already ships it, the action adds a dependency on a third-party action and a resolution step to every job for no change in outcome. The README's own table shows ubuntu-26.04 and the Windows Server images already carrying PHP 8.5. A project that only needs PHP 8.5 on those images can skip the action entirely.

The third is that the action configures the runner, it does not reproduce production. It will not give you a specific web server configuration, a database server, or the exact php-fpm pool settings your deployment uses. The examples directory contains MySQL and Postgres workflow files, which suggests the intended pattern is to pair setup-php with separate service containers rather than to expect the action to stand up the whole stack. If your CI failures come from web server behaviour rather than from PHP itself, this is the wrong layer to fix them at.

setup-php against a hand-written apt and pecl step

The obvious alternative is doing it yourself: a run step that installs PHP packages from the distribution, adds a PPA for versions the distribution does not carry, and edits the ini file. The difference is not speed, it is where the platform knowledge lives. A hand-written step encodes assumptions about one distribution. The moment you add windows-latest or a macOS runner to the matrix, those assumptions break, and you maintain a second and third branch of shell.

setup-php puts that branching inside the action. The README presents a single input surface (php-version, extensions, coverage, ini-values, tools) and states that it works across GitHub-hosted and self-hosted runners on Ubuntu, Debian, Windows and macOS. That is the actual trade: you accept a third-party action in your workflow in exchange for not owning the per-platform install logic. The cost is that when a version or extension combination is unsupported, you are waiting on the action rather than patching your own step, and the support table in the README is the document that decides.

A second alternative is running tests inside a php Docker image. That gives you exact, reproducible PHP builds and is a better fit if you already containerise the application. It does not give you the runner's native macOS or Windows environment, which matters if you ship a library that must pass on those platforms.

Versioning, licence and what maintenance looks like

The README has a Versioning section, and the practical choice is between a moving major tag such as v2 and a full release tag. A major tag picks up patch releases without an edit; a full tag freezes the exact behaviour your workflow ran against. Both are supported, and the decision is yours to make per repository.

The licence is MIT, declared in package.json and present as LICENSE at the repository root. MIT is permissive: it allows use, modification and redistribution with the licence text retained. That is a statement about the project's terms, not about your obligations, and it is worth reading the LICENSE file itself rather than a summary.

On maintenance, the last push to the default branch was on 2026-06-08, which is the same date as the 2.37.2 release. The two releases before it are 2.37.1 on 2026-05-14 and 2.37.0 on 2026-03-15. The repository is not archived. That is a release cadence of roughly one to two months across those three versions, and the version number in package.json matches the latest release tag, which is a small signal that the published artefact and the repository are kept in step. The upgrade cost of the action itself is low: a patch release is picked up automatically if you use a major tag, and the inputs documented in the README are the stable surface. The real upgrade cost sits in the PHP versions you test, not in the action.

Editorial conclusion

Adopt setup-php if your workflow needs a PHP version, extension set or coverage driver that the runner image does not ship, or if you maintain a matrix across Linux, Windows and macOS. Skip it if you are happy with the pre-installed PHP on the runner and need nothing beyond it, because pinning a version and a tool list adds a moving part to every job. Before you commit, check the OS/Platform Support table for the runner label you target, confirm your PHP version is listed for that runner, and read the Versioning section to decide whether you pin a major tag or a full release tag.

Frequently asked questions

How do I set up a PHP environment for a GitHub Actions workflow with setup-php?

Add a step that uses shivammathur/setup-php@v2 and pass php-version as an input. Extensions, coverage and ini-values are additional inputs on the same step, and the README's Usage section lists them. The action resolves the version on the runner and switches to it if it is already installed.

How do I install a specific PHP version like PHP 8.3 with setup-php?

Set the php-version input to '8.3'. The README's PHP Support table lists which versions are available per runner type; GitHub-hosted runners cover PHP 5.3 to 8.6, macOS ARM64 runners cover PHP 5.6 to 8.6, and self-hosted runners cover PHP 5.6 to 8.6.

Does setup-php work on Windows runners?

Yes. The README's OS/Platform Support table lists Windows Server 2025 and Windows Server 2022 as GitHub-hosted runners, and Windows 7 and newer plus Windows Server 2012 R2 and newer as self-hosted runners. The action is described as a cross-platform interface for setting up the PHP environment.

How do I install and set up PHP with setup-php?

There is nothing to install on your machine. You reference the action from a workflow step with the php-version input, and the action installs or switches to that PHP version on the runner before your test step runs.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/shivammathur-setup-php.svg)](https://hysenlabs.com/projects/shivammathur-setup-php)