当前位置: 首页 > 后端技术 > PHP

php自定义扩展(二)

时间:2023-03-29 14:44:31 PHP

接下来说说php的生命周期,看看扩展哪些hooks。php生命周期大概有5个阶段,模块初始化阶段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断点,执行,看调用堆栈,bphp_module_startup(gdb)rtest.phpbtphp_module_startup(sf=0x1406460,additional_modules=0x0,num_additional_modules=0)at/www/test/php/php-7.4.3/main/main.c:2098#10x00000000008bae7cinphp_cli_startup(<460_modules=cli_sapi_module>)at/www/test/php/php-7.4.3/sapi/cli/php_cli.c:407#20x00000000008bcc80inmain(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配置文件加载等,我们先来看看如何加载模块。/if(php_register_internal_extensions_func()==FAILURE){php_printf("无法启动内置模块\n");返回失败;}这里是加载php内置的模块,这里只贴出核心功能,先检查依赖/*Checkmoduledependencies*/if(module->deps){constzend_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:检查版本关系*/zend_error(E_CORE_WARNING,"Cannotloadmodule'%s'becauseconflictingmodule'%s'isalreadyloaded",module->name,dep->name);返回空值;}zend_string_efree(lcname);}++部门;}}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:无法注册函数,无法加载",module->name);返回空值;}这就是内置模块加载的原理,现在来看看ini中的extension是如何加载php_ini_register_extensions();zend_llist_apply(&extension_lists.functions,php_load_php_extension_cb);使用此函数加载php_load_extension(char*filename,inttype,intstart_now)也执行加载内置模块功能就是调用module->functions注册模块函数函数。现在知道为什么function函数要写在helloworld_functions这里了。zend_module_entryhelloworld_module_entry={STANDARD_MODULE_HEADER,"helloworld",/*扩展名*/helloworld_functions,/*zend_function_entry*/PHP_M(helloworld),/*PHP_MINIT-模块初始化*/NULL,/*PHP_MSHUTDOWN-模块关闭*/PHP_RINIT(helloworld),/*PHP_RINIT-请求初始化*/NULL,/*PHP_RSHUTDOWN-请求关闭*/PHP_MINFO(helloworld),/*PHP_MINFO-模块信息*/PHP_HELLOWORLD_VERSION,/*版本*/PHP_MODULE_GLOBALS(pib),NULL,NULL,NULL,STANDARD_MODULE_PROPERTIES_EX};现在看几个扩展钩子函数/*startZendextensions*/zend_startup_extensions();这里的核心是func(element->data),就是执行扩展的PHP_MINIT函数element=l->head;while(元素){下一个=元素->下一个;if(func(element->data)){DEL_LLIST_ELEMENT(element,l);}元素=下一个;}现在我们知道了PHP_MINIThook可以做很多初始化函数,如何注册自定义扩展函数Class,如何将扩展变量写入php.ini,如何重写php内置函数,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人;INIT_CLASS_ENTRY(人,CLASS_NAME,person_functions);zend_register_internal_class_ex(&person,NULL);这里是重写var_dump函数,在这里注册一个person类,先介绍一下小编将介绍如何通过php代码的词法分析和语法分析生成AST,然后编译opcode指令供zend虚拟机调用