Skip to content

fix: use DDEV's php.ini and .ddev/php settings, add common extensions - #16

Merged
rfay merged 1 commit into
mainfrom
20261022_static_php_config
Oct 22, 2026
Merged

rfay merged 1 commit into
mainfrom
20261022_static_php_config

Conversation

@rfay

@rfay rfay commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

The Issue

PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 fix the "PHP2Shell" vulnerabilities (DDEV blog post). deb.sury.org only has amd64 packages so far, so on arm64 this add-on is one way to get a patched PHP. Testing it with 8.5.12 on Apple Silicon turned up these problems:

  • No PHP configuration was loaded. Static PHP looks only in /usr/local/etc/php, which didn't exist; the existing /etc/php.ini symlink was never read. The CLI and php-fpm both ran on PHP's built-in defaults: memory_limit 128M, upload_max_filesize 2M, and sendmail_path not pointing at Mailpit. .ddev/php/*.ini overrides had no effect, including the blog post's mitigation settings.
  • Common extensions were missing, most importantly mysqli, which WordPress requires.
  • The build took 19.5 minutes on arm64.

How This PR Solves The Issue

  • Link DDEV's cli and fpm php.ini into /usr/local/etc/php as php-cli.ini and php-fpm-fcgi.ini. PHP reads the SAPI-specific file before php.ini, so each SAPI gets DDEV's settings.
  • Build with --with-config-file-scan-dir=/usr/local/etc/php/conf.d:/mnt/ddev_config/php so .ddev/php/*.ini overrides apply. DDEV's own conf.d can't be scanned because its extension= lines make static PHP print warnings, so its one other setting, zend.assertions = 1, is written to /usr/local/etc/php/conf.d.
  • Point /etc/php-fpm.conf at the php_version directory, where DDEV copies the overrides, instead of the static version's directory.
  • Download pre-built libraries with --prefer-pre-built. The build took 13m54s, about 5 minutes of which was a mirror returning 504s, so roughly 9 minutes, while building 12 more extensions.
  • Add mysqli, intl, zip, sodium, iconv, exif, xmlreader, xmlwriter, posix, pcntl and calendar to the default extensions.
  • README: new "PHP configuration" and "Extensions" sections. They list extensions that can be added and those that can't be built (uploadprogress, xmlrpc, Xdebug), with an example of adding extensions. php_version is no longer described as ignored: it chooses the php.ini and php-fpm configuration, so it should match the built minor version.

Tested by hand on arm64 (OrbStack, DDEV HEAD) with PHP 8.5.12:

  • The CLI and FPM load DDEV's php.ini plus .ddev/php overrides.
  • php -m shows no warnings.
  • mysqli connects to the db container, and mail() goes through Mailpit.
  • Extensions still missing compared with the stock image: bz2, FFI, gettext, igbinary, imagick, ldap, memcached, msgpack, readline, redis, shmop, soap, sockets, sysv*, uploadprogress, xmlrpc, xsl and yaml.

Manual Testing Instructions

ddev config --php-version=8.5
ddev dotenv set .ddev/.env.php-patch-build --static-php-version=8.5.12
ddev add-on get https://github.com/rfay/ddev-php-patch-build/tarball/refs/pull/16/head
ddev restart
ddev php --ini      # php-cli.ini plus /mnt/ddev_config/php files
ddev php -m         # no warnings; mysqli, intl, zip, sodium present

Add a .ddev/php/test.ini containing upload_max_filesize = 7M, run ddev restart, and confirm ini_get('upload_max_filesize') returns 7M from both ddev php and a web page.

Automated Testing Overview

A new unreleased_checks function runs only in the "install from directory" test. It checks:

  • memory_limit, a .ddev/php override and zend.assertions, from both the CLI and php-fpm
  • that php -m prints no warnings
  • that the new extensions are present

The "install from release" test can't check these until they're released. After the next release, these checks can move into health_checks.

I didn't run the bats tests locally (my router uses a non-default port), so CI is the first run.

Release/Deployment Notes

  • Existing users get a rebuild with the new extensions and their DDEV PHP settings applied. Their memory_limit and upload limits change from PHP defaults to DDEV's.
  • Users whose php_version differs from the static version's minor will now get the php.ini and php-fpm configuration for php_version.
  • After releasing, consider moving unreleased_checks into health_checks.

🤖 Generated with Claude Code

Static PHP looked for its configuration only in /usr/local/etc/php, which
didn't exist, so neither the CLI nor php-fpm loaded any php.ini. DDEV's
settings (memory_limit, upload limits, Mailpit's sendmail_path) and
.ddev/php/*.ini overrides had no effect.

- Link DDEV's cli and fpm php.ini as php-cli.ini and php-fpm-fcgi.ini,
  which static PHP reads before php.ini
- Build with a scan dir that includes /mnt/ddev_config/php so .ddev/php
  overrides apply; DDEV's conf.d can't be used because static PHP can't
  load shared extensions, so carry over its zend.assertions setting
- Point /etc/php-fpm.conf at the php_version directory
- Download pre-built libraries where available, which roughly halved
  the build time on arm64
- Add mysqli, intl, zip, sodium, iconv, exif, xmlreader, xmlwriter,
  posix, pcntl and calendar to the default extensions
- Document configuration, missing extensions and how to add extensions
- Test ini loading and the new extensions for directory installs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rfay
rfay merged commit f80d849 into main Oct 22, 2026
2 checks passed
@rfay
rfay deleted the 20261022_static_php_config branch October 22, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant