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()

初始化针对不同内容类型的处理函数