美文网首页
zend_string_dup 也有坑?

zend_string_dup 也有坑?

作者: Bun_Wong | 来源:发表于2016-07-15 20:50 被阅读107次

    今天在写 Azalea 的时候,无意中遇到一个奇怪的 bug,代码片段如下

    $testModel = $this->getModel('test');
    $testModel->test();
    var_dump('test');
    

    getModel 是 Controller 方法用于获取 Model,$testModel 确实已经获取回来了,但是 $testModel->test() 出错了,同时 var_dump('test') 输出了 "Test",基本上定位到了 getModel方法中的把传入名称首字母大写而影响了 Model 类名的位置

    zend_string *modelName, *name, *modelClass;
    
    if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "S", &modelName) == FAILURE) {
        return;
    }
    
    name = zend_string_dup(modelName, 0);  // 复制一份传入的名称
    ZSTR_VAL(name)[0] = toupper(ZSTR_VAL(name)[0]);  // 首字母大写
    modelClass = strpprintf(0, "%sModel", ZSTR_VAL(name));  // 连接生成类名
    zend_string_release(name);  // 释放 name
    

    乍看没发现什么问题,最大嫌疑是 zend_string_dup 方法,跟踪进入源代码,

    static zend_always_inline zend_string *zend_string_dup(zend_string *s, int persistent)
    {
        if (ZSTR_IS_INTERNED(s)) {
            return s;
        } else {
            return zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), persistent);
        }
    }
    

    问题就出在 ZSTR_IS_INTERNED 宏,它判断了 zend_stirng 结构中的 gc.u.v.flags 值是否等于 IS_STR_INTERNED,即字符串是否为内部的。
    因为 modelName 是指向参数栈传入的第一个字符串,因此并不会产生 zend_string_init

    ===== 更新 =====
    这个坑罪魁祸首是 opcache,启动了 opcache 之后缓存了传入字符串 "test" 是放到了同一个内存块里,即 getModel('test')var_dump('test'),都是同一个 "test",如果禁用了 opcache 不会踩到该坑。

    相关文章

      网友评论

          本文标题:zend_string_dup 也有坑?

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