数据类型 - 类型转换

 

前言

PHP弱语言,使用时不需要明确定义变量的类型。我大学时候接触都是C,C++,当自己去接触PHP的时候,就震惊不已,哇哇,啥啥,string 与 int 都可以相加,竟然还能通过去!!!!当时就对PHP底层对于数据操作这块很有兴趣,它到底有些啥奇技淫巧,能实现的这些不同类型之间的比较运算,算数运算。这里我们就要稍微讲一下,PHP两种方式:自动类型转换强制类型转换

自动类型转换

Zend虚拟机在执行PHP代码会根据具体的应用场景进行转换,比如$a = "100"+200, 执行时Zend发现相加的是字符串,这个时候会尝试将“100”转换成数值类型(整型或浮点类型),装换时候不会改变原有值,而是产生一个新的变量来处理。

强制类型转换

  • (int)(integer) : 转换为整型
  • (bool)(boolean):转换成布尔类型
  • (float)(double) (real) : 转换为浮点类型
  • (string): 转换为字符串
  • (array): 转换为数组
  • (object): 转换为对象
  • (unset): 转换为NULL

比较运算符下的类型转换

涉及到类型转换,最多两个运算情况,应该是比较运算算数运算,下面给在比较运算时,类型中情况,嘻嘻,这里就体现弱语言的一些不好的地方,但是通过强比较,过滤操作数据类型,这样代码习惯还是可以避免的~(PHP是世界上最好的语言,我也来自黑一下,哈哈)

先给出PHP在比较运算符下的类型转换规则下~  图片来源截图php.net

1.对于字符串和数值类型,进行比较操作,需要转换成数值类型来进行比较,辣么字符串是怎么转换成数值类型,可以往下看我的转换成数值类型~

2.对于字符串中出现“e”字符的特殊字符串

3.比较运算符的比较规则,特别要注意两个数字的字符串来进行比较,进行的是数字比较

4.PHP类型比较表,松散比较,严格比较

场景A : 正数开头,拥有e字符,后续都是数字的字符串 和 整数类型0 比较

 

场景B: 不含e这样的特殊字符串与正整数 0 

 

场景C :两个数字类型字符串,进行数字比较

场景D:PHP是否“0E”开头都解析为0来处理

0*10^111 = 0
   var_dump("0E111" == "0000"); // true 的确是true 是因为转换成了数值比较 两者都是0
   var_dump("0E111b" == 0 ); // true "0E111b"=>0*10^111 = 0 转换成了数值类型,所以两者相等
   var_dump("0E111b" == "0000"); //false 因为两者都是字符串进行的是词汇比较

所以不是所有“0E”开头字符串都解析为0来处理,而是看两者进行的是什么比较类型。

转换为NULL

//file zend_operators.c
 
 
ZEND_API void ZEND_FASTCALL convert_to_null(zval *op) /* {{{ */
{
   if (Z_TYPE_P(op) == IS_OBJECT) {
      if (Z_OBJ_HT_P(op)->cast_object) {
         //如果是转化是一个对象,且定义了handler,则调用
         zval org;
 
         ZVAL_COPY_VALUE(&org, op);
         if (Z_OBJ_HT_P(op)->cast_object(&org, op, IS_NULL) == SUCCESS) {
            zval_dtor(&org);
            return;
         }
         ZVAL_COPY_VALUE(op, &org);
      }
   }
   //销毁zval,将类型设置为NULL
   zval_ptr_dtor(op);
   ZVAL_NULL(op);
}

转换为布尔类型

当要转换成布尔类型时,根据原值的true,false决定结果,下面值被认为是false,其他情况被认为true,比如资源,对象。

  • 布尔值false
  • 整数类型 0 
  • 浮点类型 0.0
  • 空字符串,字符串 “0”
  • 空数组
  • NULL
//file zend_operators.c
 
 
ZEND_API int ZEND_FASTCALL zend_is_true(zval *op) /* {{{ */
{
   return i_zend_is_true(op);
}
 
 
static zend_always_inline int i_zend_is_true(zval *op)
{
   int result = 0;
 
again:
   switch (Z_TYPE_P(op)) {
      case IS_TRUE:
         result = 1;
         break;
      case IS_LONG:
         //非0既真
         if (Z_LVAL_P(op)) {
            result = 1;
         }
         break;
      case IS_DOUBLE:
         if (Z_DVAL_P(op)) {
            result = 1;
         }
         break;
      case IS_STRING:
         //非空字符串及“0”外都为true
         if (Z_STRLEN_P(op) > 1 || (Z_STRLEN_P(op) && Z_STRVAL_P(op)[0] != '0')) {
            result = 1;
         }
         break;
      case IS_ARRAY:
         //非空数组即为true
         if (zend_hash_num_elements(Z_ARRVAL_P(op))) {
            result = 1;
         }
         break;
      case IS_OBJECT:
         //默认返回true
         result = zend_object_is_true(op);
         break;
      case IS_RESOURCE:
         //合法资源返回true
         if (EXPECTED(Z_RES_HANDLE_P(op))) {
            result = 1;
         }
         break;
      case IS_REFERENCE:
         op = Z_REFVAL_P(op);
         goto again;
         break;
      default:
         break;
   }
   return result;
}

转换为整型,或者浮点类型

浮点类型的转换与整形差不多,所以这里合在一起了,单看整形的源码。其他类型转换为整型的规则

  • NULL :转为0
  • 布尔类型 :false 转换成0 true 转换为 1
  • 浮点类型: 向下取整
  • 字符串类型:采用C的strtol()函数规则,如果字符串以合法数值开始,则使用该数值,否则其值为0。合法数值由可选的正负号,后面跟着一个或多个数字,再跟着指数部分(比如我们前面E为底的指数)组成
  • 数组类型:非空数组转为1  空数组 0 
  • 对象类型:与数组类似
  • 资源类型:转为分配给这个资源的唯一编号
//file zend_operators.c
 
 
ZEND_API zend_long ZEND_FASTCALL _zval_get_long_func(zval *op) /* {{{ */
{
try_again:
   switch (Z_TYPE_P(op)) {
      case IS_NULL:
      case IS_FALSE:
         return 0;
      case IS_TRUE:
         return 1;
      case IS_RESOURCE:
         //资源转为zend_resource->handler
         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:
         //字符串转为strtol()处理 10代表着10进制
         return ZEND_STRTOL(Z_STRVAL_P(op), NULL, 10);
      case IS_ARRAY:
         //数组是否为空,转化0,1
         return zend_hash_num_elements(Z_ARRVAL_P(op)) ? 1 : 0;
      case IS_OBJECT:
         {
            zval dst;
            convert_object_to_type(op, &dst, IS_LONG, convert_to_long);
            if (Z_TYPE(dst) == IS_LONG) {
               return Z_LVAL(dst);
            } else {
               //默认为1
               return 1;
            }
         }
      case IS_REFERENCE:
         op = Z_REFVAL_P(op);
         goto try_again;
      EMPTY_SWITCH_DEFAULT_CASE()
   }
   return 0;
}
//strtol宏定义,就是在这里进行字符串的转化啦,对于数组开头的字符串这里将使用10进制来进行,转化,base取值范围为了2~36进制之间,或者是特殊值0
# define ZEND_STRTOL(s0, s1, base) strtol((s0), (s1), (base))

转换为字符串

下面是字符串转化的规则

  • NULL/False :转为空字符串
  • TRUE : 转为“1”
  • 整型:原样转为字符串,转换将各位依次除10取余
  • 浮点类型:原样转为字符串
  • 资源类型:转为“Resource id #xxx”
  • 数组:转为“Array”
  • 对象:不能转化
//file zend_operators.c
 
 
ZEND_API zend_string* ZEND_FASTCALL _zval_get_string_func(zval *op) /* {{{ */
{
try_again:
   switch (Z_TYPE_P(op)) {
      case IS_UNDEF:
      case IS_NULL:
      case IS_FALSE:
         return ZSTR_EMPTY_ALLOC();
      case IS_TRUE:
         if (CG(one_char_string)['1']) {
            return CG(one_char_string)['1'];
         } else {
            return zend_string_init("1", 1, 0);
         }
      case IS_RESOURCE: {
         char buf[sizeof("Resource id #") + MAX_LENGTH_OF_LONG];
         int len;
 
         len = snprintf(buf, sizeof(buf), "Resource id #" ZEND_LONG_FMT, (zend_long)Z_RES_HANDLE_P(op));
         return zend_string_init(buf, len, 0);
      }
      case IS_LONG: {
         return zend_long_to_str(Z_LVAL_P(op));
      }
      case IS_DOUBLE: {
         return zend_strpprintf(0, "%.*G", (int) EG(precision), Z_DVAL_P(op));
      }
      case IS_ARRAY:
         zend_error(E_NOTICE, "Array to string conversion");
         return zend_string_init("Array", sizeof("Array")-1, 0);
      case IS_OBJECT: {
         zval tmp;
         if (Z_OBJ_HT_P(op)->cast_object) {
            if (Z_OBJ_HT_P(op)->cast_object(op, &tmp, IS_STRING) == SUCCESS) {
               return Z_STR(tmp);
            }
         } else if (Z_OBJ_HT_P(op)->get) {
            zval *z = Z_OBJ_HT_P(op)->get(op, &tmp);
            if (Z_TYPE_P(z) != IS_OBJECT) {
               zend_string *str = zval_get_string(z);
               zval_ptr_dtor(z);
               return str;
            }
            zval_ptr_dtor(z);
         }
         zend_error(EG(exception) ? E_ERROR : E_RECOVERABLE_ERROR, "Object of class %s could not be converted to string", ZSTR_VAL(Z_OBJCE_P(op)->name));
         return ZSTR_EMPTY_ALLOC();
      }
      case IS_REFERENCE:
         op = Z_REFVAL_P(op);
         goto try_again;
      case IS_STRING:
         return zend_string_copy(Z_STR_P(op));
      EMPTY_SWITCH_DEFAULT_CASE()
   }
   return NULL;
}

未完之处

还有转换成数组,对象两种的源码,没有说明。对于字符串,布尔值,整型转成数组,都是变成数组的单个元素,对象变成数组,对象的属性变成数组的键名,同理对象也差不多是这样~ 哈哈,原谅我偷一点点的懒,有时间一定补一定补!!!每天起床都给自己打口气~

你可能感兴趣的:(读PHP7源码日记)