美文网首页
php底层rtrim的一个“bug”

php底层rtrim的一个“bug”

作者: d866c6045d74 | 来源:发表于2017-10-19 15:10 被阅读75次

    php底层rtrim的一个“bug”

    背景

    trim系列函数是用于去除字符串中首尾的空格或其他字符。ltrim函数只去除掉字符串首部的字符,rtrim函数只去除字符串尾部的字符。

    string trim ( string $str [, string $character_mask = " \t\n\r\0\x0B" ] )
    

    看一个例子:

    $str = "e?type";
    
    echo $str;
    echo "\n";
    
    echo rtrim($str, '?type');
    echo "\n";
    

    不知道别人怎么想,我觉得这个返回的应该是”e”吧。

    但是实际上返回了一个空的字符串。

    又是一个黑魔法吗?

    源码

    看一下php的底层实现:

    image.png

    这个是php7 trim系列函数的源码,红框内就是rtrim的代码。

    image.png

    这个是php_charmask的源码。

    执行步骤

    trim执行步骤

    trim、ltrim、rtrim三个函数都是调用了php_do_trim函数,区别在于第二个参数mode的不同。本文主要对trim函数进行分析,ltrim和rtrim函数跟trim的类似。然后php_do_trim会调用了php_trim来实现功能,因此trim函数的核心函数时php_trim函数。其执行步骤如下:

    1. 根据what的值设置保存过滤字符的mask数组
    2. 过滤在字符串首部的待过滤字符
    3. 过滤在字符串尾部的待过滤字符

    php_trim函数执行的流程图如下:

    image.png

    源码解读

    php_trim函数先调用了php_charmask,这个函数试将过滤字符设置为mask[char] = 1的形式,这样就是一个哈希数组,然后可用于后面的判断。如果第二个参数是范围值时,调用了memset函数给mask数组赋值。

    根据源码提炼出了以下的小demo:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void php_charmask(unsigned char *input, size_t len, char *mask);
    char *rtrim(char *str,char *character_mask);
    
    int main(int argc, char const *argv[])
    {
        printf("%s\n",rtrim("e?type","?type"));    
        return 0;
    }
    
    char *rtrim(char *str,char *character_mask)
    {
        char *res;
        char mask[256];
        register size_t i;
        
        size_t len = strlen(str);
        
        php_charmask((unsigned char*)character_mask, strlen(character_mask), mask);
        
        if (len > 0) {
            i = len - 1;
            do {
                if (mask[(unsigned char)str[i]]) {
                    len--;
                } else {
                    break;
                }
            } while (i-- != 0);
        }
        
        res = (char *) malloc(sizeof(char) * (len+1));
        memcpy(res,str,len);
        
        return res;
    }
    
    void php_charmask(unsigned char *input, size_t len, char *mask)
    {
        unsigned char *end;
        unsigned char c;
        
        memset(mask, 0, 256);
        
        for (end = input+len; input < end; input++) {
            c = *input;
            mask[c]= 1;
        }
    }
    

    在php_charmask中,构造了一个简单的hash数组

    image.png

    在最上面的例子中,?type分别落到了这个数组中的不同位置(这也就是为什么mask的大小要设置成256,和ASCII的数量一致)。

    所以在最上面的例子中,从后往前e,p,y,t,?,e都命中了hash。

    结论

    其实这也不算一个bug,文档说的很清楚,trim系列函数是字符级别的,可是惯性就让我们以为是字符串级别的,以后想清楚了再用。

    相关文章

      网友评论

          本文标题:php底层rtrim的一个“bug”

          本文链接:https://www.haomeiwen.com/subject/ywwuuxtx.html