fix(php8): prevent OPcache double-load without disabling it on older parents - #91
Conversation
The official php:8.x-fpm image already enables OPcache via docker-php-ext-opcache.ini (zend_extension=opcache). Adding a second 00-opcache-load.ini (zend_extension=opcache.so) made PHP load OPcache twice on PHP <= 8.4, emitting 'Cannot load Zend OPcache - it was already loaded' at every invocation. On PHP 8.5 OPcache is statically compiled, so the guard never created the file there anyway. The block only ever fired on <= 8.4, exactly where the parent image already loads OPcache, so it was always redundant. opcache.ini still tunes OPcache in both cases; it carries no zend_extension line. Assisted-by: claude-code/claude-opus-4-8
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨No code suggestions found for the PR. |
opcache.fast_shutdown was removed in PHP 7.2 — fast shutdown became always-on and the directive no longer exists. PHP 8.4 does not list it in phpinfo() and ini_get() returns false, so the line in 8/conf/opcache.ini is silently ignored dead config. The 8/ image only targets PHP 8.x, so it can never apply. Remove it. Assisted-by: claude-code/claude-opus-4-8
Removing the loader block entirely regressed OPcache on the 8.3.x and
8.4.2 builds: their official php:8.x-fpm-alpine parents (alpine 3.18/3.21)
do not ship docker-php-ext-opcache.ini, so the removed block was the only
thing enabling OPcache there. Verified by building 8.3.15 from this branch:
no loader, extension_loaded('Zend OPcache') === false.
Only the newer alpine3.24 parents (PHP 8.4.22+) and the static 8.5 build
enable OPcache on their own, which is why the double-load warning only
appeared there.
Restore the block but guard it: add 00-opcache-load.ini only when the parent
has not already enabled OPcache (docker-php-ext-opcache.ini absent) and
opcache.so exists. Result, verified across builds:
- 8.3.15: block fires, OPcache on, no warning
- 8.4.22: parent ini present, block skipped, no double-load
- 8.5.7: static, no opcache.so, block skipped
Assisted-by: claude-code/claude-opus-4-8
There was a problem hiding this comment.
Pull request overview
This PR fixes a PHP 8.x image runtime warning caused by OPcache being loaded twice when the upstream php:<tag> parent already enables OPcache by default, while preserving OPcache enablement for older (pre-change) parents.
Changes:
- Guard OPcache loader creation (
00-opcache-load.ini) so it’s only added when the parent image hasn’t already enabled OPcache. - Remove the obsolete
opcache.fast_shutdowndirective from the PHP 8 OPcache config.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| 8/Dockerfile | Adds a presence-check guard to prevent OPcache double-loading while keeping OPcache enabled on older parents. |
| 8/conf/opcache.ini | Removes a dead OPcache setting that no longer applies to PHP 8.x. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Enable OPcache as a Zend extension only when the parent image hasn't already | ||
| # (older php:8.x parents don't ship docker-php-ext-opcache.ini) and it ships as | ||
| # a shared object. Newer parents (PHP 8.4.22+) and static builds (PHP 8.5) | ||
| # already load it, so the guard avoids the "Cannot load Zend OPcache - it was | ||
| # already loaded" double-load while keeping OPcache enabled everywhere. |
Key the explanation off the docker-php-ext-opcache.ini file and docker-library/php#1587 rather than a specific patch version (8.4.22+), since rolling 8.3+ tags rebuilt after that PR also ship the ini. Assisted-by: claude-code/claude-opus-4-8
Problem
Since
e9b6e49("feat(php8): add PHP 8.5.7 and refresh image dependencies"), the 8.x image emits at every PHP invocation on some builds:Reproduced on the
8.4.22-fpm-alpine3.24-rootlesstag. The8.4.2tag (built before that commit) is clean.Root cause
Commit
e9b6e49added a loader block:This fires whenever
opcache.soexists (all PHP <= 8.4). Whether that double-loads depends on whether the officialphpparent already enables OPcache.The official image started enabling OPcache by default in docker-library/php#1587 "Enable opcache by default", merged 2025-06-25, which adds
RUN docker-php-ext-enable opcacheto every Dockerfile variant (motivated by the RFC make_opcache_required). The determining factor is whether a givenphp:<patch>tag was built after that merge — not the Alpine version. Pinned old patch releases keep their pre-merge frozen image; newer patches were rebuilt with the change.So:
php:8.4.22-fpm-alpine3.24) — already loads OPcache viadocker-php-ext-opcache.ini; our block adds a second loader → double-load warning.php:8.3.2-fpm-alpine3.18,php:8.3.15-fpm-alpine3.21,php:8.4.2-fpm-alpine3.21) — does NOT load OPcache; our block is the only loader.opcache.sois absent, the block never fires; parent loads it statically.Verified against the official parents:
docker-php-ext-opcache.iniphp:8.3.2-fpm-alpine3.18(pinned, pre-merge)php:8.3.15-fpm-alpine3.21(pinned, pre-merge)php:8.4.2-fpm-alpine3.21(pinned, pre-merge)php:8.3-fpm-alpine3.21(rolling, post-merge)php:8.4.22-fpm-alpine3.24(post-merge)php:8.5.7-fpm-alpine3.24(static)The two alpine3.21 rows show the split is build-date, not Alpine version.
Why not just delete the block
An earlier revision of this PR removed the block entirely. That regressed OPcache on every build whose parent predates docker-library/php#1587 (currently 8.3.2, 8.3.15, 8.4.2) — verified by building 8.3.15 from that revision: no loader present,
extension_loaded("Zend OPcache")→false. The block was not "always redundant"; it was redundant only on post-merge parents.Fix
Guard the block so it adds
00-opcache-load.inionly when the parent has not already enabled OPcache andopcache.soexists:This is version- and date-agnostic: it keys off the actual presence of the parent's opcache ini.
Additional cleanup: drop dead
opcache.fast_shutdown8/conf/opcache.inisetopcache.fast_shutdown=1. That directive was removed in PHP 7.2 — fast shutdown became always-on and the option no longer exists. On PHP 8.x it is absent fromphpinfo()andini_get("opcache.fast_shutdown")returnsfalse, so the line is silently ignored dead config. The8/image only targets PHP 8.x, so it can never apply. Removed.Verification
Built from this branch (
--build-arg PHPVER=...,--target dist) and checked OPcache state:php -vwarningextension_loadedenable_cli=1)opcache.so)docker-php-ext-opcache.iniOPcache stays enabled on every build, the double-load warning is gone, and
opcache.inistill tunes it (it carries nozend_extensionline).Assisted-by: claude-code/claude-opus-4-8