上篇文章講述了模塊初始化階段以前的準備工做,本篇我來詳細介紹PHP生命週期的五個階段。php
咱們先來看一下該階段的每一個函數的做用。sql
// main/SAPI.c SAPI_API void sapi_initialize_empty_request(void) { SG(server_context) = NULL; SG(request_info).request_method = NULL; SG(request_info).auth_digest = SG(request_info).auth_user = SG(request_info).auth_password = NULL; SG(request_info).content_type_dup = NULL; }
這個函數主要爲前面定義的SG宏中的成員變量進行初始化。api
// main/SAPI.c SAPI_API void sapi_activate(void) { zend_llist_init(&SG(sapi_headers).headers, sizeof(sapi_header_struct), (void (*)(void *)) sapi_free_header, 0); SG(sapi_headers).send_default_content_type = 1; SG(sapi_headers).http_status_line = NULL; SG(sapi_headers).mimetype = NULL; SG(headers_sent) = 0; ...... /* Handle request method */ if (SG(server_context)) { ...... if (sapi_module.activate) { sapi_module.activate(); } } if (sapi_module.input_filter_init) { sapi_module.input_filter_init(); } }
函數的前半部分主要仍是對SG宏的成員變量進行初始化。後半部分先是調用了sapi_module_struct
內部實現的activate
函數,又調用了input_filter_init
函數,可是在CLI模式並無實現這兩個函數,只是返回了NULL。代碼以下:緩存
NULL, /* activate */
//main/output.c PHPAPI void php_output_startup(void) { ZEND_INIT_MODULE_GLOBALS(output, php_output_init_globals, NULL); zend_hash_init(&php_output_handler_aliases, 8, NULL, NULL, 1); zend_hash_init(&php_output_handler_conflicts, 8, NULL, NULL, 1); zend_hash_init(&php_output_handler_reverse_conflicts, 8, NULL, reverse_conflict_dtor, 1); php_output_direct = php_output_stdout; }
咱們先來看ZEND_INIT_MODULE_GLOBALS
宏作了什麼事情:cookie
#define ZEND_INIT_MODULE_GLOBALS(module_name, globals_ctor, globals_dtor) \ globals_ctor(&module_name##_globals);
由代碼得知,該宏只是作了一層替換,替換後的內容爲:函數
php_output_init_globals(&output_globals);
那php_output_init_globals
函數又作了什麼呢?post
//main/output.c static inline void php_output_init_globals(zend_output_globals *G) { ZEND_TSRMLS_CACHE_UPDATE(); memset(G, 0, sizeof(*G)); }
該函數經過memset
函數對output_globals
進行了內存相關的初始化,咱們能夠在main/php_output.h
中的155行找到它的宏定義OG。性能
//main/php_output.h # define OG(v) (output_globals.v)
OG對應的結構體是php_output_init_globals
的入參zend_output_globals
,在這裏花了些時間,由於沒找到定義在哪裏,最後發現它也是經過宏定義替換得來的,代碼以下:ui
//main/php_output.h ZEND_BEGIN_MODULE_GLOBALS(output) zend_stack handlers; php_output_handler *active; php_output_handler *running; const char *output_start_filename; int output_start_lineno; int flags; ZEND_END_MODULE_GLOBALS(output)
看似是定義了一個結構體,可是代碼中又出現了兩個宏,咱們再來瞅瞅這兩個宏是幹嗎的:編碼
//Zend/zend_API.h #define ZEND_BEGIN_MODULE_GLOBALS(module_name) \ typedef struct _zend_##module_name##_globals { #define ZEND_END_MODULE_GLOBALS(module_name) \ } zend_##module_name##_globals;
原來只是作了個替換而已,替換後的代碼以下:
//這個是意淫出來的代碼 typedef struct _zend_output_globals { zend_stack handlers; php_output_handler *active; php_output_handler *running; const char *output_start_filename; int output_start_lineno; int flags; } zend_output_globals
這纔是zend_output_globals
最純粹的定義,寫的非常騷氣,差點看斷片。這樣看來咱們的OG宏對應的就是這個結構體了,姑且認爲它是PHP輸出相關的結構體。咱們繼續往下看:
zend_hash_init(&php_output_handler_aliases, 8, NULL, NULL, 1); zend_hash_init(&php_output_handler_conflicts, 8, NULL, NULL, 1); zend_hash_init(&php_output_handler_reverse_conflicts, 8, NULL, reverse_conflict_dtor, 1); php_output_direct = php_output_stdout;
接下來又對三個HashTable進行了初始化,初始化完成後,將php_output_direct
指針指向了php_output_stdout
函數。php_output_stdout
函數的做用是調用fwrite函數,輸出字符串到stdout中。代碼以下:
//main/output.c static size_t php_output_stdout(const char *str, size_t str_len) { fwrite(str, 1, str_len, stdout); return str_len; }
int php_startup_ticks(void) { zend_llist_init(&PG(tick_functions), sizeof(struct st_tick_function), NULL, 1); return SUCCESS; }
這裏又出現了一個PG宏,來看下它的定義
# define PG(v) (core_globals.v)
PG對應的結構體是core_globals
,core_globals
又對應_php_core_globals
,代碼以下
extern ZEND_API struct _php_core_globals core_globals;
php_core_globals顧名思義,就是php核心的全局變量,定義不少PHP相關的參數,好比內存上限、是否顯示錯誤信息、上傳文件大小限制、輸入輸出編碼、禁用的函數等等,這裏再也不贅述,感興趣的同窗能夠去看一下源碼。
//main/php_globals.h struct _php_core_globals { zend_bool implicit_flush; zend_long output_buffering; zend_bool sql_safe_mode; zend_bool enable_dl; ...... };
而php_startup_ticks
函數就是對PG宏的成員變量tick_functions
進行初始化。
ZEND_API void gc_globals_ctor(void) { gc_globals_ctor_ex(&gc_globals); }
這裏又出現了一個gc_globals
,它是與垃圾回收相關的結構體,這段代碼是對gc_globals
進行初始化。
//Zend/zend_gc.c typedef struct _zend_gc_globals { zend_bool gc_enabled; zend_bool gc_active; zend_bool gc_full; gc_root_buffer *buf; /* preallocated arrays of buffers */ ...... } zend_gc_globals;
++start_memory_manager++:初始化內存管理器,對結構體alloc_globals
進行初始化。
++virtual_cwd_startup++:virtual_cwd_startup初始化了cwd_globals,根據源碼能夠看出成員變量都與realpath_cache有關,realpath_cache是什麼呢?咱們平時在寫代碼的時候,常常會使用include、include_once、require、require_once等語句導入文件,若是每次使用這些語句都要去對應的目錄中尋找目標文件,勢必會下降性能,因此官方加入了緩存,以便PHP再次使用時沒必要到include_path中查找,加快PHP的執行速度。
//Zend/zend_virtual_cwd.c typedef struct _virtual_cwd_globals { cwd_state cwd; zend_long realpath_cache_size; zend_long realpath_cache_size_limit; zend_long realpath_cache_ttl; realpath_cache_bucket *realpath_cache[1024]; } virtual_cwd_globals;
++zend_startup_extensions_mechanism++。啓動擴展機制,初始化zend_extensions
結構體。
int zend_startup_extensions_mechanism() { /* Startup extensions mechanism */ zend_llist_init(&zend_extensions, sizeof(zend_extension), (void (*)(void *)) zend_extension_dtor, 1); last_resource_number = 0; return SUCCESS; }
++提供編譯與執行入口++。
zend_compile_file = compile_file; zend_execute_ex = execute_ex;
++zend_init_opcodes_handlers++。初始化Zend虛擬機的handler
++初始化CG、EG++。初始化CG(function_table)、CG(class_table)、CG(auto_globals)、EG(zend_constants)。
GLOBAL_FUNCTION_TABLE = (HashTable *) malloc(sizeof(HashTable)); GLOBAL_CLASS_TABLE = (HashTable *) malloc(sizeof(HashTable)); GLOBAL_AUTO_GLOBALS_TABLE = (HashTable *) malloc(sizeof(HashTable)); GLOBAL_CONSTANTS_TABLE = (HashTable *) malloc(sizeof(HashTable));
++ini_scanner_globals_ctor++。初始化ini_scanner_globals
。
++php_scanner_globals_ctor++。初始化language_scanner_globals
。
++zend_set_default_compile_time_values++。設置了編譯相關的配置。
//Zend/zend.c static void zend_set_default_compile_time_values(void) /* {{{ */ { /* default compile-time values */ CG(short_tags) = short_tags_default; CG(compiler_options) = compiler_options_default; }
++EG(error_reporting)++。EG宏就是executor_globals
,Zend執行器相關的全局變量,在這裏對咱們熟知的error_reporting
進行配置。
//Zend/zend_globals.h struct _zend_executor_globals { zval uninitialized_zval; zval error_zval; /* symbol table cache */ zend_array *symtable_cache[SYMTABLE_CACHE_SIZE]; zend_array **symtable_cache_limit; zend_array **symtable_cache_ptr; ...... }
++zend_interned_strings_init++。初始化內部字符串。
//Zend/zend_string.c void zend_interned_strings_init(void) { #ifndef ZTS zend_string *str; zend_hash_init(&CG(interned_strings), 1024, NULL, _str_dtor, 1); ...... }
++zend_startup_builtin_functions++。初始化內部函數。
//Zend/zend_builtin_functions.c int zend_startup_builtin_functions(void) /* {{{ */ { zend_builtin_module.module_number = 0; zend_builtin_module.type = MODULE_PERSISTENT; return (EG(current_module) = zend_register_module_ex(&zend_builtin_module)) == NULL ? FAILURE : SUCCESS; }
++zend_register_standard_constants++。註冊常量,好比E_ERROR、E_WARNING、E_NOTICE、E_CORE_ERROR等。
//Zend/zend_constants.c void zend_register_standard_constants(void) { REGISTER_MAIN_LONG_CONSTANT("E_ERROR", E_ERROR, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("E_RECOVERABLE_ERROR", E_RECOVERABLE_ERROR, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("E_WARNING", E_WARNING, CONST_PERSISTENT | CONST_CS); ...... }
++zend_register_auto_global++。將GLOBALS加入CG(auto_globals)。
//Zend/zend.c zend_register_auto_global(zend_string_init("GLOBALS", sizeof("GLOBALS") - 1, 1), 1, php_auto_globals_create_globals);
++zend_init_rsrc_plist++。初始化持久化符號表。
int zend_init_rsrc_plist(void) { zend_hash_init_ex(&EG(persistent_list), 8, NULL, plist_entry_destructor, 1, 0); return SUCCESS; }
++zend_init_exception_op++。初始化EG(exception_op)。
++zend_init_call_trampoline_op++。初始化EG(call_trampoline_op)。
++zend_ini_startup++。初始化與php.ini解析相關的變量。
//Zend/zend_ini.c ZEND_API int zend_ini_startup(void) /* {{{ */ { registered_zend_ini_directives = (HashTable *) malloc(sizeof(HashTable)); EG(ini_directives) = registered_zend_ini_directives; EG(modified_ini_directives) = NULL; EG(error_reporting_ini_entry) = NULL; zend_hash_init_ex(registered_zend_ini_directives, 128, NULL, free_ini_entry, 1, 0); return SUCCESS; }
初始化析構函數
//Zend/zend_list.c ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, const char *type_name, int module_number) { zend_rsrc_list_dtors_entry *lde; zval zv; lde = malloc(sizeof(zend_rsrc_list_dtors_entry)); lde->list_dtor_ex = ld; lde->plist_dtor_ex = pld; lde->module_number = module_number; lde->resource_id = list_destructors.nNextFreeElement; lde->type_name = type_name; ZVAL_PTR(&zv, lde); if (zend_hash_next_index_insert(&list_destructors, &zv) == NULL) { return FAILURE; } return list_destructors.nNextFreeElement-1; }
獲取PHP執行的二進制路徑
static void php_binary_init(void) { char *binary_location; #ifdef PHP_WIN32 binary_location = (char *)malloc(MAXPATHLEN); if (GetModuleFileName(0, binary_location, MAXPATHLEN) == 0) { free(binary_location); PG(php_binary) = NULL; } #else if (sapi_module.executable_location) { binary_location = (char *)malloc(MAXPATHLEN); if (!strchr(sapi_module.executable_location, '/')) { char *envpath, *path; int found = 0; ...... }
初始化輸出相關的預約義常量
//main/output.c PHPAPI void php_output_register_constants(void) { REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START", PHP_OUTPUT_HANDLER_START, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_WRITE", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSH", PHP_OUTPUT_HANDLER_FLUSH, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEAN", PHP_OUTPUT_HANDLER_CLEAN, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FINAL", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEANABLE", PHP_OUTPUT_HANDLER_CLEANABLE, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSHABLE", PHP_OUTPUT_HANDLER_FLUSHABLE, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_REMOVABLE", PHP_OUTPUT_HANDLER_REMOVABLE, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STDFLAGS", PHP_OUTPUT_HANDLER_STDFLAGS, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STARTED", PHP_OUTPUT_HANDLER_STARTED, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_DISABLED", PHP_OUTPUT_HANDLER_DISABLED, CONST_CS | CONST_PERSISTENT); }
註冊文件上傳相關的預約義常量
//main/rfc1867.c void php_rfc1867_register_constants(void) /* {{{ */ { REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_OK", UPLOAD_ERROR_OK, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_INI_SIZE", UPLOAD_ERROR_A, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_FORM_SIZE", UPLOAD_ERROR_B, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_PARTIAL", UPLOAD_ERROR_C, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_FILE", UPLOAD_ERROR_D, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_TMP_DIR", UPLOAD_ERROR_E, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_CANT_WRITE", UPLOAD_ERROR_F, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_EXTENSION", UPLOAD_ERROR_X, CONST_CS | CONST_PERSISTENT); }
初始化配置文件php.ini,並經過zend_parse_ini_file
解析。
初始化ini相關的變量
void zend_register_standard_ini_entries(void) /* {{{ */ { int module_number = 0; REGISTER_INI_ENTRIES(); }
註冊咱們熟知的全局變量$_GET、$_POST、$_COOKIE等等
//main/php_variables.c void php_startup_auto_globals(void) { zend_register_auto_global(zend_string_init("_GET", sizeof("_GET")-1, 1), 0, php_auto_globals_create_get); zend_register_auto_global(zend_string_init("_POST", sizeof("_POST")-1, 1), 0, php_auto_globals_create_post); zend_register_auto_global(zend_string_init("_COOKIE", sizeof("_COOKIE")-1, 1), 0, php_auto_globals_create_cookie); zend_register_auto_global(zend_string_init("_SERVER", sizeof("_SERVER")-1, 1), PG(auto_globals_jit), php_auto_globals_create_server); zend_register_auto_global(zend_string_init("_ENV", sizeof("_ENV")-1, 1), PG(auto_globals_jit), php_auto_globals_create_env); zend_register_auto_global(zend_string_init("_REQUEST", sizeof("_REQUEST")-1, 1), PG(auto_globals_jit), php_auto_globals_create_request); zend_register_auto_global(zend_string_init("_FILES", sizeof("_FILES")-1, 1), 0, php_auto_globals_create_files); }
初始化針對不一樣內容類型的處理函數
//main/php_content_types.c int php_startup_sapi_content_types(void) { sapi_register_default_post_reader(php_default_post_reader); sapi_register_treat_data(php_default_treat_data); sapi_register_input_filter(php_default_input_filter, NULL); return SUCCESS; }
函數 | 說明 |
---|---|
php_output_activate() | 重置輸出全局變量,初始化輸出相關堆棧 |
zend_activate() | 初始化Zend引擎 |
sapi_activate() | 初始化SG宏,調各sapi鉤子函數activate |
zend_signal_activate() | 信號處理 |
zend_set_timeout() | 設置超時時間 |
php_hash_environment() | 初始化PHP請求的全局變量 |
zend_activate_modules() | 調用各擴展定義的request_startup鉤子函數 |
從新爲output_globals分配內存,初始化與輸出處理程序相關的堆棧,並將OG宏的flags設置爲激活狀態。
//main/output.c PHPAPI int php_output_activate(void) { #ifdef ZTS memset((*((void ***) ZEND_TSRMLS_CACHE))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)], 0, sizeof(zend_output_globals)); #else memset(&output_globals, 0, sizeof(zend_output_globals)); #endif zend_stack_init(&OG(handlers), sizeof(php_output_handler *)); OG(flags) |= PHP_OUTPUT_ACTIVATED; return SUCCESS; }
zend引擎的初始化,主要做用爲重置垃圾回收、初始化編譯器、初始化執行器、初始化掃描器。
函數 | 說明 |
---|---|
gc_reset() | 重置垃圾回收 |
init_compiler() | 初始化編譯器 |
init_executor() | 初始化執行器 |
startup_scanner() | 初始化掃描器 |
對SG宏內的一些變量進行初始化,並調用當前sapi_module_struct中定義的鉤子函數activate()以及input_filter_init(),可是在cli模式下,這兩個鉤子函數都沒有實現,返回了null。
//main/SAPI.c SAPI_API void sapi_activate(void) { zend_llist_init(&SG(sapi_headers).headers, sizeof(sapi_header_struct), (void (*)(void *)) sapi_free_header, 0); SG(sapi_headers).send_default_content_type = 1; /* SG(sapi_headers).http_response_code = 200; */ SG(sapi_headers).http_status_line = NULL; SG(sapi_headers).mimetype = NULL; SG(headers_sent) = 0; ZVAL_UNDEF(&SG(callback_func)); SG(read_post_bytes) = 0; SG(request_info).request_body = NULL; ...... }
爲http_globals分配內存,初始化auto_globals,解析請求參數並存放到全局變量中。
PHPAPI int php_hash_environment(void) { memset(PG(http_globals), 0, sizeof(PG(http_globals))); zend_activate_auto_globals(); if (PG(register_argc_argv)) { php_build_argv(SG(request_info).query_string, &PG(http_globals)[TRACK_VARS_SERVER]); } return SUCCESS; }
該函數經過遍歷註冊在module_registry的全部模塊,調用每一個模塊的鉤子函數request_startup()進行初始化。
ZEND_API void zend_activate_modules(void) /* {{{ */ { zend_module_entry **p = module_request_startup_handlers; while (*p) { zend_module_entry *module = *p; if (module->request_startup_func(module->type, module->module_number)==FAILURE) { zend_error(E_WARNING, "request_startup() for %s module failed", module->name); exit(1); } p++; } }
函數 | 說明 |
---|---|
compile_file() | - |
open_file_for_scanning() | 讀取PHP代碼內容 |
zend_parse() | 詞法語法分析生成AST |
init_op_array() | 初始化op_array |
zend_compile_top_stmt() | 將AST轉換爲op_array |
pass_two() | 設置op_array中對應zend虛擬機的handler |
zend_execute() | 執行op_array |
執行階段的入口是php_execute_script函數,該函數又調用了zend_execute_scripts函數,看了這段代碼的同窗可能會找不到上圖表中所提到的compile_file()函數,其實它在模塊初始化階段就已經將compile_file賦值給了zend_compile_file。
//Zend/zend.c zend_compile_file = compile_file;
執行階段調用示意圖:
請求關閉階段主要內容是調用各模塊的關閉函數和析構函數、輸出緩衝區內容、調用各擴展的鉤子函數RSHUTDOWN、關閉編譯器和執行器以及還原PHP配置等。
雖然請求關閉和模塊關閉看起來是兩個大相徑庭的階段,但閱讀完源碼後發現並看不出兩個階段的區別。它的主要做用是調用模塊的flush函數、清理符號表、銷燬全局變量、關閉內存管理和垃圾回收、關閉輸出等。