前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >smarty的replace陷阱

smarty的replace陷阱

作者头像
跑马溜溜的球
发布2020-12-07 15:33:28
1K0
发布2020-12-07 15:33:28
举报
文章被收录于专栏:日积月累1024

1. 问题

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

代码语言:javascript
复制
{assign var="star" value="胡哥;吴秀波;王宝强;三小只"}
{$star|regex_replace:'/;/':'/'}

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

代码语言:javascript
复制
{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

代码语言:javascript
复制
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中定义

代码语言:javascript
复制
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核心逻辑可以简化如下:

代码语言:javascript
复制
function smarty_mb_str_replace($search, $replace, $subject)
{
    $parts = mb_split($search, $subject);
    $subject = implode($replace, $parts);

    return $subject;
}

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

代码语言:javascript
复制
array(1) { [0]=> string(36) "胡哥;吴秀波;王宝强;三小只" }

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

解决方案

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

代码语言:javascript
复制
echo mb_internal_encoding();
echo mb_regex_encoding();

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

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

代码语言:javascript
复制
mb_regex_encoding('UTF-8');
  • 方法2:直接在php.ini中设置
代码语言:javascript
复制
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. 手册偶尔也不靠谱,还得看源码。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016/09/28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 问题
  • 2. 追踪
  • 解决方案
  • 怎么做更好
  • 结论
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档