smarty的replace陷阱

    xiaoxiao2023-03-25  5

    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_replace0.183sreplace0.191s

    regex_replace胜出了!

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

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

    结论

    如果php安装了mbstring扩展,在smarty模板中进行字符替换时,推荐使用regex_replace。未安装,则使用replace。直觉这东西,有时挺不靠谱的,还得看实验。手册偶尔也不靠谱,还得看源码。
    转载请注明原文地址: https://ju.6miu.com/read-1203675.html
    最新回复(0)