parse_ini_file() 读不出带点键名是因为默认将点号视为分隔符转为下划线,且不支持嵌套;需传入 true 和 ini_scanner_raw 才能保留节结构和原始键名,避免自动类型转换。

parse_ini_file() 为什么读不出带点的键名
因为 parse_ini_file() 默认把 database.host 这类键名中的点号(.)当成分隔符,直接转成下划线(database_host),甚至可能丢弃整个键。这不是 bug,是它默认解析器的设计逻辑——它只认标准 INI 节([section])和简单键名。
要保留原始键名,必须显式指定 INI_SCANNER_RAW 模式,并配合 $process_sections = true:
$cfg = parse_ini_file('config.ini', true, INI_SCANNER_RAW);
- 不加
true:所有键扁平化,[db]和[cache]的内容混在一起 - 不加
INI_SCANNER_RAW:值里的on、off、123会被自动转类型,导致密码变成布尔或整数 - 数字开头的键(如
2fa.enabled)即使开了分组也会被跳过,只能靠正则逐行解析
INI 文件里写布尔值,PHP 会自动识别吗
仅当使用 INI_SCANNER_TYPED 模式时才会尝试转换,但非常有限:字符串 "true"、"on"、"yes" 变成 true;"false"、"off"、"no" 变成 false;"null" 变成 null。其余一律是字符串。
问题在于:你写的是 debug = on,但生产环境可能需要保持字符串语义(比如日志里记录原始配置值),这时自动转换反而破坏一致性。
- 推荐统一用
INI_SCANNER_RAW,在业务层做显式判断:filter_var($cfg['debug'], FILTER_VALIDATE_BOOLEAN) - 避免在 INI 中混用写法:
debug = 1和debug = on在TYPED模式下结果不同 -
INI_SCANNER_TYPED不处理引号包裹的值:debug = "on"仍为字符串
如何安全加载用户可控的 INI 配置文件
不能直接传用户输入给 parse_ini_file(),哪怕加了 INI_SCANNER_RAW —— 因为 INI 解析器会尝试展开已定义的常量(如 password = DB_PASS),而常量可能是敏感信息。
真正安全的做法是隔离上下文 + 白名单校验:
- 用
file_get_contents()读取原始内容,再用parse_ini_string()解析(PHP 5.3+),避免文件路径被注入 - 限制文件路径范围:
$path = __DIR__ . '/configs/' . preg_replace('/[^a-z0-9_\-]/', '', $user_input) . '.ini'; - 解析后立即过滤键名:
array_intersect_key($cfg, array_flip(['host', 'port', 'timeout'])) - 禁用常量解析:不要在调用前
define()任何值,尤其不要把数据库密码设成常量再让 INI 引用
php.ini 和应用级 config.ini 的根本区别
php.ini 是 PHP 解释器启动时加载的全局运行参数,影响所有脚本;而应用自己写的 config.ini 是业务配置,由 parse_ini_file() 按需加载,完全不参与 PHP 生命周期管理。
这意味着:
-
php.ini修改后必须重启 PHP-FPM 或 Web 服务器;config.ini修改后下次请求就生效(除非你加了 APCu 缓存) -
php.ini支持include_path、extension、memory_limit等底层指令;config.ini不能控制这些 - 别试图在
config.ini里写extension=redis.so——它只是普通文本,PHP 不会执行
最容易被忽略的一点:php.ini 的 include_path 设置会影响 parse_ini_file() 查找文件的位置,但多数人根本没意识到这个隐式依赖。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











