
本文介绍在 php 后端解析 shopify cdn 上非标准 javascript 格式的 currencies.js 文件的完整方案,通过字符串预处理将其转换为合法 json,再解码获取 rates 对象,并强调类型安全与健壮性处理。
本文介绍在 php 后端解析 shopify cdn 上非标准 javascript 格式的 currencies.js 文件的完整方案,通过字符串预处理将其转换为合法 json,再解码获取 rates 对象,并强调类型安全与健壮性处理。
Shopify 提供的 https://cdn.shopify.com/s/javascripts/currencies.js 并非标准 JSON 接口,而是一个导出全局 Currency 对象的 JavaScript 文件,内容形如:
var Currency = {
"rates": {
"USD": 1,
"EUR": 0.92,
"GBP": 0.79,
"JPY": 151.3,
// ...更多货币
},
convert: function(D,R,S){ return D * this.rates[R] / this.rates[S]; }
};
//# sourceMappingURL=/s/javascripts/currencies.js.map因此直接调用 json_decode($response->body(), true) 必然返回 null —— 因为它不是 JSON,而是 JS 源码。
✅ 正确解析流程(PHP 实现)
需三步完成:获取原始内容 → 清洗为类 JSON 字符串 → 安全解码
以下为生产就绪的 PHP 示例(兼容 Laravel Http Client 或原生 cURL):
use Illuminate\Support\Facades\Http;
try {
$response = Http::timeout(10)->get('https://cdn.shopify.com/s/javascripts/currencies.js');
if (!$response->successful()) {
throw new Exception("Failed to fetch currencies.js: HTTP {$response->status()}");
}
$jsContent = $response->body();
// Step 1: 移除声明头与尾部函数、注释等无关 JS 语法
$clean = trim(str_replace('var Currency = ', '', $jsContent));
$clean = preg_replace('/,\s*convert\s*:\s*function\([^)]*\)\s*\{[^}]*\}\s*;?/', '', $clean);
$clean = preg_replace('/\/\/# sourceMappingURL=[^\r\n]+/', '', $clean);
// Step 2: 将 JS 对象字面量转换为合法 JSON(关键:补全引号、修正结构)
$clean = trim($clean, " \t\n\r\0\x0B;{}");
$clean = '{' . $clean . '}'; // 确保外层为 JSON object
// Step 3: 安全修复键名和字符串值的引号(避免正则误伤小数点/数字)
// 更稳健的做法:逐行处理或使用 token 化,但此处采用高兼容性字符串替换策略
$clean = str_replace(['"', "\n", "\r", "\t"], ['\"', '', '', ''], $clean);
$clean = preg_replace('/([a-zA-Z_][a-zA-Z0-9_]*)\s*:/i', '"$1":', $clean); // 为未引号键名加双引号
$clean = preg_replace('/:\s*([0-9.]+)/', ':$1', $clean); // 保留数字原格式(不加引号)
// Step 4: 解码并验证
$data = json_decode($clean, true);
if (json_last_error() !== JSON_ERROR_NONE || !isset($data['rates']) || !is_array($data['rates'])) {
throw new Exception('Invalid or malformed currency data: ' . json_last_error_msg());
}
$rates = $data['rates'];
// ✅ 安全取值示例:强制转为 float,避免字符串参与计算
$usdRate = floatval($rates['USD'] ?? 1.0);
$eurRate = floatval($rates['EUR'] ?? 0.0);
$jpyRate = floatval($rates['JPY'] ?? 0.0);
// 可选:缓存该结果(如 Redis),设置 1 小时过期,避免频繁请求 CDN
// Cache::put('shopify_currency_rates', $rates, 3600);
} catch (\Exception $e) {
\Log::error('Currency parsing failed', ['error' => $e->getMessage()]);
// fallback to cached rates or default values
$rates = ['USD' => 1.0, 'EUR' => 0.92];
}⚠️ 重要注意事项
- 不要依赖硬编码字符串替换(如原答案中的多层 str_replace):易因 Shopify 微小格式变更而崩溃(例如新增字段、空格调整、注释位置变化)。推荐使用 preg_replace + 健壮正则,并始终校验 json_last_error()。
- 务必添加超时与异常处理:CDN 不稳定或响应慢将阻塞后端请求。
- 避免前端直连该 JS 文件:跨域限制(CORS)及执行环境风险使其不适合浏览器端解析;应统一由后端代理+清洗。
- 汇率精度与更新频率:Shopify 此文件约每小时更新一次,不适用于高频金融场景;如需实时汇率,请接入专业 API(如 ExchangeRate-API、Fixer)。
- 安全性提醒:切勿 eval() 或 create_function() 执行该 JS 内容——存在远程代码执行(RCE)风险。
✅ 总结
解析 currencies.js 的本质是「JS-to-JSON 转换」,核心在于剥离 JS 语法外壳、标准化对象结构、并严格验证输出。与其反复修补字符串规则,更可持续的做法是:
① 封装为可测试的 CurrencyParser 类;
② 添加单元测试覆盖常见变更(如新增货币、函数重命名);
③ 结合缓存与降级策略,保障服务韧性。
最终目标不是“能跑通”,而是“长期可靠”。










