- 文章來源: github.com/suhanyujie/…
- 做者:suhanyujie
- 基於PHP 7.3.3
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)
複製代碼
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));
}
複製代碼
$var
變量類型是 mixed
,這也就意味着,輸入參數能夠是 PHP 中的任意一種類型,包括整形、字符串、數組、對象等。所以,在源碼中直接使用 zval 接收輸入參數 zval *num;
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()
}
複製代碼
經過 switch 語句的不一樣分支對不一樣類型作了各類不一樣的處理:php
按照本文的初衷,就是要了解一下如何將字符串轉化爲整形數據,所以咱們着重看字符串的狀況:git
{
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++;
}
複製代碼
while (*ptr == '0') {
ptr++;
}
複製代碼
處理完以上的 3 種狀況後,就會對接下里的字符逐個轉換爲整數。因爲最早遍歷到的字符數字是處於高位的,因此在計算下一個字符前,須要對以前的數值 *10
操做。舉例說明:github
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_ex
函數在底層會被多種 PHP 函數調用,包括 floatval
。若是在遍歷字符串的字符時,遇到小數點該如何處理呢?我的觀點看,因爲咱們要實現的是 intval
函數,因此我以爲遇到小數點時,能夠將其看成非數字字符來處理。例如 "3.14abc"
字符串,intval 以後就直接是 3。然而實際上,_is_numeric_string_ex
的實現不是這樣的,由於它是一個通用函數。在遇到小數點時,有一些特殊處理: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
。