IT技术博客大学习 共学习 共进步

利用短信模板提取短信变量

两双筷子 2026-06-03 09:03:23 累计浏览 2 次
本机暂存

前段时间工作中遇到了一个字符串变量提取的场景,通过正则实现了,感觉实现思路蛮有意思的,就分享一下。

前言

对接过国内短信发送平台的开发者,应该知道大部分短信平台都需要使用 "短信模板"来发送短信。既:在短信服务提供商那边申请一个模板,在发送短信时只需要传递模板中的变量即可。

例如短信内容是:"您的验证码是:123456,5分钟内有效,请勿泄露"。

在短信平台中模板格式是:"您的验证码是:${code},${time}分钟内有效,请勿泄露"

我们只需要向短信平台传递 {"code":123456,"time":5} 这样一段请求参数,再由短信平台将参数替换为短信正文,最后发送到用户手机上。

需求

我们的业务系统本身需要对接多个短信平台,有的短信平台要求直接使用短信模板,有的短信平台要求直接传递短信原文。

为了统一短信组件的调用,我们在业务中统一向短信发送组件传递短信原文,再由短信发送组件根据不同短信平台要求,拆出短信变量。

利用模板变量

尝试通过正则实现这个功能,步骤如下:

  1. 通过匹配变量标签,提取出模板中所以的 key,拿到一个由 key 组成的一维数组
  2. 接着将模板中变量标签部分的内容,用正则替换成另一个正则表达式,用这个正则提取短信原文中的值
  3. 最后将 第一步的数组 key 和第二步的数组 value 组合成一个新的数组,实现短信变量的提取。

使用一个相对复杂的例子,实现代码如下:

<?php
$template = '尊敬的${name}你好,您的订单${on}将于${y}年${m}月${d}过期,请及时通过${pay}续费,支付金额:${money} ${unit}。';
$content = '尊敬的张三你好,您的订单144514将于2023年8月21过期,请及时通过支付宝续费,支付金额:2.34 RMB。';

// {"name":"张三","on":"144514","y":"2023","m":"8","d":"21","pay":"支付宝","money":"2.34","unit":"RMB"}
echo json_encode(GetTempParam($template,$content),JSON_UNESCAPED_UNICODE);

/**
 * @param $template 第三方短信平台模板
 * @param $content 需要发送的短信原文
 * @param $identifier 第三方平台变量标签
 * @return array 返回变量数组
 */
function GetTempParam($template, $content, $identifier = ['${', '}'])
{
    /**
     * 模板编写注意:变量不能连续、不可嵌套、若包含正则语法 需要转义(自动) 例如:"尊敬的\[$name\]您好"
     * 模板: 尊敬的${name}您好,您的订单${on}将于${y}年${m}月${d}过期,请及时通过${pay}续费,支付金额:${money} ${unit}。
     * 内容:尊敬的张三您好,您的订单114514将于2023年8月21过期,请及时通过支付宝续费,支付金额:2.33 RMB。
     * 标签:['${', '}']
     */
    // 标签转义为 ['\${', '}']
    $identifier = array_map('quotemeta', $identifier);
    
    // 第一步:使用 /(?<=\${)(.*?)(?=})/ 提取变量 keys ['name','on','y' ...]
    $pattern = sprintf('/(?<=%s)(.*?)(?=%s)/', $identifier[0], $identifier[1]);
    preg_match_all($pattern, $template, $temp_keys);
    // 如果没找到 key 就没有变量
    if (empty($temp_keys[1])) return [];
    /**
     * 第二步:将模板标签替换为 (.*),结果当作正则匹配内容
     * $content_pattern = 尊敬的(.*)你好,您的订单(.*)将于(.*)年(.*)月(.*)过期,请及时通过(.*)续费,支付金额:(.*) (.*)。
     * $content_vals[0] = ['张三','114514','2023' ...] ,注意 若 模板不匹配 则为 null
     */
    $content_pattern = preg_replace(sprintf('/%s(.*?)%s/', $identifier[0], $identifier[1]), '(.*)', $template);
    preg_match_all('/' . $content_pattern . '/', $content, $content_vals, PREG_SET_ORDER);
    // 如果为 0 就是没匹配上 大概率是内容错了
    if (empty($content_vals[0])) {
        return false;
    }
    // 去掉多余的匹配内容
    array_shift($content_vals[0]);
    // ["name"=>"张三","on"=>"114514","y"=>"2023"...]
    return array_combine($temp_keys[1], $content_vals[0]);
}

建议继续学习

  1. 正则表达式 — QQ微信、优酷前端 邮箱正则表达式验证 Bug (累计阅读 6,582)
  2. grep 正则表达式选项要记得转义 (累计阅读 6,520)
  3. 学习Grep,Sed中的正则 (累计阅读 5,341)
  4. URL正则表达式 (累计阅读 4,720)
  5. 前端性能优化之Html压缩 (累计阅读 4,681)
  6. 利用vim(gvim)的正则表达式实现代码自动匹配完成(等号两边数据交换) (累计阅读 4,601)
  7. 正则表达式简要入门 (累计阅读 4,440)
  8. 正则转义符汇总 (累计阅读 4,380)
  9. 使用Oracle正则表达式监控应用到数据库的连接情况 (累计阅读 4,340)
  10. bash shell - sed及awk文本捕获及替换 (累计阅读 4,240)