이전 기사에 이어 PHP 수명 주기에 대해 이야기하고 어떤 후크가 무엇을 수행하기 위해 확장되는지 살펴보겠습니다. PHP 수명 주기는 모듈 초기화 단계 와 같이 약 5단계로 구성됩니다. >php_module_startup code>, 요청 초기화 단계 php_request_startup
, 스크립트 실행 단계 php_execute_script
, 요청 종료 단계 php_request_shutdown
, 모듈 종료 단계 php_module_shutdown
, 다음은 cli 모드에 도입되었습니다. php_module_startup
,请求初始化阶段php_request_startup
,脚本执行阶段php_execute_script
,请求关闭阶段php_request_shutdown
,模块关闭阶段php_module_shutdown
,下面以cli模式介绍。
php_module_startup
先看看这个阶段做了什么,如果不知道php入口文件在哪,用gdb看看调用栈,gdb ./php
在php_module_startup
打断点,执行,在看下调用栈,
b php_module_startup (gdb) r test.php bt php_module_startup (sf=0x1406460 <cli_sapi_module>, additional_modules=0x0, num_additional_modules=0) at /www/test/php/php-7.4.3/main/main.c:2098 #1 0x00000000008bae7c in php_cli_startup ( sapi_module=0x1406460 <cli_sapi_module>) at /www/test/php/php-7.4.3/sapi/cli/php_cli.c:407 #2 0x00000000008bcc80 in main (argc=2, argv=0x1425af0) at /www/test/php/php-7.4.3/sapi/cli/php_cli.c:1323
在调用栈可以清楚看到执行流程,现在到/main/main.c文件看看做了哪些事情,也可以用gdb一步一步的看,这里就讲与php扩展有关的几个地方,这里做的初始化工作,像垃圾回收,请求初始化,注册常量,php.ini配置文件加载等,
先来看看怎么加载模块的
/* startup extensions statically compiled in */ if (php_register_internal_extensions_func() == FAILURE) { php_printf("Unable to start builtin modules\n"); return FAILURE; }
这里是加载php内置的模块,这里只贴出核心功能,先检查依赖
/* Check module dependencies */ if (module->deps) { const zend_module_dep *dep = module->deps; while (dep->name) { if (dep->type == MODULE_DEP_CONFLICTS) { name_len = strlen(dep->name); lcname = zend_string_alloc(name_len, 0); zend_str_tolower_copy(ZSTR_VAL(lcname), dep->name, name_len); if (zend_hash_exists(&module_registry, lcname) || zend_get_extension(dep->name)) { zend_string_efree(lcname); /* TODO: Check version relationship */ zend_error(E_CORE_WARNING, "Cannot load module '%s' because conflicting module '%s' is already loaded", module->name, dep->name); return NULL; } zend_string_efree(lcname); } ++dep; } }
if (module->functions && zend_register_functions(NULL, module->functions, NULL, module->type)==FAILURE) { zend_hash_del(&module_registry, lcname); zend_string_release(lcname); EG(current_module) = NULL; zend_error(E_CORE_WARNING,"%s: Unable to register functions, unable to load", module->name); return NULL; }
这是内置模块加载原理,现在看看ini里的扩展怎么加载
php_ini_register_extensions();
zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb);
利用这个函数加载
php_load_extension(char *filename, int type, int start_now)
这里面也执行了加载内置模块的功能。
是调用了module->functions
,进行模块功能函数注册,现在知道了为什么功能函数要写在helloworld_functions
这里吧
zend_module_entry helloworld_module_entry = { STANDARD_MODULE_HEADER, "helloworld", /* Extension name */ helloworld_functions, /* zend_function_entry */ PHP_MINIT(helloworld), /* PHP_MINIT - Module initialization */ NULL, /* PHP_MSHUTDOWN - Module shutdown */ PHP_RINIT(helloworld), /* PHP_RINIT - Request initialization */ NULL, /* PHP_RSHUTDOWN - Request shutdown */ PHP_MINFO(helloworld), /* PHP_MINFO - Module info */ PHP_HELLOWORLD_VERSION, /* Version */ PHP_MODULE_GLOBALS(pib), NULL, NULL, NULL, STANDARD_MODULE_PROPERTIES_EX };
现在看看扩展的几个钩子函数
/* start Zend extensions */ zend_startup_extensions();
这里的核心就是func(element->data)
也就是执行扩展
PHP_MINIT
函数
element=l->head; while (element) { next = element->next; if (func(element->data)) { DEL_LLIST_ELEMENT(element, l); } element = next; }
现在就知道PHP_MINIT
먼저 이 단계에서 수행되는 작업을 살펴보세요. PHP 항목 파일이 어디에 있는지 모르면 gdb를 사용하여 gdb ./php를 살펴보세요. code>
php_module_startup
에서 중단점, 실행, 콜 스택 살펴보기, original = zend_hash_str_find_ptr(CG(function_table), "var_dump", sizeof("var_dump")-1);
if (original != NULL) {
original->internal_function.handler = my_overwrite_var_dump;
}
zend_class_entry person;
INIT_CLASS_ENTRY(person,CLASS_NAME,person_functions);
zend_register_internal_class_ex(&person,NULL);
콜 스택에서 실행 과정을 명확하게 볼 수 있으니 이제 /main/main.c 파일로 이동하세요. 수행된 작업을 보려면 gdb를 사용하여 단계별로 살펴보겠습니다. 다음은 가비지 수집, 요청 초기화, 등록 상수와 같은 PHP 확장과 관련된 몇 가지 작업입니다. , php.ini 구성 파일 로드 등