In PHP, how does parse_ini_file() handle sections and INI_SCANNER_TYPED when loading staging and production database settings from one file?
answer
- second argument keeps the sections
- without it, later keys overwrite
- on/yes/true become "1" in normal mode
- INI_SCANNER_TYPED gives bool, null, int
- quote values with special characters
basics
~20 sPass true as the second argument to get one sub-array per [section]; otherwise sections are flattened and later keys overwrite earlier ones. INI_SCANNER_TYPED returns real bool, null, int and float values instead of strings like "1" and "".
solid answer
~40 s`parse_ini_file(string $filename, bool $process_sections = false, int $scanner_mode = INI_SCANNER_NORMAL): array|false` reads an INI file into an array, returning `false` on failure (a syntax error also raises an `E_WARNING`). With `$process_sections = false`, `[staging]` and `[production]` are ignored and a `host` key in production overwrites staging's; pass `true` to get `$ini['staging']['host']`. In the default normal mode, unquoted `on`, `yes`, `true` become `"1"` and `off`, `no`, `false`, `none`, `null` become `""`, and numbers stay strings. `INI_SCANNER_TYPED` turns those words into `true`, `false` and `null` and unquoted numbers into `int` or `float`. Quote passwords: characters like `!`, `&`, `|`, `(` and `"` have special meaning, and an unquoted password `no` becomes empty.
code
php · 12 lines<?php
declare(strict_types=1);
$env = getenv('APP_ENV') ?: 'staging';
$ini = parse_ini_file(__DIR__ . '/../config/database.ini', true, INI_SCANNER_TYPED);
if ($ini === false || !isset($ini[$env])) {
throw new RuntimeException("No database section for {$env}");
}
$db = $ini[$env];
var_dump($db['port']); // int(5432)
var_dump($db['ssl']); // bool(true) in productiongo deeper
Recall that parse_ini_file($path, true) keeps [sections] as sub-arrays and that it returns false when the file cannot be parsed.
Explain the three scanner modes, what normal mode does to on/off/null and numbers, and why passwords must be quoted.
Design a per-environment config load that validates the section and types, keeps secrets out of the committed file through ${...} interpolation, and stores the file outside the document root.
Decide whether INI files still earn their place next to PHP files returning arrays or environment-only config, weighing readability for operators against type safety and caching.
## The function `parse_ini_file()` reads a file written in the same syntax as `php.ini` and returns its settings as an array. It has nothing to do with PHP's own configuration — php.ini has already been processed before the script runs — so it is simply a parser for your application's config files. Its signature in `ext/standard/basic_functions.stub.php`: `parse_ini_file(string $filename, bool $process_sections = false, int $scanner_mode = INI_SCANNER_NORMAL): array|false` It returns `false` on failure; a syntax error also produces an `E_WARNING` describing the problem. A relative `$filename` is resolved against the current working directory and then `include_path`, so build the path from `__DIR__` to avoid surprises when a cron job runs from another directory. ## Sections: the second argument Consider this file: ```ini [staging] host = db-staging.internal port = 5432 ssl = off [production] host = db-prod.internal port = 5432 ssl = on ``` - `parse_ini_file($path)` ignores the section headers and puts every key into one flat array. `host` appears twice, so the **later** value wins: you get production's host even when you wanted staging. - `parse_ini_file($path, true)` returns `['staging' => [...], 'production' => [...]]`, and you select `$ini[$env]` where `$env` comes from something like `getenv('APP_ENV')`. ## Scanner modes: the third argument | Value in file | `INI_SCANNER_NORMAL` (default) | `INI_SCANNER_TYPED` | `INI_SCANNER_RAW` | |---|---|---|---| | `on`, `yes`, `true` | `"1"` | `true` | the literal text | | `off`, `no`, `false`, `none` | `""` | `false` | the literal text | | `null` | `""` | `null` | the literal text | | `5432` | `"5432"` | `5432` (`int`) | `"5432"` | The source converts unquoted numeric values in typed mode to `int`, or to `float` for decimals — slightly more than the manual's wording, which mentions integers only. In normal and typed modes, quoting a value keeps it a string: `password = "no"` stays `"no"`. **Typed mode** is what a modern codebase with `declare(strict_types=1)` wants: `ssl = off` becomes `false`, so `if ($db['ssl'])` works, and `port` can go straight into an `int` parameter without a cast. **Raw mode** skips value parsing: no boolean words, no constants, no escape handling. The manual recommends it when parsing a file you do not fully trust, because normal mode substitutes the values of PHP constants and `${...}` references. ## Special characters and credentials INI values are not free text: - characters `?{}|&~!()^"` have special meaning in values and may not appear in keys; - a value containing non-alphanumeric characters should be wrapped in double quotes; - the words `null`, `yes`, `no`, `true`, `false`, `on`, `off`, `none` cannot be used as keys; - `${NAME}` in a value is replaced by the environment variable or configuration option `NAME`, and a bare constant name is replaced by that constant's value. For database passwords this means: **always quote them**. An unquoted `password = s3cret!(x)` can fail to parse, and an unquoted `password = no` becomes an empty string in normal mode. The `${DB_PASSWORD}` form lets the INI file hold the structure while the secret itself comes from the environment. ## Keeping the file safe - Store it **outside the document root**: web servers typically serve `.ini` files as plain text, credentials included. - Commit a template (for example `config.ini.dist`) without real secrets, and deploy the real file separately. - Validate after parsing: check that the section for the current environment exists and that required keys are present and of the expected type.
- Why did a staging script connect to the production database after reading database.ini?It called `parse_ini_file()` without `true` as the second argument. Sections are then ignored and all keys land in one flat array, so the `host` from `[production]`, which appears later in the file, overwrites the one from `[staging]`. Pass `true` and select `$ini['staging']`.
- When would you pick INI_SCANNER_RAW over INI_SCANNER_TYPED?When the file may contain text you do not fully control, or values that must stay exactly as written. Raw mode does not interpret boolean words, constants or escape sequences, so values come back as written. You then convert types yourself.
saying these in an interview costs you the question
- parse_ini_file() keeps sections by default
- In normal mode, ssl = off comes back as the boolean false
- INI_SCANNER_TYPED converts quoted values too
- parse_ini_file() throws an exception on a syntax error
- An .ini file in the web root is safe because PHP does not execute it