前段时间工作中遇到了一个字符串变量提取的场景,通过正则实现了,感觉实现思路蛮有意思的,就分享一下。
前言
对接过国内短信发送平台的开发者,应该知道大部分短信平台都需要使用 "短信模板"来发送短信。既:在短信服务提供商那边申请一个模板,在发送短信时只需要传递模板中的变量即可。
例如短信内容是:"您的验证码是:123456,5分钟内有效,请勿泄露"。
在短信平台中模板格式是:"您的验证码是:${code},${time}分钟内有效,请勿泄露"
我们只需要向短信平台传递 {"code":123456,"time":5}
这样一段请求参数,再由短信平台将参数替换为短信正文,最后发送到用户手机上。
需求
我们的业务系统本身需要对接多个短信平台,有的短信平台要求直接使用短信模板,有的短信平台要求直接传递短信原文。
为了统一短信组件的调用,我们在业务中统一向短信发送组件传递短信原文,再由短信发送组件根据不同短信平台要求,拆出短信变量。
利用模板变量
尝试通过正则实现这个功能,步骤如下:
- 通过匹配变量标签,提取出模板中所以的 key,拿到一个由 key 组成的一维数组
- 接着将模板中变量标签部分的内容,用正则替换成另一个正则表达式,用这个正则提取短信原文中的值
- 最后将 第一步的数组 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]);
}