Reproducible Windows builds of PHP with security patches we need, built on GitHub-hosted runners with the PHP project's own php-windows-builder and published as GitHub Releases.
Why: end-of-life PHP versions bundled with third-party products (currently PHP 7.3.31 for IceWarp Server on Windows) do not receive upstream Windows binaries for new vulnerabilities such as php2shell (CVE-2026-109618, CVE-2026-109619). This repository builds exactly the bundled version plus the required patches, from pinned sources, with a verifiable build log.
Every release (php-<version>-r<N>) contains:
| File | Content |
|---|---|
php-<version>-nts-Win32-<toolset>-<arch>.zip |
Standard PHP layout, non thread safe: php.exe (CLI), php-cgi.exe (CGI/FastCGI), php-win.exe, php7.dll, ext\, … |
php-<version>-Win32-<toolset>-<arch>.zip |
Same, thread safe (php7ts.dll) |
php-<version>-icewarp-<nts|ts>-<arch>.zip |
IceWarp layout: php.exe = CGI/FastCGI binary, php-cli.exe = CLI binary (see below) |
php-debug-pack-*.zip, php-devel-pack-*.zip, php-test-pack-*.zip |
Debug symbols, headers/libs, test suite |
SHA256SUMS.txt |
Checksums of all files |
BUILD-INFO.txt inside every binary archive names the source commit, the patches with their hashes,
the builder commit and the GitHub Actions run that produced the file.
IceWarp runs PHP as FastCGI: it starts several instances of <IceWarp>\php\php.exe and talks to them
over TCP (IceWarp docs).
That is the behaviour of PHP's CGI binary, so in the IceWarp folder php.exe is php-cgi.exe and
php-cli.exe is the command line php.exe. The icewarp bundle has the files renamed accordingly.
Before replacing anything, confirm on the server which flavour (thread safety) IceWarp ships:
& 'C:\Program Files\IceWarp\php\php.exe' -v # expect "(cgi-fcgi) ... NTS" or "... ZTS"
& 'C:\Program Files\IceWarp\php\php-cli.exe' -v # expect "(cli)"
Test-Path 'C:\Program Files\IceWarp\php\php7ts.dll' # True = thread safe build -> use the -ts- bundle
# 32-bit IceWarp (C:\Program Files (x86)\IceWarp): use the -x86 bundlesThen: stop the IceWarp services, back up the php folder, copy the bundle contents over it
(keep IceWarp's own php.ini and any extra extensions it ships; our ext\ only contains PHP's
bundled extensions), start the services and check php.exe -v / php-cli.exe -v again plus a
WebClient login. The build date in -v distinguishes the patched build from the original 7.3.31.
scripts/Verify-Fix.ps1 checks an installed PHP (not an attack - a positive test). It runs the real
binaries from the folder, so it tests the loaded php7ts.dll/php7.dll, and proves the mitigation by
its behaviour: the patched memory manager terminates with exit code 1 on an allocation failure,
where an unpatched PHP exits with 255.
# on the IceWarp server, in an elevated PowerShell
Set-ExecutionPolicy -Scope Process Bypass -Force
.\Verify-Fix.ps1 -PhpDir 'C:\Program Files (x86)\IceWarp\php'It reports PATCHED / NOT PATCHED per binary and warns if the engine Build Date is not the expected one
(a sign that php.exe was replaced but the DLL was not). After replacing files, restart the IceWarp
services so the running php.exe instances reload the new DLL, then re-run.
builds/<version>/source.json pinned php-src tag + commit, pinned builder commit
builds/<version>/patches/ *.patch applied in name order (provenance in patches/README.md)
builds/<version>/bundles.json product bundles = file renames on top of the standard archive
scripts/*.ps1 patch application, smoke tests, build info, bundle assembly
.github/workflows/build.yml the pipeline
preparereads the definition and builds the job matrix.build(matrixarch × ts,windows-2022) checks outphp/php-srcat the pinned commit, applies the patches, and runsphp/php-windows-builder/php@<pinned sha>from the source tree. The builder installs the matching Visual C++ toolset (VC15 for PHP 7.3) and the official dependency bundles, builds with PGO and packages like the official windows.php.net releases.packagesmoke-tests every archive (version banner, thread safety, modules,php-cgi.exe, out-of-memory handling (the patch terminates with exit code 1 instead of 255, which proves it is active), a multipart/form-data POST through php-cgi), addsBUILD-INFO.txt, creates the bundles andSHA256SUMS.txt, and publishes a release when a tag was given.
A full run takes roughly 45 minutes (about 35 for the two builds).
- Actions → Build PHP → Run workflow:
version(default7.3.31),arch-list,ts-list,build-run-id(re-package the artifacts of an earlier run without rebuilding),append-release, optionalrelease-tag. Without a release tag the result is only a workflow artifact (release-<version>), useful for testing. - Tag push:
git tag php-7.3.31-r1 && git push origin php-7.3.31-r1buildsx64,x86×nts,tsand publishes the release. append-release: add the files of a run (for example another architecture) to an existing release instead of failing;SHA256SUMS.txtis merged.
- Create
builds/<version>/source.json(copy an existing one; resolve the tag to a commit withgit rev-parse php-<version>^{commit}in a php-src clone) andpatches/. - Check locally that the patches apply:
git apply --checkon a checkout of the tag. - Bump the release tag suffix (
-r2, …) when only patches change for the same PHP version.
Supported PHP versions follow php-windows-builder (7.1 and newer; the toolset per version is in its
php/BuildPhp/config/vs.json).
- PHP source is checked out by commit hash, not by tag name; the builder action is pinned by hash.
- Patch files live in this repository; their origin and SHA-256 are documented in
builds/<version>/patches/README.mdand repeated inBUILD-INFO.txt. - Builds run on GitHub-hosted
windows-2022runners only; nothing is uploaded from developer machines. - The build log of every release is linked in its notes.
Scripts and workflows: MIT (see LICENSE). PHP and the produced binaries: PHP License 3.01.