原文地址:https://www.hongweipeng.com/i...php
表單提交到PHP腳本時,底層的PHP會作一層轉換。將一些符號轉成下劃線 _
。java
實際上這層轉換中會發生不少意想不到的狀況。git
一個簡單的測試就出現了意外,一個是單個 [
也會被替換,對於 array
的輸入, key 不會作轉換。因而我多多測了一下,得出以下列表:github
<input name="a.b" /> 轉爲: $_REQUEST["a_b"] <input name="a b" /> 轉爲: $_REQUEST["a_b"] <input name="a[b" /> 轉爲: $_REQUEST["a_b"] <input name="a]b" /> 轉爲: $_REQUEST["a]b"] <input name="a-b" /> 轉爲: $_REQUEST["a-b"] <input name=" ab" /> 轉爲: $_REQUEST["ab"] <input name="ab " /> 轉爲: $_REQUEST["ab "] <input name="arr[a.b]" /> 轉爲: $_REQUEST["arr"]["a.b"] <input name="ar.r[a.b]" /> 轉爲: $_REQUEST["ar_r"]["a.b"] <input name="arr[a[b]]" /> 轉爲: $_REQUEST["arr"]["a[b"] <input name="arr[a[]x]" /> 轉爲: $_REQUEST["arr"]["a["] <input name="arr[]ab" /> 轉爲: $_REQUEST["arr"][0] <input name="arr[a]b" /> 轉爲: $_REQUEST["arr"]["a"] <input name="arr[a.b" /> 轉爲: $_REQUEST["arr_a.b"] <input name="arr[[a.b" /> 轉爲: $_REQUEST["arr_[a.b"]
這個轉換機制十分詭異是吧。查了一下,在 Bug#77172 convert error on receiving variables from external sources 中提出了 id[]_text
轉換成 id[]
的問題,採起的結果是補全文檔上的說明。c#
另外也有幾個討論是否關閉這層轉換:數組
這三個 Request
都仍是 open 狀態,尚未結果,其中關於關閉轉換的討論早在06年就提出來了。我不清楚 PHP 爲何會作這個轉換,目的是什麼。據我所知的 java,Django 都不會作轉換的。函數
PHP對於外部輸入的變量都會轉換的,這就涉及到了 $_POST, $_GET, $_FILES, $_COOKIE, $_REQUEST
這些變量了。源碼分析
雖然我沒有閱讀過php源碼,在朋友的幫助下,關於這部分的轉換代碼在 main/php_variables.c
的 php_register_variable_ex
函數中 php_variables.c#L68 ,源碼精簡了下流程:post
PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars_array) { char *p = NULL; char *ip = NULL; /* index pointer */ char *index; char *var, *var_orig; /* ignore leading spaces in the variable name */ while (*var_name==' ') { // 忽略前置空格 var_name++; } for (p = var; *p; p++) { if (*p == ' ' || *p == '.') { // 空格和點替換成下劃線 *p='_'; } else if (*p == '[') { is_array = 1; // 若是遇到 [ 則視爲數組,is_array 設爲1 ip = p; *p = 0; break; } } ... }
這裏能夠看出,忽略前置空格是最早作的動做;當遇到第一個 [
時,php則認爲數數組,再也不進行轉換,設置了 is_array = 1
就 break 了。測試
這個 is_array
有什麼用呢,往下看:
if (is_array) { int nest_level = 0; while (1) { char *index_s; size_t new_idx_len = 0; ip++; // [ 的下一個字符 index_s = ip; if (*ip==']') { // 若是下一個字符就已是],表示沒有設置key index_s = NULL; } else { ip = strchr(ip, ']'); // 查找剩餘字符串中的 ] if (!ip) { /* PHP variables cannot contain '[' in their names, so we replace the character with a '_' */ *(index_s - 1) = '_'; // 若是沒找到,則將 [ 替換成下劃線 index_len = 0; if (index) { index_len = strlen(index); } goto plain_var; return; } *ip = 0; new_idx_len = strlen(index_s); // key 的長度到第一個出現 ] 爲止 } } ... }
到此,轉化處理的過程就很清晰了,對於數組狀況的變量名,分爲兩種:
]
與其匹配,該變量名不是數組,將 [
替換成下劃線,後續字符串不作處理;]
與其匹配,取到第一個出現 ]
的位置做爲 key ,捨棄後面的字符。對於狀況1 就很奇怪了,若是輸入是 arr[[a.b
那麼就會轉成成 arr_[a.b
了。
鑑於當前的轉換規則總結的規律以下:
[
以前的字符中,忽略前置的空格,將 .
和 空格
替換成下劃線 _
;在第一個 [
以後的字符,再也不進行替換處理:
]
時,第一個 [
替換成 _
,後續字符串不作轉換;]
時,取到第一次出現 ]
的位置做爲 key,捨棄後續字符。另外,誰能告訴我PHP的這層轉換的設計初衷是什麼啊。