
本文介绍一种健壮、易维护的方法,将类似配置文件结构的文本(如 `host { ... }` 块)逐行解析为嵌套 php 数组,支持读取、修改与后续序列化操作。
在实际开发中,当程序受限于历史原因将结构化数据以纯文本形式(非 JSON/YAML/INI)存储时,我们需要一种可靠的方式将其“结构化还原”。你提供的配置片段具有清晰的块级语义:每个 Host { ... } 包含若干 Key = Value 形式的属性行。直接使用 strpos/substr 进行字符串匹配容易出错(如嵌套括号、空格不一致、注释干扰等),而基于按行解析 + 状态机逻辑的方法更稳定、可读性更强。
以下是一个生产就绪的解析函数:
function hostsToArray(string $filepath): array
{
// 读取文件并预处理:去除空行、首尾空白
$lines = array_map('trim', file($filepath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES));
$hosts = [];
$currentHostIndex = -1; // 初始化为 -1,首次遇到 "Host {" 时置为 0
$inHostBlock = false;
foreach ($lines as $line) {
if ($line === 'Host {') {
$currentHostIndex++;
$hosts[$currentHostIndex] = [];
$inHostBlock = true;
continue;
}
if ($line === '}') {
$inHostBlock = false;
continue;
}
if ($inHostBlock && strpos($line, '=') !== false) {
// 安全分割:仅按第一个 '=' 切分,避免值中含 '=' 导致错误(如 URL)
$parts = explode(' = ', $line, 2);
if (count($parts) === 2) {
[$key, $value] = $parts;
$hosts[$currentHostIndex][trim($key)] = trim($value);
}
}
// 忽略非 Host 块内的其他行(如注释、空行已过滤,此处可扩展日志警告)
}
return $hosts;
}✅ 使用示例:
$hosts = hostsToArray('/etc/Program/Hosts.conf');
print_r($hosts);
// 输出:
// Array (
// [0] => Array ( [Name] => test1 [Address] => 192.168.0.1 [Port] => 8080 )
// [1] => Array ( [Name] => test2 [Address] => 192.168.0.2 [Port] => 8080 )
// )⚠️ 关键注意事项:
- 安全性: 此函数不执行 eval() 或 create_function(),完全避免代码注入风险;
- 健壮性: 使用 explode(' = ', $line, 2) 限定分割次数,兼容值中含 = 的场景(如 URL = http://example.com/path?k=v);
- 可扩展性: 若需支持嵌套结构(如 Group { Host { ... } }),可改用栈式状态机;当前设计专注解决单层 Host 块;
- 写回支持: 解析为数组后,可轻松修改 $hosts[0]['Name'] = 'prod-server',再通过自定义 arrayToHostsFile() 函数格式化写回磁盘。
? 提示:若未来需高频读写,建议将此解析逻辑封装为 HostConfig 类,添加缓存、校验(如必填字段 Name, Address)、以及原子写入(file_put_contents(..., LOCK_EX))等工业级特性。











