php扩展开发-内核执行流程与扩展结构
在开发扩展之前,最好了解下php内核的执行流程,php大概包括三个方面: sapi
zend vm
内部扩展
zend vm是php的虚拟机,与jvm类似,都是各自语言的编译/执行的核心。它们都会把各自的代码先编译为一种中间代码,php的通常叫opcode,java通常叫bytecode,不同的是php的opcode直接被zend vm的执行单元调用对应的c函数执行,不会显示保留下来(可以cache保留),而java通常是生成class文件保留下来。而这一点可能也是php interpreter的名称的由来吧。其实相对严格的c/c++等编译型语言,php和java更多的是结合了编译型和解释性的风格。sapi可以看作是zend vm向外界提供编译/执行php代码服务的方式和规范。无论是作为cli/cgi/fastcgi/apache_mod与其他程序交互,还是嵌入其他语言中如c/c++等,都可以通过sapi的规范实现。它的一个重要数据结构就是sapi_module_struct(main/sapi.h line 217)内部扩展部分可以看作是搭建在zend vm和sapi之上的库,为php开发人员提供性能和易用性上的保证。java的各种包/python的各种模块功能类似,不同的是php中为了性能是用c扩展来实现的,类似的在java中可以通过jni来实现,python中如_socket和_select(多路复用)都不是原生python实现。生命周期关于各种sapi或者php本身的生命周期,可能会和其他组件如apache耦合,后续再细谈。关于php扩展的生命周期,这里借用一张图。流程应该是很容易明白的,关于这个过程,网上也有很多资料,不再赘述。我们开发扩展需要注意的几个地方也可以对应到图中的某些节点:
全局变量的定义,通常是zend_modulename_globals
模块的初始化,包括资源/类/常量/ini配置等模块级的初始化
请求的初始化,包括与单次请求相关的一些初始化
请求的结束,清理单次请求相关的数据/内存
模块的卸载,清理模块相关的数据/内存
基本上我们要做的就是按照上面的流程,实现相关的内置函数,定义自己的资源/全局变量/类/函数等。值得注意的地方是在在嵌入其他语言如python或者被嵌入其他组件如apache时,要小心多进程多线程相关的问题。
php扩展结构使用php-src/ext/ext_skel可以生成php扩展的框架
./ext_skel --extname=myext[[email protected] ~/software/needbak/php-5.5.20/ext 12:24]$==> ls myext/config.m4 config.w32 credits experimental myext.c myext.php php_myext.h tests
比较重要的文件是config.m4(当然还有源码),config.m4文件可以使用phpize命令生成configure文件,其中说明了我们是否开启模块,以及外部依赖的库。
//config.m4//如果你的扩展依赖其他外部库dnl php_arg_with(myext, for myext support,dnl make sure that the comment is aligned:dnl [ --with-myext include myext support])//扩展不依赖外部库dnl php_arg_enable(myext, whether to enable myext support,dnl make sure that the comment is aligned:dnl [ --enable-myext enable myext support])//寻找并包含头文件if test $php_myext != no; then dnl write more examples of tests here... dnl # --with-myext -> check with-path dnl search_path=/usr/local /usr # you might want to change this dnl search_for=/include/myext.h # you most likely want to change this dnl if test -r $php_myext/$search_for; then # path given as parameter dnl myext_dir=$php_myext dnl else # search default path list dnl ac_msg_checking([for myext files in default path]) dnl for i in $search_path ; do dnl if test -r $i/$search_for; then dnl myext_dir=$i dnl ac_msg_result(found in $i) dnl fi dnl done dnl fi dnl dnl if test -z $myext_dir; then dnl ac_msg_result([not found]) dnl ac_msg_error([please reinstall the myext distribution]) dnl fi dnl # --with-myext -> add include path dnl php_add_include($myext_dir/include) //加载的lib位置 dnl # --with-myext -> check for lib and symbol presence dnl libname=myext # you may want to change this dnl libsymbol=myext # you most likely want to change this dnl php_check_library($libname,$libsymbol, dnl [ dnl php_add_library_with_path($libname, $myext_dir/$php_libdir, myext_shared_libadd) dnl ac_define(have_myextlib,1,[ ]) dnl ],[ dnl ac_msg_error([wrong myext lib version or lib not found]) dnl ],[ dnl -l$myext_dir/$php_libdir -lm dnl ]) dnl dnl php_subst(myext_shared_libadd) php_new_extension(myext, myext.c, $ext_shared)fi
//php_myext.h#ifndef php_myext_h#define php_myext_hextern zend_module_entry myext_module_entry;#define phpext_myext_ptr &myext_module_entry//导出符号,在链接的时候有用#ifdef php_win32# define php_myext_api __declspec(dllexport)#elif defined(__gnuc__) && __gnuc__ >= 4# define php_myext_api __attribute__ ((visibility(default)))#else# define php_myext_api#endif#ifdef zts#include tsrm.h#endif//几个核心函数的声明php_minit_function(myext);php_mshutdown_function(myext);php_rinit_function(myext);php_rshutdown_function(myext);php_minfo_function(myext);//自动生成的测试函数声明,我们自己定义的模块函数需要在此声明php_function(confirm_myext_compiled); //全局变量在这定义,展开后是zend_myext_globals结构体zend_begin_module_globals(myext) long global_value; char *global_string;zend_end_module_globals(myext)//线程安全与非线程安全下获取全局变量的方式#ifdef zts#define myext_g(v) tsrmg(myext_globals_id, zend_myext_globals *, v)#else#define myext_g(v) (myext_globals.v)#endif#endif /* php_myext_h */
//myext.c#ifdef have_config_h#include config.h#endif#include php.h#include php_ini.h#include ext/standard/info.h#include php_myext.h//全局变量声明zend_declare_module_globals(myext)/* true global resources - no need for thread safety here */static int le_myext;//模块函数的导出const zend_function_entry myext_functions[] = { php_fe(confirm_myext_compiled, null) /* for testing, remove later. */ php_fe_end /* must be the last line in myext_functions[] */};//模块结构zend_module_entry myext_module_entry = {#if zend_module_api_no >= 20010901 standard_module_header,#endif myext, myext_functions, php_minit(myext), php_mshutdown(myext), php_rinit(myext), /* replace with null if there's nothing to do at request start */ php_rshutdown(myext), /* replace with null if there's nothing to do at request end */ php_minfo(myext),#if zend_module_api_no >= 20010901 php_myext_version,#endif standard_module_properties};#ifdef compile_dl_myextzend_get_module(myext)#endif//ini配置文件的设置php_ini_begin() std_php_ini_entry(myext.global_value, 42, php_ini_all, onupdatelong, global_value, zend_myext_globals, myext_globals) std_php_ini_entry(myext.global_string, foobar, php_ini_all, onupdatestring, global_string, zend_myext_globals, myext_globals)php_ini_end()//初始化全局变量static void php_myext_init_globals(zend_myext_globals *myext_globals){ myext_globals->global_value = 0; myext_globals->global_string = null;}//模块加载时的函数php_minit_function(myext){ /* if you have ini entries, uncomment these lines register_ini_entries(); */ return success;}//模块卸载时函数php_mshutdown_function(myext){ /* uncomment this line if you have ini entries unregister_ini_entries(); */ return success;}//请求初始化函数php_rinit_function(myext){ return success;}//请求关闭函数php_rshutdown_function(myext){ return success;}//模块信息,phpinfophp_minfo_function(myext){ php_info_print_table_start(); php_info_print_table_header(2, myext support, enabled); php_info_print_table_end(); /* remove comments if you have entries in php.ini display_ini_entries(); */}//测试函数php_function(confirm_myext_compiled){ char *arg = null; int arg_len, len; char *strg; if (zend_parse_parameters(zend_num_args() tsrmls_cc, s, &arg, &arg_len) == failure) { return; } len = spprintf(&strg, 0, congratulations! you have successfully modified ext/%.78s/config.m4. module %.78s is now compiled into php., myext, arg); return_stringl(strg, len, 0);}
