PHP

PHP7源码系列-常用函数的实现

2019年4月11日

memory_get_usage()

memory_get_usage ([ bool $real_usage = false ] ) : int

PHP_FUNCTION(memory_get_usage) {
    zend_bool real_usage = 0;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &real_usage) == FAILURE) {
        RETURN_FALSE;
    }

    RETURN_LONG(zend_memory_usage(real_usage));
}

ZEND_API size_t zend_memory_usage(int real_usage)
{
#if ZEND_MM_STAT
    if (real_usage) {
        return AG(mm_heap)->real_size;
    } else {
        size_t usage = AG(mm_heap)->size;
        return usage;
    }
#endif
    return 0;
}

implode

#define ZEND_HASH_FOREACH_VAL(ht, _val) \
    ZEND_HASH_FOREACH(ht, 0); \
    _val = _z;

#define ZEND_HASH_FOREACH(_ht, indirect) do { \
        Bucket *_p = (_ht)->arData; \
        Bucket *_end = _p + (_ht)->nNumUsed; \
        for (; _p != _end; _p++) { \
            zval *_z = &_p->val; \
            if (indirect && Z_TYPE_P(_z) == IS_INDIRECT) { \
                _z = Z_INDIRECT_P(_z); \
            } \
            if (UNEXPECTED(Z_TYPE_P(_z) == IS_UNDEF)) continue;
PHP_FUNCTION(implode)
{
    zval *arg1, *arg2 = NULL, *arr;
    zend_string *delim;

    ZEND_PARSE_PARAMETERS_START(1, 2)
        Z_PARAM_ZVAL(arg1)
        Z_PARAM_OPTIONAL
        Z_PARAM_ZVAL(arg2)
    ZEND_PARSE_PARAMETERS_END();

    if (arg2 == NULL) {
        if (Z_TYPE_P(arg1) != IS_ARRAY) {
            php_error_docref(NULL, E_WARNING, "Argument must be an array");
            return;
        }

        delim = ZSTR_EMPTY_ALLOC();
        arr = arg1;
    } else {
        if (Z_TYPE_P(arg1) == IS_ARRAY) {
            delim = zval_get_string(arg2);
            arr = arg1;
        } else if (Z_TYPE_P(arg2) == IS_ARRAY) {
            delim = zval_get_string(arg1);
            arr = arg2;
        } else {
            php_error_docref(NULL, E_WARNING, "Invalid arguments passed");
            return;
        }
    }

    php_implode(delim, arr, return_value);
    zend_string_release(delim);
}

PHPAPI void php_implode(const zend_string *delim, zval *arr, zval *return_value)
{
    zval         *tmp;
    int           numelems;
    zend_string  *str;
    char         *cptr;
    size_t        len = 0;
    zend_string **strings, **strptr;
    // 获取数组长度
    numelems = zend_hash_num_elements(Z_ARRVAL_P(arr));

    // 长度为0 直接返回空字符串
    if (numelems == 0) {
        RETURN_EMPTY_STRING();

    // 长度为1 这里为什么用遍历?是因为HashTable中会有IS_UNDEF的Bucket
    // 应该返回非IS_UNDEF的元素值,宏ZEND_HASH_FOREACH_VAL会为我没解决这个问题
    } else if (numelems == 1) {
        /* loop to search the first not undefined element... */
        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) {
            // zval_get_string 函数会进行类型转换,无论zval是类型值都会转换为string
            RETURN_STR(zval_get_string(tmp));
        } ZEND_HASH_FOREACH_END();
    }


    strings = emalloc((sizeof(zend_long) + sizeof(zend_string *)) * numelems);
    strptr = strings - 1;

    // 计算需要为最后的str申请的内存大小
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) {
        // 
        if (Z_TYPE_P(tmp) == IS_LONG) {
            zend_long val = Z_LVAL_P(tmp);

            *++strptr = NULL;
            ((zend_long *) (strings + numelems))[strptr - strings] = Z_LVAL_P(tmp);
            if (val <= 0) {
                len++;
            }
            while (val) {
                val /= 10;
                len++;
            }
        } else {
            *++strptr = zval_get_string(tmp);
            len += ZSTR_LEN(*strptr);
        }
    } ZEND_HASH_FOREACH_END();

    /* numelems can not be 0, we checked above */
    // 向内存池申请一块非持久化的内存
    str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(delim), len, 0);
    cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
    *cptr = 0;

    do {
        if (*strptr) {
            cptr -= ZSTR_LEN(*strptr);
            memcpy(cptr, ZSTR_VAL(*strptr), ZSTR_LEN(*strptr));
            zend_string_release(*strptr);
        } else {
            char *oldPtr = cptr;
            char oldVal = *cptr;
            zend_long val = ((zend_long *) (strings + numelems))[strptr - strings];
            cptr = zend_print_long_to_buf(cptr, val);
            *oldPtr = oldVal;
        }

        cptr -= ZSTR_LEN(delim);
        // 添加delim
        memcpy(cptr, ZSTR_VAL(delim), ZSTR_LEN(delim));
    } while (--strptr > strings);

    if (*strptr) {
        memcpy(ZSTR_VAL(str), ZSTR_VAL(*strptr), ZSTR_LEN(*strptr));
        zend_string_release(*strptr);
    } else {
        char *oldPtr = cptr;
        char oldVal = *cptr;
        zend_print_long_to_buf(cptr, ((zend_long *) (strings + numelems))[strptr - strings]);
        *oldPtr = oldVal;
    }

    efree(strings);
    RETURN_NEW_STR(str);
}

explode

explode ( string $delimiter , string $string [, int $limit ] ) : array

limit
如果设置了 limit 参数并且是正数,则返回的数组包含最多 limit 个元素,而最后那个元素将包含 string的剩余部分。

如果 limit 参数是负数,则返回除了最后的 –limit 个元素外的所有元素。

如果 limit 是 0,则会被当做 1。

PHP_FUNCTION(explode)
{
    zend_string *str, *delim;
    zend_long limit = ZEND_LONG_MAX; /* No limit */
    zval tmp;

    ZEND_PARSE_PARAMETERS_START(2, 3)
        Z_PARAM_STR(delim)
        Z_PARAM_STR(str)
    // limit为可选参数
        Z_PARAM_OPTIONAL
        Z_PARAM_LONG(limit)
    ZEND_PARSE_PARAMETERS_END();

  // delim不能为空字符串
    if (ZSTR_LEN(delim) == 0) {
        php_error_docref(NULL, E_WARNING, "Empty delimiter");
        RETURN_FALSE;
    }

    // 初始化return_value
  // PHP_FUNCTION中 return_value 是一个预定义的特殊的变量zval
  // 函数不需要显示返回,默认返回return_value
    array_init(return_value);

    // str为空的情况
    if (ZSTR_LEN(str) == 0) {
        if (limit >= 0) {
      // 初始化为一个空字符串类型的zval  
            ZVAL_EMPTY_STRING(&tmp);
      // return_value[0] = ''  
            zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
        }
        return;
    }

    if (limit > 1) {
        php_explode(delim, str, return_value, limit);
    } else if (limit < 0) {
        php_explode_negative_limit(delim, str, return_value, limit);
    } else {
    // limit == 0 的情况
    // copy 一份str给tmp
    // 设置return_value[0] = tmp
        ZVAL_STR_COPY(&tmp, str);
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
    }
}

PHPAPI void php_explode(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
{
    char *p1 = ZSTR_VAL(str);
    char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
  // 查找下一个delim位置并返回指针
    char *p2 = (char *) php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
    zval  tmp;

  // 未找到
    if (p2 == NULL) {
        ZVAL_STR_COPY(&tmp, str);
    // 设置return_value
    // return_value[] = tmp
        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
    } else {
        do {
      // 取固定长的字符串
            ZVAL_STRINGL(&tmp, p1, p2 - p1);
      // 设置 return_value return_value[] = tmp
            zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
            p1 = p2 + ZSTR_LEN(delim);
      // 继续查找
            p2 = (char *) php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
        } while (p2 != NULL && --limit > 1); // 当p2不为空 并且 limit未达到时继续

        // 退出循环后,需要额外处理最后一个元素 
        if (p1 <= endp) {
            ZVAL_STRINGL(&tmp, p1, endp - p1);
            zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
        }
    }
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注