有极速快乐十分吗|极速快乐十分走势图|

PHP內核探索:變量類型的轉換

隱式類型轉換與強制類型轉換
服務器君一共花費了273.770 ms進行了7次數據庫查詢,努力地為您提供了這個頁面。
試試閱讀模式?希望聽取您的建議

PHP是弱類型的動態語言,在前面的章節中我們已經介紹了PHP的變量都存放在一個名為ZVAL的容器中, ZVAL包含了變量的類型和各種類型變量的值。 PHP中的變量不需要顯式的數據類型定義,可以給變量賦值任意類型的數據, PHP變量之間的數據類型轉換有兩種:隱式和顯式轉換。

隱式類型轉換

隱式類型轉換也被稱為自動類型轉換,是指不需要程序員書寫代碼,由編程語言自動完成的類型轉換。 在PHP中,我們經常遇到的隱式轉換有:

1.直接的變量賦值操作

在PHP中,直接對變量的賦值操作是隱式類型轉換最簡單的方式,也是我們最常見的一種方式,或許我們已經習以為常,從而沒有感覺到變量的變化。 在直接賦值的操作中,變量的數據類型由賦予的值決定,即左值的數據類型由右值的數據類型決定。 比如,當把一個字符串類型的數據賦值給變量時,不管該變量以前是什么類型的變量,此時該變量就是一個字符串類型的變量。 看一段代碼:

$string = "To love someone sincerely means to love all the people,  to love the world and life,  too."
$integer = 10;
$string = $integer;

上面的代碼,當執行完第三行代碼,$string變量的類型就是一個整形了。 通過VLD擴展可以查到第三次賦值操作的中間代碼及操作數的類型,再找到賦值的最后實現為zend_assign_to_variable函數。 這在前面的小節中已經詳細介紹過了。我們這個例子是很簡單的一種賦值,在源碼中是直接將$string的ZVAL容器的指針指向$integer變量指向的指針, 并將$integer的引用計數加1。這個操作在本質上改變了$string變量的內容,而原有的變量內容則被垃圾收集機制回收。關于賦值的具體細節,請返回上一節查看。

2.運算式結果對變量的賦值操作

我們常說的隱式類型轉換是將一個表達式的結果賦值給一個變量,在運算的過程中發生了隱式的類型轉換。 這種類型轉換不僅僅在PHP語言,在其它眾多的語言中也有見到,這是我們常規意義上的隱式類型轉換。 這種類型轉換又分為兩種情況:

  • 表達式的操作數為同一數據類型 這種情況的作用以上面的直接變量的類型轉換是同一種情況,只是此時右值變成了表達式的運算結果。
  • 表達式的操作數不為同的數據類型 這種情況的類型轉換發生在表達式的運算符的計算過程中,在源碼中也就是發生在運行符的實現過程中。

看一個字符串和整數的隱式數據類型轉換:

<?php
$a = 10;
$b = 'a string ';
 
echo $a . $b;
?>

上面例子中字符串連接操作就存在自動數據類型轉化,$a變量是數值類型,$b變量是字符串類型, 這里$b變量就是隱式(自動)的轉換為字符串類型了。通常自動數據類型轉換發生在特定的操作上下文中, 類似的還有求和操作"+"。具體的自動類型轉換方式和特定的操作有關。 下面就以字符串連接操作為例說明隱式轉換的實現:

腳本執行的時候字符串的連接操作是通過Zend/zend_operators.c文件中的如下函數進行:

ZEND_API int concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
{           
        zval op1_copy, op2_copy;
        int use_copy1 = 0, use_copy2 = 0;
 
        if (Z_TYPE_P(op1) != IS_STRING) { 
                zend_make_printable_zval(op1, &op1_copy, &use_copy1);
        }           
        if (Z_TYPE_P(op2) != IS_STRING) { 
                zend_make_printable_zval(op2, &op2_copy, &use_copy2);
        }       
        // 省略
}

可用看出如果字符串鏈接的兩個操作數如果不是字符串的話, 則調用zend_make_printable_zval函數將操作數轉換為"printable_zval"也就是字符串。

ZEND_API void zend_make_printable_zval(zval *expr, zval *expr_copy, int *use_copy)
{
    if (Z_TYPE_P(expr)==IS_STRING) {
        *use_copy = 0;
        return;
    }
    switch (Z_TYPE_P(expr)) {
        case IS_NULL:
            Z_STRLEN_P(expr_copy) = 0;
            Z_STRVAL_P(expr_copy) = STR_EMPTY_ALLOC();
            break;
        case IS_BOOL:
            if (Z_LVAL_P(expr)) {
                Z_STRLEN_P(expr_copy) = 1;
                Z_STRVAL_P(expr_copy) = estrndup("1", 1);
            } else {
                Z_STRLEN_P(expr_copy) = 0;
                Z_STRVAL_P(expr_copy) = STR_EMPTY_ALLOC();
            }
            break;
        case IS_RESOURCE:
            // ...省略
        case IS_ARRAY:
            Z_STRLEN_P(expr_copy) = sizeof("Array") - 1;
            Z_STRVAL_P(expr_copy) = estrndup("Array", Z_STRLEN_P(expr_copy));
            break;
        case IS_OBJECT:
                // ... 省略
        case IS_DOUBLE:
            *expr_copy = *expr;
            zval_copy_ctor(expr_copy);
            zend_locale_sprintf_double(expr_copy ZEND_FILE_LINE_CC);
            break;
        default:
            *expr_copy = *expr;
            zval_copy_ctor(expr_copy);
            convert_to_string(expr_copy);
            break;
    }
    Z_TYPE_P(expr_copy) = IS_STRING;
    *use_copy = 1;
}

這個函數根據不同的變量類型來返回不同的字符串類型,例如BOOL類型的數據返回0和1, 數組只是簡單的返回Array等等,類似其他類型的數據轉換也是類型, 都是根據操作數的不同類型的轉換為相應的目標類型。在表達式計算完成后,表達式最后會有一個結果, 這個結果的數據類型就是整個表達式的數據類型。當執行賦值操作時,如果再有數據類型的轉換發生, 則是直接變量賦值的數據類型轉換了。

顯式類型轉換(強制類型轉換)

在前面介紹了隱式類型轉換,在我們的日常編碼過程也會小心的使用這種轉換, 這種不可見的操作可能與我們想象中的不一樣,如整形和浮點數之間的轉換。 當我們是一定需要某個數據類型的變量時,可以使用強制的數據類型轉換,這樣在代碼的可讀性等方面都會好些。 在PHP中的強制類型轉換和C中的非常像:

<?php
$double = 20.10;
echo (int)$double;
?>

PHP中允許的強制類型有:

  • (int), (integer) 轉換為整型
  • (bool), (boolean) 轉換為布爾類型
  • (float), (double) 轉換為浮點類型
  • (string) 轉換為字符串
  • (array) 轉換為數組
  • (object) 轉換為對象
  • (unset) 轉換為NULL

在Zend/zend_operators.c中實現了轉換為這些目標類型的實現函數convert_to_*系列函數, 讀者自行查看這些函數即可,這些數據類型轉換類型中有一個我們比較少見的unset類型轉換:

ZEND_API void convert_to_null(zval *op) /* {{{ */
{
    if (Z_TYPE_P(op) == IS_OBJECT) {
        if (Z_OBJ_HT_P(op)->cast_object) {
            zval *org;
            TSRMLS_FETCH();
 
            ALLOC_ZVAL(org);
            *org = *op;
            if (Z_OBJ_HT_P(op)->cast_object(org, op, IS_NULL TSRMLS_CC) == SUCCESS) {
                zval_dtor(org);
                return;
            }
            *op = *org;
            FREE_ZVAL(org);
        }
    }
 
    zval_dtor(op);
    Z_TYPE_P(op) = IS_NULL;
}

轉換為NULL非常簡單,對變量進行析構操作,然后將數據類型設為IS_NULL即可。 可能讀者會好奇(unset)$a和unset($a)這兩者有沒有關系,其實并沒有關系, 前者是將變量$a的類型變為NULL,這只是一個類型的變化,而后者是將這個變量釋放,釋放后當前作用域內該變量及不存在了。

除了上面提到的與C語言很像,在其它語言中也經常見到的強制數據轉換,PHP中有一個極具PHP特色的強制類型轉換。 PHP的標準擴展中提供了兩個有用的方法settype()以及gettype()方法,前者可以動態的改變變量的數據類型, gettype()方法則是返回變量的數據類型。在ext/standard/type.c文件中找到settype的實現源碼:

PHP_FUNCTION(settype)
{
    zval **var;
    char *type;
    int type_len = 0;
 
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zs", &var, &type, &type_len) == FAILURE) {
        return;
    }
 
    if (!strcasecmp(type, "integer")) {
        convert_to_long(*var);
    } else if (!strcasecmp(type, "int")) {
        convert_to_long(*var);
    } else if (!strcasecmp(type, "float")) {
        convert_to_double(*var);
    } else if (!strcasecmp(type, "double")) { /* deprecated */
        convert_to_double(*var);
    } else if (!strcasecmp(type, "string")) {
        convert_to_string(*var);
    } else if (!strcasecmp(type, "array")) {
        convert_to_array(*var);
    } else if (!strcasecmp(type, "object")) {
        convert_to_object(*var);
    } else if (!strcasecmp(type, "bool")) {
        convert_to_boolean(*var);
    } else if (!strcasecmp(type, "boolean")) {
        convert_to_boolean(*var);
    } else if (!strcasecmp(type, "null")) {
        convert_to_null(*var);
    } else if (!strcasecmp(type, "resource")) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot convert to resource type");
        RETURN_FALSE;
    } else {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type");
        RETURN_FALSE;
    }
    RETVAL_TRUE;
}

這個極具PHP特色的強制類型轉換就是這個函數,而這個函數是作為一個代理方法存在, 具體的轉換規則由各個類型的處理函數處理,不管是自動還是強制類型轉換,最終都會調用這些內部轉換方法, 這和前面的強制類型轉換在本質上是一樣的。

延伸閱讀

此文章所在專題列表如下:

  1. PHP內核探索:從SAPI接口開始
  2. PHP內核探索:一次請求的開始與結束
  3. PHP內核探索:一次請求生命周期
  4. PHP內核探索:單進程SAPI生命周期
  5. PHP內核探索:多進程/線程的SAPI生命周期
  6. PHP內核探索:Zend引擎
  7. PHP內核探索:再次探討SAPI
  8. PHP內核探索:Apache模塊介紹
  9. PHP內核探索:通過mod_php5支持PHP
  10. PHP內核探索:Apache運行與鉤子函數
  11. PHP內核探索:嵌入式PHP
  12. PHP內核探索:PHP的FastCGI
  13. PHP內核探索:如何執行PHP腳本
  14. PHP內核探索:PHP腳本的執行細節
  15. PHP內核探索:操作碼OpCode
  16. PHP內核探索:PHP里的opcode
  17. PHP內核探索:解釋器的執行過程
  18. PHP內核探索:變量概述
  19. PHP內核探索:變量存儲與類型
  20. PHP內核探索:PHP中的哈希表
  21. PHP內核探索:理解Zend里的哈希表
  22. PHP內核探索:PHP哈希算法設計
  23. PHP內核探索:翻譯一篇HashTables文章
  24. PHP內核探索:哈希碰撞攻擊是什么?
  25. PHP內核探索:常量的實現
  26. PHP內核探索:變量的存儲
  27. PHP內核探索:變量的類型
  28. PHP內核探索:變量的值操作
  29. PHP內核探索:變量的創建
  30. PHP內核探索:預定義變量
  31. PHP內核探索:變量的檢索
  32. PHP內核探索:變量的類型轉換
  33. PHP內核探索:弱類型變量的實現
  34. PHP內核探索:靜態變量的實現
  35. PHP內核探索:變量類型提示
  36. PHP內核探索:變量的生命周期
  37. PHP內核探索:變量賦值與銷毀
  38. PHP內核探索:變量作用域
  39. PHP內核探索:詭異的變量名
  40. PHP內核探索:變量的value和type存儲
  41. PHP內核探索:全局變量Global
  42. PHP內核探索:變量類型的轉換
  43. PHP內核探索:內存管理開篇
  44. PHP內核探索:Zend內存管理器
  45. PHP內核探索:PHP的內存管理
  46. PHP內核探索:內存的申請與銷毀
  47. PHP內核探索:引用計數與寫時復制
  48. PHP內核探索:PHP5.3的垃圾回收機制
  49. PHP內核探索:內存管理中的cache
  50. PHP內核探索:寫時復制COW機制
  51. PHP內核探索:數組與鏈表
  52. PHP內核探索:使用哈希表API
  53. PHP內核探索:數組操作
  54. PHP內核探索:數組源碼分析
  55. PHP內核探索:函數的分類
  56. PHP內核探索:函數的內部結構
  57. PHP內核探索:函數結構轉換
  58. PHP內核探索:定義函數的過程
  59. PHP內核探索:函數的參數
  60. PHP內核探索:zend_parse_parameters函數
  61. PHP內核探索:函數返回值
  62. PHP內核探索:形參return value
  63. PHP內核探索:函數調用與執行
  64. PHP內核探索:引用與函數執行
  65. PHP內核探索:匿名函數及閉包
  66. PHP內核探索:面向對象開篇
  67. PHP內核探索:類的結構和實現
  68. PHP內核探索:類的成員變量
  69. PHP內核探索:類的成員方法
  70. PHP內核探索:類的原型zend_class_entry
  71. PHP內核探索:類的定義
  72. PHP內核探索:訪問控制
  73. PHP內核探索:繼承,多態與抽象類
  74. PHP內核探索:魔術函數與延遲綁定
  75. PHP內核探索:保留類與特殊類
  76. PHP內核探索:對象
  77. PHP內核探索:創建對象實例
  78. PHP內核探索:對象屬性讀寫
  79. PHP內核探索:命名空間
  80. PHP內核探索:定義接口
  81. PHP內核探索:繼承與實現接口
  82. PHP內核探索:資源resource類型
  83. PHP內核探索:Zend虛擬機
  84. PHP內核探索:虛擬機的詞法解析
  85. PHP內核探索:虛擬機的語法分析
  86. PHP內核探索:中間代碼opcode的執行
  87. PHP內核探索:代碼的加密與解密
  88. PHP內核探索:zend_execute的具體執行過程
  89. PHP內核探索:變量的引用與計數規則
  90. PHP內核探索:新垃圾回收機制說明

本文地址:http://www.bavugt.tw/librarys/veda/detail/1437,歡迎訪問原出處。

不打個分嗎?

轉載隨意,但請帶上本文地址:

http://www.bavugt.tw/librarys/veda/detail/1437

如果你認為這篇文章值得更多人閱讀,歡迎使用下面的分享功能。
小提示:您可以按快捷鍵 Ctrl + D,或點此 加入收藏

大家都在看

閱讀一百本計算機著作吧,少年

很多人覺得自己技術進步很慢,學習效率低,我覺得一個重要原因是看的書少了。多少是多呢?起碼得看3、4、5、6米吧。給個具體的數量,那就100本書吧。很多人知識結構不好而且不系統,因為在特定領域有一個足夠量的知識量+足夠良好的知識結構,系統化以后就足以應對大量未曾遇到過的問題。

奉勸自學者:構建特定領域的知識結構體系的路徑中再也沒有比學習該專業的專業課程更好的了。如果我的知識結構體系足以囊括面試官的大部分甚至吞并他的知識結構體系的話,讀到他言語中的一個詞我們就已經知道他要表達什么,我們可以讓他坐“上位”畢竟他是面試官,但是在知識結構體系以及心理上我們就居高臨下。

所以,閱讀一百本計算機著作吧,少年!

《深入理解計算機系統(原書第2版)》 布萊恩特(Randal E.Bryant) (作者), 奧哈拉倫(David R.O'Hallaron) (作者), 龔奕利 (譯者), 雷迎春 (譯者)

《深入理解計算機系統》從程序員的視角詳細闡述計算機系統的本質概念,并展示這些概念如何實實在在地影響應用程序的正確性、性能和實用性。全書共12章,主要內容包括信息的表示和處理、程序的機器級表示、處理器體系結構、優化程序性能、存儲器層次結構、鏈接、異常控制流、虛擬存儲器、系統級I/O、網絡編程、并發編程等。書中提供子大量的例子和練習題,并給出部分答案,有助于讀者加深對正文所述概念和知識的理解。

更多計算機寶庫...

有极速快乐十分吗
2014 3月股票推荐 快乐赛车app注册平台 全民炒股 十分快乐开奖结果 股票配资公司都是怎么开展业务 长春麻将规则 时彩计划网 四川麻将秘籍 理财产品排行p2p 捉鸡麻将技巧十句口