smarty的replace陷阱

来源:互联网 发布:襄阳seo 编辑:程序博客网 时间:2024/05/17 21:56

1. 问题

为描述方便,我们简化下问题。

{assign var="star" value="胡哥;吴秀波;王宝强;三小只"}{$star|regex_replace:'/;/':'/'}

在smarty模板中,将“;”(半角分号)替换为“/”。在看这段代码时,第一反应是用replace替代regex_replace,效率会高些。于是动手改了一行代码:

{assign var="star" value="胡哥;吴秀波;王宝强;三小只"}{$star|replace:';':'/'}

测试无误,上线!

上线后问题来了,线上环境中的”;”居然没有被替换为”/”!无奈回滚。

2. 追踪

smarty手册说到:replace等同与php函数的str_replace。所以首先怀疑是php版本问题,但一个replace,真会和php版本有关系么?于是分别在两个环境上直接尝试用php的str_replace做上文的字符替换,都没有问题。

看来smarty的replace实现并不是直接调用了php的str_replace,只能读smarty源码定位问题了。

replace的实现位于Smarty/plugins/modifier.replace.php

function smarty_modifier_replace($string, $search, $replace){    if (Smarty::$_MBSTRING) {        require_once(SMARTY_PLUGINS_DIR . 'shared.mb_str_replace.php');        return smarty_mb_str_replace($search, $replace, $string);    }    return str_replace($search, $replace, $string);}

其中Smarty :: $_MBSTRING在./Smarty.class.php中定义

define('SMARTY_MBSTRING', function_exists('mb_split'))

逻辑很清晰了,当安装了mbstring扩展时,使用smarty_mb_str_replace进行替换,否则用php的str_replace进行替换(是谁说equivalent to the PHP’s str_replace() function来着…可以枪毙一会儿)。

我的php有mbstring扩展,只能继续跟进了。
smarty_mb_str_replace核心逻辑可以简化如下:

function smarty_mb_str_replace($search, $replace, $subject){    $parts = mb_split($search, $subject);    $subject = implode($replace, $parts);    return $subject;}

先用待替换字符切分源串,再用目标字符拼接得到结果串。不得不说这个实现思路有点…好吧,吐槽的事暂时放放,先追问题。
debug发现,问题出在mb_split,在线上环境(出问题的环境)中,此处我们得到的$parts结果为

array(1) { [0]=> string(36) "胡哥;吴秀波;王宝强;三小只" }

字串没有被切为预期的四部分。what’s wrong!

解决方案

受php手册mb_split例子的启发(还是php手册靠谱),想到可能是编码问题导致。在问题环境测试

echo mb_internal_encoding();echo mb_regex_encoding();

得到的结果居然是EUC-JP!一个日文字符集。我堂堂天朝公司的线上php版本居然默认字符集是日文…好在哥不是反日愤青,不然必格盘而后快。

知道问题所在就好解决了。
- 方法1:在php执行smarty前设置

mb_regex_encoding('UTF-8');
  • 方法2:直接在php.ini中设置
mbstring.internal_encoding = UTF-8

然后重启php-fpm让配置生效。

怎么做更好

继续看smarty源码,regex_replace最终是使用php的preg_replace实现。介于replace的无语实现方法,二者哪个快还真不一定,实测下吧。我们每次测试者渲染模板1000次,测5次取均值,实验结果如下:

modifier 耗时 regex_replace 0.183s replace 0.191s

regex_replace胜出了!

直接用php的str_replace,自己实现一个modifier会怎么样呢?
采用上面同样的测试方法,得到的结果是0.179s,比regex_replace只是略有提高。

综合考虑,regex_replace不依赖环境,不用额外代码,速度也还好,性价比最高。

结论

  1. 如果php安装了mbstring扩展,在smarty模板中进行字符替换时,推荐使用regex_replace。未安装,则使用replace。
  2. 直觉这东西,有时挺不靠谱的,还得看实验。
  3. 手册偶尔也不靠谱,还得看源码。
0 0
原创粉丝点击