php lifecycle 1 - yaokun123/php-wiki GitHub Wiki
模块初始化阶段
模块初始化入口在sapi/cli/php_cli.c中的1324行sapi_module->startup(sapi_module),调用的其实是cli_sapi_module中的模块初始化函数php_cli_startup。
php_cli_startup中又调用了main/main.c中的php_module_startup函数。
我们先来看一下该阶段的每个函数的作用。
一、sapi_initialize_request_empty函数
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宏中的成员变量进行初始化。
二、sapi_activate函数
函数的前半部分主要还是对SG宏的成员变量进行初始化。后半部分先是调用了sapi_module_struct内部实现的activate函数,又调用了input_filter_init函数,但是在CLI模式并没有实现这两个函数,只是返回了NULL。
三、php_output_startup函数
//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宏做了什么事情:
Zend/zend_API.h
#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函数又做了什么呢?
//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,在这里花了些时间,因为没找到定义在哪里,最后发现它也是通过宏定义替换得来的,代码如下:
//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输出相关的结构体。我们继续往下看(php_output_startup):
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;
}
四、php_startup_ticks函数
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进行初始化。
五、gc_globals_ctor函数
ZEND_API void gc_globals_ctor(void)
{
gc_globals_ctor_ex(&gc_globals);
}
这里又出现了一个gc_globals,它是与垃圾回收相关的结构体,这段代码是对gc_globals进行初始化。
六、zend_startup函数
-
start_memory_manager():初始化内存管理器,对结构体alloc_globals进行初始化。
-
virtual_cwd_startup():初始化了cwd_globals,根据源码可以看出成员变量都与realpath_cache有关,realpath_cache是什么呢?我们平时在写代码的时候,经常会使用include、include_once、require、require_once等语句导入文件,如果每次使用这些语句都要去对应的目录中寻找目标文件,势必会降低性能,所以官方加入了缓存,以便PHP再次使用时不必到include_path中查找,加快PHP的执行速度。
-
zend_startup_extensions_mechanism()。启动扩展机制,初始化zend_extensions结构体。
-
提供编译与执行入口
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()。设置了编译相关的配置。
-
EG(error_reporting)++。EG宏就是executor_globals,Zend执行器相关的全局变量,在这里对我们熟知的error_reporting进行配置。
-
zend_interned_strings_init()。初始化内部字符串。
-
zend_startup_builtin_functions()。初始化内部函数。
-
zend_register_standard_constants。注册常量,比如E_ERROR、E_WARNING、E_NOTICE、E_CORE_ERROR等。
-
zend_register_auto_global()。将GLOBALS加入CG(auto_globals)。
-
zend_init_rsrc_plist。初始化持久化符号表。
-
zend_init_exception_op。初始化EG(exception_op)。
-
zend_init_call_trampoline_op。初始化EG(call_trampoline_op)。
-
zend_ini_startup。初始化与php.ini解析相关的变量。
七、zend_register_list_destructors_ex()
初始化析构函数
八、php_binary_init()
获取PHP执行的二进制路径
九、php_output_register_constants()
初始化输出相关的预定义常量
十、php_rfc1867_register_constants()
注册文件上传相关的预定义常量
十一、php_init_config()
初始化配置文件php.ini,并通过zend_parse_ini_file解析
十二、zend_register_standard_ini_entries()
初始化ini相关的变量
十三、php_startup_auto_globals()
注册我们熟知的全局变量$_GET、$_POST、$_COOKIE等等
十四、php_startup_sapi_content_types()
初始化针对不同内容类型的处理函数