[php-src] Issue #22133: API to obtain the compiled-in default value of a PHP INI setting
[email protected] (sebastianbergmann) Sat, 23 May 2026 04:53:49 +0000
| Newsgroups | php.bugs |
|---|---|
| Message-ID | <[email protected]> |
Issue: https://github.com/php/php-src/issues/22133
Author: sebastianbergmann
### Description
Provide an API to obtain the compiled-in default value of a PHP INI setting (the value that would be in effect if no `php.ini`, no additional scanned INI files, and no `-d`/`ini_set()` overrides were applied) from within a running PHP process, without having to spawn a child process.
## Motivation
A program sometimes needs to know which INI settings differ from the value a fresh PHP process would use, for example to faithfully reproduce the current configuration in a sub-process via `-d key=value`. PHPUnit relies on this when running tests in separate processes, and the `sebastianbergmann/environment` component provides `Runtime::getCurrentSettings()` for exactly this purpose.
The information PHP exposes today is not sufficient to do this reliably:
- `ini_get($name)` returns the current value (after `php.ini`, scanned INI files, `-d`, and `ini_set()`).
- `ini_get_all($extension, true)` additionally exposes `global_value` (the
"master" value) and `local_value`. However, `global_value` already reflects values set in `php.ini`, in additionally scanned INI files, and via `-d`. It is therefore not the compiled-in default.
- `get_cfg_var()` reads from the INI files as well.
There is no API that returns the value a setting would have with no configuration applied, i.e. the value built into the engine/extension via `PHP_INI_ENTRY`/`STD_PHP_INI_ENTRY` (the registered default) or, for settings registered with `OnModify` semantics, the module's default.
## Current workaround and why it is problematic
The only known way to obtain the compiled-in defaults is to spawn a child process with all configuration disabled and serialize its INI table:
```php
$process = proc_open(
[PHP_BINARY, '-n', '-r', 'echo json_encode(ini_get_all(null, true));'],
[1 => ['pipe', 'w']],
$pipes,
);
$stdout = stream_get_contents($pipes[1]);
proc_close($process);
$defaults = json_decode($stdout, true);
```
This works, but spawning a child process has a serious side effect: `proc_open()` (like `popen()`, `exec()`, `shell_exec()`, `system()`) forks the current process. Extensions that register `pthread_atfork()` handlers run those handlers on every fork. The gRPC extension, for instance, reacts to the fork by attempting to re-initialize its event engine and writes diagnostics to `STDERR`:
```
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
E0000 00:00:1779463505.812250 1517 ev_posix.cc:132] No event engine could be initialized from ...
```
This output is emitted in the parent process (and/or in the forked child before `execve()` replaces the image), so it cannot be suppressed by redirecting the child's file descriptors. In a test runner that is configured to be strict about output it fails the entire run. See <https://github.com/sebastianbergmann/environment/issues/97>.
In short: a purely informational query ("what is the default value of this setting?") currently requires forking the process, and forking has process-global, extension-dependent side effects that the caller cannot control or avoid.
## Proposed API
A function that returns the compiled-in default for one or all settings without spawning a process. For example:
```php
/**
* Returns the compiled-in default value of $name, or null when the
* setting is unknown. The returned value ignores php.ini, additionally
* scanned INI files, -d command-line overrides, and ini_set() changes.
*/
function ini_get_default(string $name): ?string {}
```
or, mirroring `ini_get_all()`, an additional column on the existing structure:
```php
$all = ini_get_all(null, true);
// $all['precision'] === [
// 'global_value' => '14',
// 'local_value' => '14',
// 'default_value' => '14', // <-- new: compiled-in default
// 'access' => 7,
// ];
```
The engine already knows these defaults: each INI entry is registered with its default string (`PHP_INI_ENTRY3`/`STD_PHP_INI_ENTRY` and friends), and that default is applied before configuration files are parsed. Exposing it is primarily a matter of recording the registered default alongside the current master/local values.
## Alternatives considered
- Parsing the INI files in-process and comparing. This detects values set in `php.ini`/scanned files, but cannot account for settings left at their compiled-in default, nor for `-d` overrides, which is precisely the gap that motivated the child-process approach.
- Suppressing the child's output. Not possible from PHP: the offending output is written by an extension's atfork handler to the process's real file descriptor 2 before any redirection set up for the child takes effect.
- Gating the child process on specific extensions (e.g. skipping it when `extension_loaded('grpc')` is true). This would be fragile: it special-cases one extension, silently reduces fidelity, and does not cover other extensions that install fork-hostile handlers.
A first-class API would remove the need to fork at all for this use case and would make the result correct and side-effect-free for every extension.