apache 接收到请求以后, 交给 php 处理php
php 模块在接收到请求后, 会对请求进行初始化, 及 RINIT 过程apache
apache 启动时注册的钩子函数 ap_hook_handler 在接收请求时触发, 实际调用的是 php_handler 函数api
ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE);
调用 php_handler数组
static int php_handler(request_rec *r) { // 省略 ... #define PHPAP_INI_OFF php_apache_ini_dtor(r, parent_req TSRMLS_CC); // 获取 php 模块配置 conf = ap_get_module_config(r->per_dir_config, &php5_module); // 设置 server_context ctx = SG(server_context); if (ctx == NULL || (ctx && ctx->request_processed && !strcmp(r->protocol, "INCLUDED"))) { normal: ctx = SG(server_context) = apr_pcalloc(r->pool, sizeof(*ctx)); apr_pool_cleanup_register(r->pool, (void *)&SG(server_context), php_server_context_cleanup, apr_pool_cleanup_null); ctx->r = r; ctx = NULL; /* May look weird to null it here, but it is to catch the right case in the first_try later on */ } else { parent_req = ctx->r; ctx->r = r; } // 应用配置 apply_config(conf); // 省略 ... // php 层面对请求进行检查, 若发现问题, 返回相应错误 if (r->used_path_info == AP_REQ_REJECT_PATH_INFO && r->path_info && r->path_info[0]) { PHPAP_INI_OFF; return HTTP_NOT_FOUND; } // 省略 ... // 构造请求 if (php_apache_request_ctor(r, ctx TSRMLS_CC)!=SUCCESS) { zend_bailout(); } // 省略 ... if (AP2(last_modified)) { ap_update_mtime(r, r->finfo.mtime); ap_set_last_modified(r); } // 判断是解析文件, 仍是不经解析展现源码 if (strncmp(r->handler, PHP_SOURCE_MAGIC_TYPE, sizeof(PHP_SOURCE_MAGIC_TYPE) - 1) == 0) { zend_syntax_highlighter_ini syntax_highlighter_ini; php_get_highlight_struct(&syntax_highlighter_ini); highlight_file((char *)r->filename, &syntax_highlighter_ini TSRMLS_CC); } else { zend_file_handle zfd; zfd.type = ZEND_HANDLE_FILENAME; zfd.filename = (char *) r->filename; zfd.free_filename = 0; zfd.opened_path = NULL; if (!parent_req) { // 执行 php 脚本 php_execute_script(&zfd TSRMLS_CC); } else { zend_execute_scripts(ZEND_INCLUDE TSRMLS_CC, NULL, 1, &zfd); } apr_table_set(r->notes, "mod_php_memory_usage", apr_psprintf(ctx->r->pool, "%" APR_SIZE_T_FMT, zend_memory_peak_usage(1 TSRMLS_CC))); } } zend_end_try(); if (!parent_req) { // 清除请求结构 php_apache_request_dtor(r TSRMLS_CC); ctx->request_processed = 1; // 省略 ... apr_pool_cleanup_run(r->pool, (void *)&SG(server_context), php_server_context_cleanup); } else { ctx->r = parent_req; } return OK; }
设置 server 上下文 server_context服务器
加载配置app
对请求进行检查, 若出现问题, 返回对应错误less
构造请求 php_apache_request_ctor函数
// 构造请求 static int php_apache_request_ctor(request_rec *r, php_struct *ctx TSRMLS_DC) { char *content_length; const char *auth; // 设置全局变量的值 SG(sapi_headers).http_response_code = !r->status ? HTTP_OK : r->status; SG(request_info).content_type = apr_table_get(r->headers_in, "Content-Type"); SG(request_info).query_string = apr_pstrdup(r->pool, r->args); SG(request_info).request_method = r->method; SG(request_info).proto_num = r->proto_num; SG(request_info).request_uri = apr_pstrdup(r->pool, r->uri); SG(request_info).path_translated = apr_pstrdup(r->pool, r->filename); r->no_local_copy = 1; content_length = (char *) apr_table_get(r->headers_in, "Content-Length"); SG(request_info).content_length = (content_length ? atol(content_length) : 0); apr_table_unset(r->headers_out, "Content-Length"); apr_table_unset(r->headers_out, "Last-Modified"); apr_table_unset(r->headers_out, "Expires"); apr_table_unset(r->headers_out, "ETag"); auth = apr_table_get(r->headers_in, "Authorization"); // 验证 auth php_handle_auth_data(auth TSRMLS_CC); if (SG(request_info).auth_user == NULL && r->user) { SG(request_info).auth_user = estrdup(r->user); } ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user); // 调用 php_request_startup 构造请求 return php_request_startup(TSRMLS_C); }
调用 php_request_startup, 初始化请求, 在这里, 程序对因而否认义了 APACHE_HOOKS 定义不一样的 php_request_startup 函数, 可是主体内容基本是一致, 如下仅针对定义过 APACHE_HOOKS 常量的 php_request_startupcode
int php_request_startup(TSRMLS_D) { int retval = SUCCESS; #if PHP_SIGCHILD signal(SIGCHLD, sigchld_handler); #endif // 启动 sapi, php 处理请求 sapi 是第一步 if (php_start_sapi() == FAILURE) { return FAILURE; } php_output_activate(TSRMLS_C); sapi_activate(TSRMLS_C); php_hash_environment(TSRMLS_C); zend_try { PG(during_request_startup) = 1; if (PG(expose_php)) { sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); } } zend_catch { retval = FAILURE; } zend_end_try(); return retval; }
php_start_sapi 启动 sapi, sapi 是处理 php 请求的入口, 第一步orm
// 启动 sapi static int php_start_sapi(TSRMLS_D) { int retval = SUCCESS; if(!SG(sapi_started)) { zend_try { PG(during_request_startup) = 1; // 初始化变量 PG(modules_activated) = 0; PG(header_is_being_sent) = 0; PG(connection_status) = PHP_CONNECTION_NORMAL; // zend 引擎激活 zend_activate(TSRMLS_C); // 设置超时时间 zend_set_timeout(EG(timeout_seconds), 1); // 激活各子模块 zend_activate_modules(TSRMLS_C); // 设置模块激活标识 PG(modules_activated)=1; } zend_catch { retval = FAILURE; } zend_end_try(); SG(sapi_started) = 1; } return retval; }
调用 zend_activate, 激活 zend 引擎
// 激活 zend 引擎 void zend_activate(TSRMLS_D) /* {{{ */ { // 重置垃圾回收 gc_reset(TSRMLS_C); // 初始化编译器 init_compiler(TSRMLS_C); // 初始化执行器 init_executor(TSRMLS_C); startup_scanner(TSRMLS_C); }
gc_reset, 重置 gc 垃圾回收全局结构
ZEND_API void gc_reset(TSRMLS_D) { GC_G(gc_runs) = 0; GC_G(collected) = 0; #if GC_BENCH GC_G(root_buf_length) = 0; GC_G(root_buf_peak) = 0; GC_G(zval_possible_root) = 0; GC_G(zobj_possible_root) = 0; GC_G(zval_buffered) = 0; GC_G(zobj_buffered) = 0; GC_G(zval_remove_from_buffer) = 0; GC_G(zobj_remove_from_buffer) = 0; GC_G(zval_marked_grey) = 0; GC_G(zobj_marked_grey) = 0; #endif GC_G(roots).next = &GC_G(roots); GC_G(roots).prev = &GC_G(roots); if (GC_G(buf)) { GC_G(unused) = NULL; GC_G(first_unused) = GC_G(buf); GC_G(zval_to_free) = NULL; } else { GC_G(unused) = NULL; GC_G(first_unused) = NULL; GC_G(last_unused) = NULL; } }
init_compiler, 初始化编译器, 主要指的是初始化 compiler_globals 全局结构体的值以及初始化资源列表
void init_compiler(TSRMLS_D) { // 设置 opcode 命令数组为空 CG(active_op_array) = NULL; memset(&CG(context), 0, sizeof(CG(context))); // 初始化全局编译结构 zend_init_compiler_data_structures(TSRMLS_C); // 初始化资源列表 zend_init_rsrc_list(TSRMLS_C); zend_hash_init(&CG(filenames_table), 5, NULL, (dtor_func_t) free_estring, 0); zend_llist_init(&CG(open_files), sizeof(zend_file_handle), (void (*)(void *)) file_handle_dtor, 0); CG(unclean_shutdown) = 0; }
init_executor, 初始化执行器, 主要是设置 executor_globals 全局执行器结构的值
// 初始化全局执行器 void init_executor(TSRMLS_D) /* {{{ */ { // 初始化浮点运算单元 zend_init_fpu(TSRMLS_C); // 初始化 zval 结构 INIT_ZVAL(EG(uninitialized_zval)); // 将 zval 结构的 refcount__gc 自增 1 Z_ADDREF(EG(uninitialized_zval)); INIT_ZVAL(EG(error_zval)); EG(uninitialized_zval_ptr)=&EG(uninitialized_zval); EG(error_zval_ptr)=&EG(error_zval); /* destroys stack frame, therefore makes core dumps worthless */ #if 0&&ZEND_DEBUG original_sigsegv_handler = signal(SIGSEGV, zend_handle_sigsegv); #endif EG(return_value_ptr_ptr) = NULL; EG(symtable_cache_ptr) = EG(symtable_cache) - 1; EG(symtable_cache_limit) = EG(symtable_cache) + SYMTABLE_CACHE_SIZE - 1; EG(no_extensions) = 0; EG(function_table) = CG(function_table); EG(class_table) = CG(class_table); EG(in_execution) = 0; EG(in_autoload) = NULL; EG(autoload_func) = NULL; EG(error_handling) = EH_NORMAL; zend_vm_stack_init(TSRMLS_C); zend_vm_stack_push((void *) NULL TSRMLS_CC); zend_hash_init(&EG(symbol_table), 50, NULL, ZVAL_PTR_DTOR, 0); EG(active_symbol_table) = &EG(symbol_table); zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_activator TSRMLS_CC); EG(opline_ptr) = NULL; zend_hash_init(&EG(included_files), 5, NULL, NULL, 0); EG(ticks_count) = 0; EG(user_error_handler) = NULL; EG(current_execute_data) = NULL; zend_stack_init(&EG(user_error_handlers_error_reporting)); zend_ptr_stack_init(&EG(user_error_handlers)); zend_ptr_stack_init(&EG(user_exception_handlers)); // 初始化对象池 zend_objects_store_init(&EG(objects_store), 1024); EG(full_tables_cleanup) = 0; #ifdef ZEND_WIN32 EG(timed_out) = 0; #endif EG(exception) = NULL; EG(prev_exception) = NULL; EG(scope) = NULL; EG(called_scope) = NULL; EG(This) = NULL; EG(active_op_array) = NULL; EG(active) = 1; EG(start_op) = NULL; }
startup_scanner, 启动扫描器, 初始化扫描器
void startup_scanner(TSRMLS_D) { CG(parse_error) = 0; CG(doc_comment) = NULL; CG(doc_comment_len) = 0; zend_stack_init(&SCNG(state_stack)); zend_ptr_stack_init(&SCNG(heredoc_label_stack)); }
扫描器结构
// 扫描器 struct _zend_php_scanner_globals { zend_file_handle *yy_in; zend_file_handle *yy_out; unsigned int yy_leng; unsigned char *yy_start; unsigned char *yy_text; unsigned char *yy_cursor; unsigned char *yy_marker; unsigned char *yy_limit; int yy_state; zend_stack state_stack; zend_ptr_stack heredoc_label_stack; /* original (unfiltered) script */ unsigned char *script_org; size_t script_org_size; /* filtered script */ unsigned char *script_filtered; size_t script_filtered_size; /* input/output filters */ zend_encoding_filter input_filter; zend_encoding_filter output_filter; const zend_encoding *script_encoding; };
调用 zend_activate_modules, 调用各子模块的 request_startup_func 函数, 各子模块结构在 sapi MINIT 时被记录下来
void zend_activate_modules(TSRMLS_D) { // module_request_startup_handlers 在 sapi MINIT 的 zend_collect_module_handlers 时被记录下来 zend_module_entry **p = module_request_startup_handlers; // 循环遍历, 调用各模块的 request_startup_func 函数 while (*p) { zend_module_entry *module = *p; if (module->request_startup_func(module->type, module->module_number TSRMLS_CC)==FAILURE) { zend_error(E_WARNING, "request_startup() for %s module failed", module->name); exit(1); } p++; } }
判断是否解析文件
执行 php 脚本
清除请求结构
request RINIT 是每一个请求传给 php 时均须要执行的
RINIT 过程主要是为每一个请求设置服务器环境以及相关的各类配置, 以便后续执行
RINIT 过程的主要部分在于 php_request_startup 函数, 该函数的主要做用以下:
- 启动 sapi
- 激活 zend 引擎
- 重置垃圾回收
- 初始化编译器
- 初始化执行器
- 初始化扫描器