首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 超全局變量生成全過程:從 SAPI 到 php_register_variable_ex

     編輯:PHP 相關知識

核心流程圖

HTTP Request
    ↓
Apache SAPI (mod_php / php-fpm)
    ↓
php_request_startup()
    ↓
php_hash_environment()  ← 關鍵入口
    ↓
sapi_module.treat_data() (php_default_treat_data)
    ↓
php_register_variable_safe()
    ↓
php_register_variable_ex()  ← 變量名規範化 (點號→下劃線)
    ↓
填充 $_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES

關鍵源碼節點 (PHP 5.6/7.x main/php_variables.c)

1. php_hash_environment() 初始化自動全局變量

struct auto_global_record {
    char *name;           // "_GET"
    uint name_len;
    char *long_name;      // "HTTP_GET_VARS" (已移除 PHP 5.4+)
    uint long_name_len;
    zend_bool jit_initialization;
} auto_global_records[] = {
    {"_GET", ..., "HTTP_GET_VARS", ..., 0},
    {"_POST", ..., "HTTP_POST_VARS", ..., 0},
    {"_COOKIE", ..., "HTTP_COOKIE_VARS", ..., 0},
    {"_SERVER", ..., "HTTP_SERVER_VARS", ..., 1}, // JIT
    {"_ENV", ..., "HTTP_ENV_VARS", ..., 1},       // JIT
    {"_FILES", ..., "HTTP_POST_FILES", ..., 0},
};
  • 按 variables_order (默認 "GPCS") 依次處理 GET/POST/COOKIE/SERVER/ENV。
  • jit_initialization=1 表示惰性初始化:首次訪問 $_SERVER 時才填充。

2. sapi_module.treat_data() 解析輸入

// 簡化版 php_default_treat_data
switch (arg) {
    case PARSE_POST:
        if (content_type == application/x-www-form-urlencoded)
            parse_str(raw_post_data, &array);
        else if (content_type == multipart/form-data)
            sapi_module.read_post() → rfc1867_post_handler() → 填充 $_POST/$_FILES;
        break;
    case PARSE_GET:
        parse_str(query_string, &array); // $_SERVER['QUERY_STRING']
        break;
    case PARSE_COOKIE:
        parse_str(cookie_header, &array); // 分號分隔
        break;
}

3. php_register_variable_ex() 核心規範化邏輯 (回答「點號變下劃線」)

PHPAPI void php_register_variable_ex(char *var, zval *val, zval *track_vars_array) {
    // 1. 去除前導空格
    while (*var && *var == ' ') var++;

    // 2. 關鍵:將空格和點號替換為下劃線
    for (char *p = var; *p; p++) {
        if (*p == ' ' || *p == '.') {
            *p = '_';  // ← 這裡!表單 name="a.b" 變為 $_POST['a_b']
        } else if (*p == '[') {
            is_array = 1; // 識別陣列語法 name="arr[key]"
        }
    }

    // 3. 解析陣列語法 a[b][c] → 多維陣列
    // 4. 將值插入 track_vars_array (即 $_GET/$_POST 等)
}

為什麼要把點號換成下劃線?

  • PHP 變量名規則:[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*,**不允許點號**。
  • HTML 表單歷史慣例:早期 CGI 時代常用 name="user.name",PHP 為兼容自動轉換。
  • 副作用:name="a.b" 和 name="a_b" 會衝突,無法區分。

variables_order 控制生成順序與覆蓋規則

; php.ini
variables_order = "GPCS" ; 默認

; 處理順序:G → P → C → S
; 後處理的會覆蓋先處理的同名鍵 (若 register_globals=On)
; 現代 PHP (register_globals=Off) 僅影響超全局變量內部順序,不影響用戶代碼訪問。

現代 PHP (7.0+) 變化

  • 移除 $HTTP_*_VARS 長名稱變量。
  • auto_globals_jit 默認開啟:$_SERVER/$_ENV 首次使用時才填充,節省啟動開銷。
  • $_REQUEST 由 request_order 控制 (默認 "GP"),不再包含 Cookie。

實戰:手動模擬超全局變量填充 (測試/CLI 環境)

<?php
// 僅 CLI 測試用,勿用於生產
function mockSuperGlobals(array $get=[], array $post=[], array $cookie=[]) {
    $_GET = $get;
    $_POST = $post;
    $_COOKIE = $cookie;
    $_REQUEST = array_merge($_GET, $_POST); // 依 request_order
    $_SERVER['REQUEST_METHOD'] = 'POST';
    $_SERVER['QUERY_STRING'] = http_build_query($get);
    $_SERVER['HTTP_COOKIE'] = http_build_query($cookie, '', '; ');
}
?>

總結

超全局變量生成鏈路:SAPI → php_hash_environment → treat_data → register_variable_ex (點號轉下劃線) → 填充符號表。理解此流程有助於排查「為什麼 $_POST 為空」(Content-Type 錯誤)、「為什麼鍵名變了」(點號轉換)、「為什麼 $_SERVER 延遲初始化」(JIT)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved