setup-php: three minimum PHP versions, one per runner family
Project brief: GitHub action to set up PHP with extensions, php.ini configuration, coverage drivers, and various tools.
At a glance
- What is it?
- setup-php is a GitHub Action that installs PHP, its extensions, its configuration file and coverage drivers on whichever runner you give it. What it can install depends on the runner in ways the page states and then tabulates: the minimum PHP differs across hosted, macOS and self-hosted runners, the newest Ubuntu label is not what latest resolves to, and the Intel macOS runners are the only ones with PHP already present.
- Who is it for?
- setup-php fits a CI workflow that needs a specific PHP version on a specific operating system without pinning a container, and the action is explicit about the combinations it will and will not attempt. Verify four things before you rely on it.
- 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 received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- shivammathur/setup-php3,279 ★ · +39
| Repository | 2026-07-30 | 2026-11-14 |
|---|---|---|
| shivammathur/setup-php | 3.2k | 3.3k |
39 Stars in the last 107 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Three minimum PHP versions, one per runner family
The support section gives three separate ranges rather than one. Hosted runners take anything from the oldest listed release up to a ceiling two versions ahead of the current stable line. Apple silicon macOS hosted runners start later, at the release that dropped the older runtime. Self-hosted runners start at the same later point. So the answer to "what is the oldest PHP I can set up here" is three different numbers depending on which runner you are on, and none of them is stated next to the tables that decide it. The version table that follows compounds the ambiguity by grading each release on two axes at once, listing the oldest one as both stable and end of life, which is not a contradiction but does mean a reader has to hold two meanings of stable in mind while scanning one column.
Apple silicon macOS ships no PHP, and the Intel runners that do are deprecated
Read the pre-installed column of the hosted table and the pattern is immediate. All three Apple silicon macOS rows have nothing in that column at all. Both Intel macOS rows have a modern PHP already present. Then the note below the table says support for Intel macOS is deprecated and will be removed completely in a future release, recommends migrating to an arm64 machine on a recent macOS, and names one runner label that does not appear anywhere in the table. So the recommended destination has no PHP pre-installed and the discouraged origin is the only one that does. Anyone migrating as advised will start paying for a PHP download on every job, and the deprecation names a label the support matrix never listed, which makes it hard to tell whether it is already gone or merely planned.
The latest label is one distribution behind, and its PHP is two minors older
The table maps the rolling label to a specific distribution, and that mapping is worth reading literally. The rolling Linux label resolves to the previous long-term release with a PHP two minor versions behind the newest distribution's. So a workflow that says latest is not getting the newest thing available, and a workflow that upgrades from the rolling label to the explicit newest label gets a different PHP without changing its version request. Two more asymmetries sit in the same column set. The Windows on Arm runner ships a PHP one minor version behind the Windows on x64 runners, and the Windows labels distinguish a Visual Studio edition suffix from the plain one. The only consistent thing across the whole table is that the action will switch to a pre-installed version when it finds one and install otherwise.
Windows 7 and a 2012 server are listed as supported self-hosted hosts
The self-hosted table reaches back a long way. Windows 7 and newer is listed, as is Windows Server 2012 R2 and newer, alongside three current Linux distributions, two Debian releases and three macOS releases. Both Windows entries are operating systems whose own support has ended, which means the action is claiming to run on hosts that cannot receive a current PHP build from a vendor. The same table also documents the older self-hosted label convention, with the capitalised operating system names the platform used before it standardised on lowercase ones, so a workflow label written the current way does not match what the page says. Derived distributions built on those Linux bases are covered as a best effort, which is the only place in the two tables where support is qualified rather than stated.
The version jumps from 2.37.2 to 2.40.0 and the committed version is a pre-release
Three releases appear in the list. The two before the newest are patch releases a month and then three weeks apart. The newest skips two minor numbers entirely and carries a pre-release suffix, and the manifest in the repository agrees with it, so the tree is sitting on a beta rather than a stable point. That is a normal way to work and a confusing thing for anyone pinning. There is a versioning section further down the file, which is where the scheme is presumably explained, and the manifest marks the package as publishable rather than private. It is worth noting what that manifest is for, though, since the action is consumed through its action manifest rather than as a library, and that is where the next inconsistency sits.
Two builds from one tree, and the package file list covers only one
The manifest declares an entry point and a type declaration file inside a directory called lib, and it whitelists that directory and the source directory for publication. Neither of those is where a GitHub Action runs. The action's runtime is a bundle built into a different directory, and that bundle is committed to the repository, which the release script forces into the index. So there are two builds from one source tree: a compiler run that produces the whitelisted directory for the registry, and a bundler run that produces the committed directory the action actually executes. The published tarball and the action payload are therefore not the same artefact, and the fields describing the package describe the one that is not what anyone consumes. The manifest also maps a conventional test directory name onto a directory that uses double underscores.
The lint target is three shell globs and the format target force-adds snapshots
The scripts are short and the shells are doing more work than a JavaScript developer would expect. The lint script passes two glob patterns to the linter with a cache flag and a fix flag, so it lints the source and the test directories and rewrites what it finds. The format script runs the formatter over the same two patterns and then forces files into the index in the test directory, which is how snapshot files that are otherwise ignored get committed. A separate release script builds the bundle and forces that too. Three of the eight scripts therefore mutate git state rather than just the working tree, and running the formatter is enough to stage files. The remaining scripts are the ones you would expect: compile, type check, test, and a release bundler.
Twenty-two table-of-contents entries under one heading
The contents list runs to nineteen top-level entries, and the usage section under it accounts for twenty-two of the sub-entries between them: inputs, outputs, flags, ten named setup variants covering everything from matrix builds to nightly and debug builds, thread-safe builds, multi-architecture builds, self-hosted and local testing, just-in-time configuration, three separate cache sections, three separate authentication sections for the different package hosts, inline scripts, problem matchers and examples. So the longest part of the documentation is a table of contents for a single section. The headings also carry an icon each, and the header carries a marketplace listing, a coverage link, a licence link, a version link, three social links and a separate status page. The status page is the one worth knowing about: it implies the action is operated as a service rather than merely released as code.
Editorial conclusion
setup-php fits a CI workflow that needs a specific PHP version on a specific operating system without pinning a container, and the action is explicit about the combinations it will and will not attempt. Verify four things before you rely on it. Establish which PHP versions your runner family will accept, because the floor is not one number: it is lower on hosted runners and higher on Apple silicon macOS and on self-hosted hosts. Check whether your platform has PHP pre-installed, since the Apple silicon macOS runners do not and the action will therefore download it every run. Read the deprecation note rather than the table, because it names a runner label the table omits and recommends migrating away from the only macOS line that ships PHP. And note that the committed version is a pre-release while the newest tagged release skips two minor numbers.
Frequently asked questions
What is shivammathur/setup-php?
A GitHub Action that sets up a PHP environment on a runner: the required extensions, the configuration file, code-coverage support and tools such as the dependency manager. It gives one cross-platform interface across operating systems, works on both hosted and self-hosted runners, and can generate scripts, problem matchers and configuration for the job.
Which PHP versions can shivammathur/setup-php install?
The ceiling is two versions ahead of the current stable line on every runner family. The floor is not one number: hosted runners accept the oldest listed release, while Apple silicon macOS hosted runners and self-hosted runners both start one release later. The support table also records each release as stable or not alongside whether it has reached end of life.
Does shivammathur/setup-php work on macOS?
Yes, on three current releases, on both architectures. The Apple silicon rows list no pre-installed PHP at all, while the Intel rows list a current one. The note below the table deprecates Intel macOS and recommends arm64 machines on a recent release, and also names a runner label the table does not list.
What operating systems does shivammathur/setup-php support on self-hosted runners?
Three current Linux distributions, two Debian releases, two Windows ranges going back to Windows 7 and to Windows Server 2012 R2, and three current macOS releases on arm64. Derived distributions built on those Linux bases are covered as a best effort. The table documents the older capitalised self-hosted labels rather than the current lowercase ones.
How is shivammathur/setup-php released?
There are two builds from one source tree: a compiler run that produces the directory the manifest publishes to the registry, and a bundler run that produces the committed directory the action executes. The committed version in the manifest is a pre-release, and the newest tagged release skips two minor numbers relative to the one before it. A separate status page implies the action is operated as a service.
Official sources
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.
[](https://hysenlabs.com/projects/shivammathur-setup-php)Star history chart for your README
[](https://hysenlabs.com/projects/shivammathur-setup-php)Was this useful?