
本文详解如何在 PHP 后端安全、可靠地提取 cdn.shopify.com/s/javascripts/currencies.js 中的汇率对象(rates),解决因脚本非标准 JSON 格式导致 json_decode() 返回 null 的常见问题。
本文详解如何在 php 后端安全、可靠地提取 `cdn.shopify.com/s/javascripts/currencies.js` 中的汇率对象(`rates`),解决因脚本非标准 json 格式导致 `json_decode()` 返回 `null` 的常见问题。
Shopify 提供的 currencies.js 并非纯 JSON 文件,而是一个定义全局 Currency 对象的 JavaScript 模块,其内容形如:
var Currency = {
"rates": {
"USD": 1.0,
"EUR": 0.9245,
"GBP": 0.7891,
// ...更多货币对
},
convert: function(D,R,S){ return D * this.rates[R] / this.rates[S]; }
};
//# sourceMappingURL=/s/javascripts/currencies.js.map直接调用 json_decode() 失败的根本原因在于:该内容包含 JS 变量声明、函数定义、注释及无引号键名等非 JSON 语法,必须先清洗为合法 JSON 字符串。
以下是在 Laravel(或支持 Http::get() 的现代 PHP 环境)中推荐的健壮解析方案:
✅ 推荐清洗与解析流程(PHP)
use Illuminate\Support\Facades\Http;
$response = Http::get('https://www.php.cn/link/748129f3c8c47e317738dc6ffc11cdd1');
if (!$response->successful()) {
throw new RuntimeException('Failed to fetch currencies.js: ' . $response->status());
}
$body = $response->body();
// 步骤 1:移除 var 声明和末尾的函数/注释
$body = trim(str_replace('var Currency = ', '', $body));
$body = preg_replace('/,\s*convert\s*:\s*function\([^)]*\)\s*\{[^}]*\}\s*;\s*\/{2}# sourceMappingURL=[^;]+;?/', '', $body);
$body = preg_replace('/\s*\/{2}# sourceMappingURL=[^\r\n]+/', '', $body);
// 步骤 2:将 JS 对象字面量转换为标准 JSON(关键:补全引号)
// 使用正则安全处理键名(仅处理无引号的纯字母数字键,如 "rates" → "\"rates\"")
$body = preg_replace('/([{\s,])([a-zA-Z_][a-zA-Z0-9_]*)\s*:/U', '$1"$2":', $body);
// 修复值中的字符串(如 "USD")已带引号,无需改动;数值保持原样
// 步骤 3:确保首尾为花括号,并清理空白
$body = trim($body);
if (substr($body, 0, 1) !== '{') $body = '{' . $body;
if (substr($body, -1) !== '}') $body = $body . '}';
// 步骤 4:解析 JSON
$currencyData = json_decode($body, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new RuntimeException('Invalid JSON after sanitization: ' . json_last_error_msg());
}
$rates = $currencyData['rates'] ?? [];⚠️ 注意事项与最佳实践
- 不要硬编码字符串替换:原文中多次 str_replace 易受格式变更影响(如 Shopify 更新换行或空格)。应优先使用正则(如 preg_replace)提升鲁棒性。
- 务必校验响应状态:CDN 可能返回 404 或 5xx,需显式检查 $response->successful()。
- 缓存至关重要:该文件更新频率低(通常数小时/天),建议本地缓存(如 Redis 或文件),避免高频请求触发限流或增加延迟。
-
类型安全处理数值:$rates['USD'] 可能是字符串(如 "1.0000"),建议统一转为浮点数:
$usdRate = floatval($rates['USD'] ?? 1.0);
-
替代方案更优?
如业务允许,前端通过 window.Currency.rates 直接读取是最轻量方式;若需服务端计算(如订单结算),可考虑 Shopify Admin API 的 /admin/api/{version}/shop.json(含 money_format)或自建汇率同步服务,规避解析 JS 的脆弱性。
✅ 验证示例
// 成功解析后,可安全访问 echo $rates['EUR']; // 输出类似 0.9245 echo gettype($rates['EUR']); // string 或 float,取决于是否 floatval()
总之,解析 currencies.js 的核心在于:识别其 JS 模块本质 → 精准剥离非 JSON 冗余 → 规范化键名引号 → 严格校验 JSON 有效性。遵循上述步骤,即可稳定获取实时汇率数据,支撑多币种业务逻辑。










