PHP 소스 코드 - INTVAL 기능 소스 코드 분석

PHP 소스 코드 - INTVAL 기능 소스 코드 분석

PHP에서 INTVAL

intval ( mixed $var [, int $base = 10 ] ) : int
  • 의 역할은 정수 값으로 변수를 변환하는 것입니다. 두 번째 인자 $base사용을 많이하지 않습니다. 그것은 사용하는 바이너리 변환을 나타냅니다. 기본값은 10 진수입니다
  • 다음의 간단한 예제를 사용하는 방법을 이해합니다 :
$var1 = '123';
$var2 = '-123';
$var3 = [1, 2, ];
$var4 = [-1, 2, ];
var_dump(
    intval($var1),
    intval($var2),
    intval($var3),
    intval($var4)
);
// 输出如下:
// int(-123)
// int(1)
// int(1)
  • 이 기능은 기능의 100 아웃에서 선택하지만, 때때로에없는 LeetCode 브러시 타이틀 을 얻을 질문 알고리즘의 문자열을 숫자로 변환 할 수있는 아이디어를 공격, PHP는 그것이 그것의 바닥에서 구현되는 방법, INTVAL있다?

INTVAL 달성 소스

  • 기능 INTVAL 위치 php-7.3.3/ext/standard/type.c, 당신은 할 수 보려면 클릭
  • 기능 많은 소스 코드를 직접 게시 :
PHP_FUNCTION(intval)
{
    zval *num;
    zend_long base = 10;

    ZEND_PARSE_PARAMETERS_START(1, 2)
        Z_PARAM_ZVAL(num)
        Z_PARAM_OPTIONAL
        Z_PARAM_LONG(base)
    ZEND_PARSE_PARAMETERS_END();

    if (Z_TYPE_P(num) != IS_STRING || base == 10) {
        RETVAL_LONG(zval_get_long(num));
        return;
    }


    if (base == 0 || base == 2) {
        char *strval = Z_STRVAL_P(num);
        size_t strlen = Z_STRLEN_P(num);

        while (isspace(*strval) && strlen) {
            strval++;
            strlen--;
        }

        /* Length of 3+ covers "0b#" and "-0b" (which results in 0) */
        if (strlen > 2) {
            int offset = 0;
            if (strval[0] == '-' || strval[0] == '+') {
                offset = 1;
            }

            if (strval[offset] == '0' && (strval[offset + 1] == 'b' || strval[offset + 1] == 'B')) {
                char *tmpval;
                strlen -= 2; /* Removing "0b" */
                tmpval = emalloc(strlen + 1);

                /* Place the unary symbol at pos 0 if there was one */
                if (offset) {
                    tmpval[0] = strval[0];
                }

                /* Copy the data from after "0b" to the end of the buffer */
                memcpy(tmpval + offset, strval + offset + 2, strlen - offset);
                tmpval[strlen] = 0;

                RETVAL_LONG(ZEND_STRTOL(tmpval, NULL, 2));
                efree(tmpval);
                return;
            }
        }
    }

    RETVAL_LONG(ZEND_STRTOL(Z_STRVAL_P(num), NULL, base));
}
  • 사용자 상태의 관점 PHP 상기 INTVAL 함수 프로토 타입 파라미터 입력 참조에서 $var가변형 mixed수단, 상기 입력 파라미터는 플라스틱, 스트링, 어레이, 및 다른 목적을 포함 PHP 임의의 타입 일 수있다. 따라서, 사용하는 상기 수신 된 입력 파라미터는 소스에 직접 zvalzval *num;

진수 케이스

  • 소스는 대부분의 컨텐츠가 아닌, 소수의 치료를위한 것입니다. 경우 10 진수의 초점에서 살펴 보자. 데이터는 10 진수 정수로 변환 할 때, 다음과 같이 소스 완료 처리 :
if (Z_TYPE_P(num) != IS_STRING || base == 10) {
    RETVAL_LONG(zval_get_long(num));
    return;
}

static zend_always_inline zend_long zval_get_long(zval *op) {
    return EXPECTED(Z_TYPE_P(op) == IS_LONG) ? Z_LVAL_P(op) : zval_get_long_func(op);
}

ZEND_API zend_long ZEND_FASTCALL zval_get_long_func(zval *op) /* {{{ */
{
    return _zval_get_long_func_ex(op, 1);
}
  • 만큼 들어오는 데이터는 정수의 경우 아니므로, 다음의 소스 코드는 결국 호출합니다 _zval_get_long_func_ex(op, 1);. 이 기능 경우 여기서 파라미터 PHP 사용자 모드의 다양한 종류의 처리 :
switch (Z_TYPE_P(op)) {
    case IS_UNDEF:
    case IS_NULL:
    case IS_FALSE:
        return 0;
    case IS_TRUE:
        return 1;
    case IS_RESOURCE:
        return Z_RES_HANDLE_P(op);
    case IS_LONG:
        return Z_LVAL_P(op);
    case IS_DOUBLE:
        return zend_dval_to_lval(Z_DVAL_P(op));
    case IS_STRING:
        // 略 ……
    case IS_ARRAY:
        return zend_hash_num_elements(Z_ARRVAL_P(op)) ? 1 : 0;
    case IS_OBJECT:
        // 略 ……
    case IS_REFERENCE:
        op = Z_REFVAL_P(op);
        goto try_again;
    EMPTY_SWITCH_DEFAULT_CASE()
}
  • 스위치 문의 다른 지점으로 다른 유형에 대해 서로 다른 다양한 트리트먼트를 할 수 :
    • 입력 형식이 "NULL"인 경우,이 함수는 직접 INTVAL 0을 반환하는 단계;
    • true의 경우, 반환 1
    • 배열이 빈 상태 (empty)의 배열 인 경우 0을 반환, 비는 하늘의 배열을 반환 1
    • 문자열 경우, 상기 처리
    • ......
  • 우리가 볼 수있는 문자열에 초점을 맞출 수 있도록이 문서의 원래 의도에 따르면, 정수 데이터에 어떻게 문자열을 살펴 보는 것입니다 :
{
    zend_uchar type;
    zend_long lval;
    double dval;
    if (0 == (type = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &lval, &dval, silent ? 1 : -1))) {
        if (!silent) {
            zend_error(E_WARNING, "A non-numeric value encountered");
        }
        return 0;
    } else if (EXPECTED(type == IS_LONG)) {
        return lval;
    } else {
        /* Previously we used strtol here, not is_numeric_string,
         * and strtol gives you LONG_MAX/_MIN on overflow.
         * We use use saturating conversion to emulate strtol()'s
         * behaviour.
         */
         return zend_dval_to_lval_cap(dval);
    }
}
static zend_always_inline zend_uchar is_numeric_string(const char *str, size_t length, zend_long *lval, double *dval, int allow_errors) {
    return is_numeric_string_ex(str, length, lval, dval, allow_errors, NULL);
}

static zend_always_inline zend_uchar is_numeric_string_ex(const char *str, size_t length, zend_long *lval, double *dval, int allow_errors, int *oflow_info)
{
    if (*str > '9') {
        return 0;
    }
    return _is_numeric_string_ex(str, length, lval, dval, allow_errors, oflow_info);
}

ZEND_API zend_uchar ZEND_FASTCALL _is_numeric_string_ex(const char *str, size_t length, zend_long *lval, double *dval, int allow_errors, int *oflow_info) { // ... }
  • 이 논리에서, 최선의 구현은 문자열 차례 성형 알고리즘은 여전히 숨겨져 is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &lval, &dval, silent ? 1 : -1)의 함수 함수 호출 뒤에_is_numeric_string_ex
  • 문자열의 경우, 다음과 같이 우리의 규칙은 일반적으로 형성으로 변환됩니다 :
    • 등의 공백, 줄 바꿈, 탭, 포함하여 앞의 공백 문자를 제거
    • 제대로 앞의 문자열 처리 +/-기호
    • 앞의 처리 '0'와 같은 문자열의 문자, '001a'성형에 변환 후의을이고 1상기 이외에, '0'캐릭터
    • 처음 몇 나머지 값의 문자열 처리는 숫자 문자열 및 폐기 숫자가 아닌 문자입니다. 소위 디지털 문자, 그 '0'-'9'문자

빈 기호 거래

  • 다음과 같이 처리 소스 코드는 다음과 같습니다
while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r' || *str == '\v' || *str == '\f') {
    str++;
    length--;
}
  • \n, \t, \r다음은 좀 더 사용합니다. \v그것은 수직 탭을 말하며 \f페이지 나누기입니다. 이 공백의 관점에서, 치료하지, 건너 뛸. 그런 다음 포인터 연산을 사용하여 str++다음 문자로 지점

양, 음의 부호 거래

  • 필요하므로 수치, 그리고 의미에서 플러스 또는 마이너스 기호 때문에이 유지 될하지만, 숫자 값의 +수는 생략 할 수있다 :
if (*ptr == '-') {
    neg = 1;
    ptr++;
} else if (*ptr == '+') {
    ptr++;
}

문자의 수를 건너 뛰기 0

  • 십진수 값을 이전 값 0이 때문에 무의미하고 있기 때문에 생략 할 필요 :
while (*ptr == '0') {
    ptr++;
}
  • 문자가 인수에 삼가지 경우 위의를 처리 한 후, 하나 하나는 정수로 변환됩니다. 첫 문자 번호가 하이 레벨로 이송되기 때문에되도록 이전 문자의 계산에서 이전에 필요한 값 *10연산. 예를 들면 :
    • 스트링의 231aa첫 번째는 문자 트래버스 '2'가 값 TMP 임시 변수로서 저장 될 때
    • 두 번째 패스는 '3', 필요가있다 *10, 즉이 tmp * 10 + 3때 TMP (23)의 값
    • 세 번째 패스 '1', 필요성 tmp * 10 + 1이 때, TMP (231)의 값.
  • 따라서, 소스 문자 숫자 문자가 결정된다 : ZEND_IS_DIGIT(*ptr)다음, 상기와 같은 방법으로 계산된다

    • 매크로가됩니다 달성 ZEND_IS_DIGIT ((c) >= '0' && (c) <= '9')위치 '0''9'우리가 숫자를 찾을 필요 사이에 문자를.

상황 진수

  • _is_numeric_string_exPHP 기능이 포함됩니다 하단에 여러 기능이라고합니다 floatval. 당신은 소수점 발생 문자의 문자열을 이송하는 경우에는 어떻게 처리합니까? 우리가 달성하고자하기 때문에보기의 개인 요점은, intval내가 소수점의 얼굴, 그것이 다루는 숫자가 아닌 문자로 취급 될 수 있다고 생각 있도록하는 기능. 예를 들어, "3.14abc"직접 문자열 3, INTVAL 후. 그러나 실제로 _is_numeric_string_ex는 일반적인 기능이기 때문에 구현은 사실이 아니다. 소수점의 얼굴에서 특별한 치료가 있습니다 :
  • 소수점가 발생하는 경우, C는에, 점프 점프 고토 될 것입니다 process_double:
process_double:
    type = IS_DOUBLE;

    /* If there's a dval, do the conversion; else continue checking
     * the digits if we need to check for a full match */
    if (dval) {
        local_dval = zend_strtod(str, &ptr);
    } else if (allow_errors != 1 && dp_or_e != -1) {
        dp_or_e = (*ptr++ == '.') ? 1 : 2;
        goto check_digits;
    }
  • _is_numeric_string_ex 마지막 기능은 플로트 수익을 얻을 것이다 :
if (dval) {
    *dval = local_dval;
}

return IS_DOUBLE;
  • 부동 소수점 번호에 할당 된 dval포인터. 식별 및 데이터가 IS_DOUBLE돌아왔다.
  • 그런 다음 실행 스택 기능을 다시 점프 _zval_get_long_func_ex계속, 즉 return zend_dval_to_lval_cap(dval);. 이 함수는 다음과 같이 정의된다 :
static zend_always_inline zend_long zend_dval_to_lval_cap(double d)
{
    if (UNEXPECTED(!zend_finite(d)) || UNEXPECTED(zend_isnan(d))) {
        return 0;
    } else if (!ZEND_DOUBLE_FITS_LONG(d)) {
        return (d > 0 ? ZEND_LONG_MAX : ZEND_LONG_MIN);
    }
    return (zend_long)d;
}
  • 즉, 기본이의 주조 하였다 정수 결과 유형으로 부동 소수점입니다 (zend_long)d.

발문

  • 패키지의 수많은 작은 조각을 기초 PHP 로직, 코드 재사용의 정도를 크게 향상시켰다. 그러나 일부 추가 비용 유지 및 학습 소스를 제공합니다. 함수 호출의 10 개 이상의 종류의 수행에 형식 변환 기능.
  • 다음으로, 관련 INTVAL 기본 연습을 확장 할 것입니다. 계속 지켜봐 주시기 바랍니다.
  • 당신이 더 나은 아이디어가 있다면, 나는 의견과 제안을 환영합니다.

추천

출처www.cnblogs.com/ishenghuo/p/11803114.html