Dans la continuité de l'article précédent parlons du cycle de vie de php et voyons quels hooks sont étendus pour faire quoi. Le cycle de vie de php comporte environ 5 étapes, l'étape d'initialisation du module php_execute_script
, phase d'arrêt de la demande php_request_shutdown
, phase d'arrêt du module php_module_shutdown
, ce qui suit est introduit en mode 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
Jetez d'abord un œil à ce qui est fait à ce stade. Si vous ne savez pas où se trouve le fichier d'entrée php, utilisez gdb pour regarder la pile d'appels gdb ./phpcode>
Dans php_module_startup
Point d'arrêt, exécutez, regardez la pile d'appels, 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);
Vous pouvez voir clairement le processus d'exécution dans la pile d'appels, allez maintenant dans le fichier /main/main.c pour voir ce qui a été fait, vous pouvez également utiliser gdb pour étape. Prenons une vue étape par étape. Voici quelques éléments liés aux extensions PHP effectuées ici, telles que le garbage collection, l'initialisation des requêtes, les constantes d'enregistrement. , chargement du fichier de configuration php.ini, etc.