Skip to content

Name the ini file the runtime image actually ships - #375

Open
diolektor wants to merge 1 commit into
mainfrom
fix/docs-installation-image-tree-lists-ini-files-the-image-lacks
Open

diolektor wants to merge 1 commit into
mainfrom
fix/docs-installation-image-tree-lists-ini-files-the-image-lacks

Conversation

@diolektor

Copy link
Copy Markdown
Contributor

Problem

The "Image Structure" tree in docs/getting-started/installation.md describes the file layout of the runtime image, and its conf.d branch listed two files the image does not have:

├── etc/php/
│   └── conf.d/
│       ├── custom.ini                                # PHP settings for OxPHP
│       └── oxphp.ini                                # extension=oxphp_sapi.so

The release image enables the extension from extension.ini, and it ships no custom.ini at all — that one belongs to the development image, because production is meant to bring its own PHP configuration. Checked against the published ghcr.io/oxphp/oxphp:0.11.0 on both architectures:

$ docker run --rm --entrypoint ls ghcr.io/oxphp/oxphp:0.11.0 -la /usr/local/etc/php/conf.d/
-rw-r--r--  17  docker-php-ext-sodium.ini
-rw-r--r--  24  extension.ini

The names are the part of the tree a reader acts on. PHP reads conf.d in alphabetical order, so a file created under the name the documentation gave can sort on either side of the real one.

Two things the tree did not say came out of the same check. The image carries no php.ini of its own — /usr/local/etc/php/ holds only the upstream php.ini-development and php.ini-production samples, and php --ini reports Loaded Configuration File: (none). And both branches of the tree were silent about what the PHP base image contributes: the docker-php-ext-*.ini files beside extension.ini, and the extension .so files beside oxphp_sapi.so. In each case the set depends on the tag — php:8.4-zts-alpine3.23 carries docker-php-ext-opcache.ini and opcache.so, while the 8.5-based image does not, because OPcache is built in there.

What the page now says about load order

A reader who is told "put your settings in conf.d" needs two rules, and the second one is the reason this is worth more than a name swap.

Only names ending in .ini are read, in alphabetical order, and any other name is skipped without a word. This is a real failure mode for anyone mounting a ConfigMap key or a .conf file: the file is present, nothing complains, and the settings simply do not apply.

For an ordinary setting the last value read is the one that applies, so a file that overrides one has to sort after the file that sets it. But extension= and zend_extension= are the exception — PHP collects them from every file and loads them all, so a later extension= never replaces an earlier one. Stating the general rule without that exception would have been actively misleading here, because the two files in the tree contain nothing but extension= lines, and the page's own production example writes four more of them to conf.d/app-extensions.ini, which sorts before extension.ini. A reader applying "later replaces earlier" to what is sitting right in front of them would conclude their extensions get clobbered and rename the file for no reason.

Both rules were checked in the shipped image, not just read in the source:

# memory_limit = 111M in aaa-first.ini, 222M in zzz-last.ini  ->  222M
# values swapped between the two files                        ->  111M
# extension=sodium in the earlier file, extension=oxphp_sapi.so in the later
#   -> php -m lists both
# memory_limit in a .conf file and in an extensionless file
#   -> absent from php --ini, value stays at the built-in default

Component sizes

The size table in the same section was stale by about the same age. Measured in 0.11.0:

Component Was linux/arm64 linux/amd64 Now
oxphp ~8 MB 12 483 504 B 14 834 800 B ~12–15 MB
liboxphp_bridge.so ~50 KB 171 768 B 149 968 B ~150–170 KB
oxphp_sapi.so ~200 KB 712 544 B 720 728 B ~700 KB

The table now says the figures are from a named version and that they differ by architecture, so the next drift is visible rather than silent.

Scope

Docs only, no code. Every other mention of a conf.d file in docs/ was checked against both Dockerfiles and left alone: they all describe a file the user creates and mounts, where the name is the user's to choose. This section was the only place making a claim about what the runtime image contains.

The size budget in examples/dockerfile/Dockerfile and the "~188 MB → ~76 MB" figures in docs/getting-started/docker.md rest on the same stale numbers, but they are a self-consistent budget for that example's own prod stage and recomputing the total needs a build of it. Tracked separately rather than half-corrected here.

Verification

  • cargo fmt -- --check, cargo clippy --no-default-features -- -D warnings, cargo test --no-default-features — clean (no Rust in the diff; run because the check is unconditional)
  • scripts/gen-llms-txt.sh --check — up to date (58 pages); llms-full.txt is regenerated, not hand-edited
  • Every path in the documented tree exists in ghcr.io/oxphp/oxphp:0.11.0, and every conf.d and extensions/ entry the image has is represented in the tree
  • Load-order claims re-derived from main/php_ini.c at the pinned php-8.5.2 tag — php_scandir(..., php_alphasort), the .ini-only filter, zend_hash_update for directives, and the extension_lists path for extension= / zend_extension= — and confirmed by the in-image runs above

Docs:
  - The `conf.d` branch of the image's file-layout tree listed `custom.ini` and `oxphp.ini`; the image has neither — it enables the extension from `extension.ini` and ships no `custom.ini` at all, that one belongs to the development image. A reader who went by the tree to place or override PHP settings was handed names that sort differently from the real ones. Both branches of the tree now also show what the PHP base image contributes, in each case a set that depends on the tag, and a new paragraph says the image carries no `php.ini` of its own and states the rules a reader acts on: only names ending in `.ini` are read, in alphabetical order, any other name being skipped without a word, and for an ordinary setting the last value read is the one that applies — while `extension=` and `zend_extension=` are collected from every file and all loaded, so a later one never replaces an earlier one. That exception has to be stated because the tree's own two files contain nothing but `extension=` lines: without it, the one worked example sitting next to the rule is the case the rule does not cover.
  - The component size table in the same section was stale by the same age. Measured in the published `0.11.0` image on both architectures, the three artefacts are ~12–15 MB, ~150–170 KB and ~700 KB, against ~8 MB, ~50 KB and ~200 KB as published.
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