PHP數組上標類型陷阱

來源:互聯網
上載者:User
PHP數組下標類型陷阱

???? 項目使用PHP語言開發,其中用到了MONGO DB儲存;MONGO DB裡的資料是強型別,PHP裡的資料是弱類型,上周五我在MONGODB裡查詢一個資料總是找不到,最後發現問題是PHP數組的數值型字串下標自動轉變成了整數型下標;因此雖然PHP是弱類型語言,我們也要關注變數當前什麼類型,熟悉PHP的類型自動轉換規則,在一些類型敏感的地方要進行類型判斷或者強制類型轉換。

??? 以下樣本程式簡單解釋了這個現象:

?

$id = "22";$arr1[$id] = "xxx";var_dump($arr1);$id = 22;$arr2[$id] = "xxx";var_dump($arr2);$id = "022";$arr3[$id] = "xxx";var_dump($arr3);$id = "2222222222222";$arr4[$id] = "xxx";var_dump($arr4);

??? 這段程式的輸出是:

?

array(1) {  [22]=>  string(3) "xxx"}array(1) {  [22]=>  string(3) "xxx"}array(1) {  ["022"]=>  string(3) "xxx"}array(1) {  ["2222222222222"]=>  string(3) "xxx"}
?

??? 那麼,PHP的數組字串下標類型是怎麼確定的呢?我們一起到PHP的原始碼裡看一看。

??? 首先,我們在Zend/zend_language_parser.y裡搜尋[,找到數組的語義解析規則:

?

object_dim_list:        object_dim_list '[' dim_offset ']'  { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }                             |   object_dim_list '{' expr '}'        { fetch_string_offset(&$$, &$1, &$3 TSRMLS_CC); }    |   variable_name { znode tmp_znode;  zend_do_pop_object(&tmp_znode TSRMLS_CC);  zend_do_fetch_property(&$$,  &tmp_znode, &$1 TSRMLS_CC);}   ;

?

?? 我們使用的是數組,因此使用第一個規則fetch_array_dim,在fetch_array_dim函數裡,我們發現產生的opcode是ZEND_FETCH_DIM_W(84)。在Zend/zend_vm_def.h裡,ZEND_FETCH_DIM_W的處理函數裡zend_fetch_dimension_address處理取下標邏輯。

?

??? 繼續跟蹤下去,從zend_fetch_dimension_address函數到zend_fetch_dimension_address_inner,再到zend_symtable_update:

?

static inline int zend_symtable_update(HashTable *ht, char *arKey, uint nKeyLength, void *pData, uint nDataSize,  void **pDest)                 \{     HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_update(ht, idx, pData, nDataSize, pDest));    return zend_hash_update(ht, arKey, nKeyLength, pData, nDataSize, pDest);                                      } 

?

?? HANDLE_NUMERIC這個宏很有意思,如果字串下標arKey可轉化為長整數idx,則調用zend_hash_index_update把資料插入到idx位置,否則調用zend_hash_update修改arKey位置的值。我們看下宏的具體定義:

?

#define HANDLE_NUMERIC(key, length, func) {                                             \    register char *tmp=key;                                                             \                                                                                        \    if (*tmp=='-') {                                                                    \        tmp++;                                                                          \    }                                                                                   \    if ((*tmp>='0' && *tmp<='9')) do { /* possibly a numeric index */                   \        char *end=key+length-1;                                                         \        long idx;                                                                       \                                                                                        \        if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */    \            break;                                                                      \        }                                                                               \        while (tmp='0' && *tmp<='9')) {                                            \                break;                                                                  \            }                                                                           \            tmp++;                                                                      \        }                                                                               \        if (tmp==end && *tmp=='\0') { /* a numeric index */                             \            if (*key=='-') {                                                            \                idx = strtol(key, NULL, 10);                                            \                if (idx!=LONG_MIN) {                                                    \                    return func;                                                        \                }                                                                       \            } else {                                                                    \                idx = strtol(key, NULL, 10);                                            \                if (idx!=LONG_MAX) {                                                    \                    return func;                                                        \                }                                                                       \            }                                                                           \        }                                                                               \    } while (0);                                                                        \}

??? 從宏裡我們知道了字串下標自動轉化為長整數下標的規則:

??? 1. 全部為數字,但是不能有前置0,比如arKey="0123"不會轉化成123

??? 2. 不能超過long的表示範圍(LONG_MIN, LONG_MAX),即(-2147483648, 2147483647)

?

?

?

?

?

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.