程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> PHP數組下標類型陷阱

PHP數組下標類型陷阱

編輯:關於PHP編程

項目使用PHP語言開發,其中用到了MONGO DB存儲;MONGO DB裡的數據是強類型,PHP裡的數據是弱類型,上周五我在MONGODB裡查詢一個數據總是找不到,最後發現問題是PHP數組的數值型字符串下標自動轉變成了整數型下標;因此雖然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); 
$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);
    這段程序的輸出是:
 
 
 
Php代碼
array(1) {  
  [22]=>  
  string(3) "xxx" 
}  
array(1) {  
  [22]=>  
  string(3) "xxx" 
}  
array(1) {  
  ["022"]=>  
  string(3) "xxx" 
}  
array(1) {  
  ["2222222222222"]=>  
  string(3) "xxx" 

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裡搜索[,找到數組的語義解析規則:
 
 
 
Php代碼
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);}     

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:
 
 
 
Php代碼
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);                                        
}  
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位置的值 。我們看下宏的具體定義:
 
 
 
Php代碼
#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<end) {                                                               \  
            if (!(*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);                                                                        \  

#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<end) {                                                               \            if (!(*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)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved