0076: Own builds of Windows PHP extensions

On this page

Status: accepted. Settles the Windows PHP extensions 0017 left still to come.

Context

PHP on Windows is windows.php.net's official build (0017), and Short Leash loaded 22 of its extensions, against about 70 in Short Leash's macOS builds. Laravel and Symfony apps often need extensions it doesn't ship, such as redis, apcu, igbinary, mongodb, grpc and sqlsrv. PECL no longer publishes Windows DLLs. A Windows DLL is built for one PHP minor and one compiler, and then it loads on every patch release of that minor.

Decision

  • Load the official DLLs the zip ships. Short Leash also loads gettext, ldap, ffi, dba, odbc, pdo_odbc, tidy, enchant, shmop and sysvshm, and imap on PHP 8.3 and older (runtimes::php::WINDOWS_EXTENSIONS). The ini names only DLLs present in the version's ext folder.
  • Build seven extensions in ShortLeash/runtimes, one DLL per minor. apcu, igbinary, msgpack, pcov, redis, opentelemetry and grpc are built against windows.php.net's devel pack for each minor (checked against the SHA-256 in releases.json), with the compiler that minor uses: vs16 for 8.0 to 8.3, vs17 for 8.4 and 8.5. Sources come from tags at pinned commits, and grpc from PECL's tarball at a pinned SHA-256 (with a small Visual C++ patch for PHP 8.3 and newer). Each DLL must load into that minor's official PHP (php -m lists it) before its file is attested and released as php-ext-<ext>-<version>. Runs happen by name only (what: php-ext-windows) and build only the files not released yet.
  • Repackage mongodb and Microsoft's sqlsrv and pdo_sqlsrv unchanged. ShortLeash/runtimes checks mongo-php-driver's and Microsoft's own DLLs against GitHub's digest, or a pinned SHA-256 for releases older than GitHub's digests, and keeps Microsoft's only with a valid Authenticode signature from Microsoft. They're load-tested the same way and zipped as they are, so the signature stays in the DLL. PHP 8.0 gets mongodb 1.20.1 and sqlsrv 5.11.1, the newest releases with a DLL for it.
  • Publish them in the signed manifest. Each zip holds only ext/php_<ext>.dll and licenses/php_<ext>/ (licenses and BUILDINFO.json), so many zips can extract into one PHP folder. Publish runtimes refuses any other entry, verifies each file's attestation like every other build, and lists it as a build named php-ext-<ext>, version <extversion>+php<minor>, for windows/x86_64. WINDOWS_LEASH_EXTENSIONS says which minors each extension must cover, and the publish run fails if a file is missing.
  • Extra archives of the PHP release. On Windows, Php::latest() reads the signed manifest too, and runtimes::php::with_windows_extensions appends each minor's extension zips to that minor's release, checked against the manifest's SHA-256, and marks it as Short Leash's build. Install, update and the daily replacement of rebuilt versions then handle the extensions like the PHP zip: no second download path and no new verification code in Short Leash. The lookup always uses the x64 builds, because Windows on Arm runs the x64 PHP too. Without a reachable, signed manifest, PHP installs from windows.php.net with the official DLLs only.
  • pcov for the CLI only. As on macOS, the server ini never loads php_pcov.dll; only the CLI's scan folder does, so php artisan test --coverage works and php-cgi never pays for it.
  • Not on Windows: pcntl, posix, sysvmsg, sysvsem, swoole, excimer, memcached, event, ev and imagick (runtimes::php::NOT_ON_WINDOWS). A site's extension check says so instead of offering a fix.
  • imagick isn't offered. The only ImageMagick 7 Windows build ShortLeash/runtimes can check finds its image coders and configuration through the registry keys its installer writes, or MAGICK_* paths. Loaded from PHP's folder without those, imagick would load but read and write no image format.
  • The swap renames instead of deleting. Replacing an installed version used to remove its folder and then move the new one in, which Windows refuses (and can leave half done) while a php-cgi has the DLLs loaded. Short Leash now stops the version's pools, renames <minor> to <minor>.old, renames the downloaded copy into place and then removes .old on a best-effort basis. If the old folder can't be moved, nothing changes and Short Leash says to close the program using that PHP version; if the new one can't be moved in, the old one is put back.

Consequences

  • PHP on Windows has Short Leash's macOS extensions except those Windows can't run, plus a few only the official builds carry.
  • Adding an extension, or any change to a minor's extension zips, reinstalls each installed version of that minor once, within a day, and restarts its PHP; until then, the Runtimes screen lists it as rebuilt by Short Leash. Xdebug's DLL is carried across.
  • We own those builds and their pins. A new extension release, or igbinary for PHP 8.5 once 3.2.17 is out, needs a Build run by hand and then Publish runtimes; nothing rebuilds them on a schedule.
  • If Short Leash can't reach its server when it installs a PHP patch release, that version runs with the official extensions until the next daily replacement adds Short Leash's.
  • Connecting to SQL Server also needs Microsoft's ODBC Driver for SQL Server. Installing it needs administrator rights, so it stays the user's install.
  • Short Leash itself never checks Authenticode: Microsoft's signature is checked once, at build time, and Short Leash trusts the DLLs through the signed manifest's SHA-256. That keeps WinVerifyTrust and new unsafe code out of leash-core.