PHP 소스 코드 - INTVAL 기능 소스 코드 분석
- 출처 : https://github.com/suhanyujie/learn-computer/
- 저자 : suhanyujie
- PHP 7.3.3을 기반으로
PHP에서 INTVAL
- 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'
우리가 숫자를 찾을 필요 사이에 문자를.
- 매크로가됩니다 달성 ZEND_IS_DIGIT
상황 진수
_is_numeric_string_ex
PHP 기능이 포함됩니다 하단에 여러 기능이라고합니다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 기본 연습을 확장 할 것입니다. 계속 지켜봐 주시기 바랍니다.
- 당신이 더 나은 아이디어가 있다면, 나는 의견과 제안을 환영합니다.