上一章咱們對PHP的源碼目錄結構有了初步瞭解,本章咱們繼續從生命週期的維度對PHP進行剖析。php
生命週期是什麼呢?你能夠把它看做執行過程,PHP的生命週期也就是它從開始執行到結束執行的過程。api
PHP生命週期有五個階段,分別爲模塊初始化階段、請求初始化階段、執行階段、請求關閉階段、模塊關閉階段。只是不一樣SAPI模式下,請求的狀況略有不一樣,好比FastCGI下只經歷了一次模塊初始化階段,接下來全部請求只經歷請求初始化、執行腳本、請求關閉階段。緩存
在初步瞭解生命週期的五個階段以後,咱們先來說述在進入模塊初始化階段(php_module_startup)以前PHP所作的工做(本文繼續以PHP7.0.12版本的CLI模式)。好了,咱們如今開始。服務器
cli模式下的入口文件是sapi/cli/php_cli.c
,打開該文件,定位到主函數main,有沒有以爲1180行出現的結構體sapi_module_struct
很眼熟?這就是上篇文章SAPI的介紹中提到到結構體,它是擴展PHP對外服務的關鍵。cookie
//sapi/cli/php_cli.c sapi_module_struct *sapi_module = &cli_sapi_module;
先來看一下sapi_module_struct
在main/SAPI.h
中的定義:ide
//main/SAPI.h struct _sapi_module_struct { char *name; //名字,如cli、fpm等 char *pretty_name; //更容易理解的名字 int (*startup)(struct _sapi_module_struct *sapi_module); //模塊啓動時調用的函數 int (*shutdown)(struct _sapi_module_struct *sapi_module); //模塊結束時調用的函數 int (*activate)(void); //處理request時須要調用的函數 int (*deactivate)(void); //處理完request要調用的函數 size_t (*ub_write)(const char *str, size_t str_length); //用於輸出數據 void (*flush)(void *server_context); //刷新緩存 zend_stat_t *(*get_stat)(void); //判斷對執行的文件是否有執行權限 char *(*getenv)(char *name, size_t name_len); //獲取函數變量的函數指針 void (*sapi_error)(int type, const char *error_msg, ...); //錯誤處理函數指針 int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers); //調用header()時被調用的函數 int (*send_headers)(sapi_headers_struct *sapi_headers); //發送所有header的函數指針 void (*send_header)(sapi_header_struct *sapi_header, void *server_context); //發送某一個header的函數指針 size_t (*read_post)(char *buffer, size_t count_bytes); //獲取HTTP POST中數據的函數指針 char *(*read_cookies)(void); //獲取COOKIE void (*register_server_variables)(zval *track_vars_array); //從$_SERVER中獲取變量的函數指針 void (*log_message)(char *message); //輸出錯誤信息函數指針 double (*get_request_time)(void); //獲取請求時間的函數指針 void (*terminate_process)(void); //調用exit退出時的函數指針 char *php_ini_path_override; //PHP的ini文件被複寫的地址 void (*block_interruptions)(void); void (*unblock_interruptions)(void); void (*default_post_reader)(void); //負責解析POST數據 void (*treat_data)(int arg, char *str, zval *destArray); //對數據進行處理 char *executable_location; //執行的地理位置 int php_ini_ignore; //是否不使用任何ini配置文件 int php_ini_ignore_cwd; //忽略當前路徑的php.ini int (*get_fd)(int *fd); //獲取執行文件的fd的函數指針 int (*force_http_10)(void); //強制使用http1.0版本的函數指針 int (*get_target_uid)(uid_t *); //獲取執行程序的uid函數指針 int (*get_target_gid)(gid_t *); //獲取執行程序的gid函數指針 unsigned int (*input_filter)(int arg, char *var, char **val, size_t val_len, size_t *new_val_len); //對輸入進行過濾的函數指針,好比將輸入參數填充到自動全局變量$_GET、$_POST、$_COOKIE中 void (*ini_defaults)(HashTable *configuration_hash); int phpinfo_as_text; //是否輸出phpinfo信息 //默認的ini配置的函數指針,把ini配置信息在HashTable中 char *ini_entries; //執行時附帶的ini配置,可使php -d設置 const zend_function_entry *additional_functions; //每一個SAPI模塊特有的一些函數註冊,好比cli的cli_get_process_title unsigned int (*input_filter_init)(void); }
SAPI下的每個模式都實現了該結構體,好比在CLI中:函數
//sapi/cli/php_cli.c static sapi_module_struct cli_sapi_module = { "cli", /* name */ "Command Line Interface", /* pretty name */ ...... }
FPM中:源碼分析
//sapi/fpm/fpm/fpm_main.c static sapi_module_struct cgi_sapi_module = { "fpm-fcgi", /* name */ "FPM/FastCGI", /* pretty name */ ...... }
在litespeed中也有相同定義:post
//sapi/litespeed/lsapi_main.c static sapi_module_struct lsapi_sapi_module = { "litespeed", "LiteSpeed V6.10", ...... }
咱們繼續往下看,在通過一系列變量的初始化後,於1302行又調用了sapi_startup
函數。ui
//sapi/cli/php_cli.c sapi_startup(sapi_module);
該函數定義了sapi_globals_struct
,也就是咱們常說的SG宏,它的主要做用是保存請求的基本信息,好比服務器信息、header、編碼等。
//main/SAPI.h typedef struct _sapi_globals_struct { void *server_context; sapi_request_info request_info; sapi_headers_struct sapi_headers; int64_t read_post_bytes; unsigned char post_read; unsigned char headers_sent; zend_stat_t global_stat; char *default_mimetype; char *default_charset; HashTable *rfc1867_uploaded_files; zend_long post_max_size; int options; zend_bool sapi_started; double global_request_time; HashTable known_post_content_types; zval callback_func; zend_fcall_info_cache fci_cache; } sapi_globals_struct;
咱們繼續往下看,sapi_module
調用了startup
函數:
//sapi/cli/php_cli.c if (sapi_module->startup(sapi_module) == FAILURE) { exit_status = 1; goto out; }
而後又調用了CLI在sapi_module_struct
中定義的startup
對應的鉤子函數php_cli_startup
:
//sapi/cli/php_cli.c static sapi_module_struct cli_sapi_module = { "cli", /* name */ "Command Line Interface", /* pretty name */ php_cli_startup, /* startup */ ...... }
繼續跟進,php_cli_startup
函數中又調用了php_module_startup
函數:
//sapi/cli/php_cli.c static int php_cli_startup(sapi_module_struct *sapi_module) /* {{{ */ { if (php_module_startup(sapi_module, NULL, 0)==FAILURE) { return FAILURE; } return SUCCESS; }
是否是很眼熟,這不就是模塊初始化階段的函數嘛!原來執行了這麼久纔到咱們的關鍵點,模塊初始化階段內容比較多,咱們經過下一章進行詳細剖析。
//main/main.c int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additional_modules) { zend_utility_functions zuf; zend_utility_values zuv; int retval = SUCCESS, module_number=0; /* for REGISTER_INI_ENTRIES() */ char *php_os; zend_module_entry *module; ... }
注意:我在本文貼出的代碼都標識了文件位置,咱們能夠看出來,在PHP五大生命週期開始以前一直都是在sapi目錄中執行的,而從php_module_struct也就是模塊初始化階段開始,才執行到了main目錄,這意味着PHP的生命週期的第一個階段是從main目錄下開始的。