
本文介绍如何从 acmephp.phar 命令行输出的非结构化字符串中,精准提取每个域名对应的 HTTP-01 挑战所需的 token 和 payload,通过正则匹配 + JSON 解析实现可靠、可扩展的字符串归一化处理。
本文介绍如何从 acmephp.phar 命令行输出的非结构化字符串中,精准提取每个域名对应的 http-01 挑战所需的 `token` 和 `payload`,通过正则匹配 + json 解析实现可靠、可扩展的字符串归一化处理。
在使用 ACME PHP(如 acmephp.phar)进行手动 HTTP-01 挑战验证时,命令执行返回的是混合了日志文本与嵌入式 JSON 的长字符串。这类输出不具备标准结构,无法直接 json_decode() 全局解析,但其中包含多个格式统一的 JSON 片段(每个对应一个域名的挑战信息),位于 Solving authorization challenge for domain {…} 后。为后续自动化创建 .well-known/acme-challenge/ 文件或发起 HTTP 验证请求,必须将这些片段结构化提取。
✅ 推荐方案:正则定位 + JSON 解析
核心思路是:
- 使用正则表达式 /{[^}]*}}/ 安全捕获所有闭合的 JSON 对象(注意:ACME 输出中 JSON 总是成对大括号包裹且无嵌套,故该模式足够鲁棒);
- 对每个匹配结果调用 json_decode(),提取 challenge->token 和 challenge->payload;
- 以域名($data->domain)为键组织结果,便于后续按域操作。
以下是完整、生产就绪的 PHP 实现:
<?php // 执行 ACME 授权命令并获取原始输出
$authorizeOutput = shell_exec("php acmephp.phar authorize example1.com example2.com example3.com 2>&1");
if ($authorizeOutput === null || trim($authorizeOutput) === '') {
throw new RuntimeException('ACME authorization command failed or returned empty output.');
}
// 正则匹配所有独立的 JSON 对象(贪婪匹配至最邻近的 }})
$matches = [];
preg_match_all('/{[^}]*}}/', $authorizeOutput, $matches);
if (empty($matches[0])) {
throw new RuntimeException('No challenge JSON objects found in ACME output. Check command success and output format.');
}
$domains = [];
foreach ($matches[0] as $jsonStr) {
$data = json_decode($jsonStr, false);
if (!$data || !isset($data->domain, $data->challenge->token, $data->challenge->payload)) {
continue; // 跳过解析失败或字段缺失的项
}
$domains[$data->domain] = [
'token' => (string)$data->challenge->token,
'payload' => (string)$data->challenge->payload,
];
}
// 结构化结果可用于后续操作,例如:
foreach ($domains as $domain => $info) {
$filePath = "/var/www/{$domain}/.well-known/acme-challenge/{$info['token']}";
file_put_contents($filePath, $info['payload']);
echo "✅ Written challenge file for {$domain}: {$filePath}
";
}
⚠️ 注意事项与最佳实践
- 安全性:shell_exec() 存在命令注入风险,务必确保传入的域名已严格校验(如白名单、filter_var($domain, FILTER_VALIDATE_DOMAIN));
- JSON 健壮性:ACME 输出中 JSON 片段不含换行或嵌套对象,因此 /\{[^}]*\}\}/ 是安全的。若未来版本引入嵌套,应改用 PCRE 的递归模式或流式 JSON 解析器;
- 错误处理:示例中加入了空输出、无匹配、JSON 解析失败等基础防护,实际项目中建议配合日志记录(如 error_log());
- 编码兼容性:确保 PHP 环境支持 UTF-8,避免 json_decode() 因编码问题返回 null;
- 性能考量:对于大量域名(>50),正则匹配效率仍优异,无需优化;若需极致性能,可考虑逐行扫描 + 状态机解析。
通过该方法,您即可将原本难以处理的 CLI 日志字符串,归一化为清晰、可编程的关联数组,无缝对接后续的文件写入、HTTP 请求或证书签发流程。











