SAPI
Zend VM
内部扩展
Zend VM是PHP的虚拟机与JVM类似都是各自语言的编译/执行的核心。它们都会把各自的代码先编译为一种中间代码PHP的通常叫 opcodeJava通常叫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时要小心多进程多线程相关的问题。
<img src="http://www.ahlinux.com/uploadfile/2015/0314/20150314114134554.jpg" title="b□" http:="" www.ahlinux.com="" php="" "="" target="_blank" class="keylink" data-bd-imgshare-binded="1" style="margin: 0px; padding: 3px; border: 1px solid rgb(204, 204, 204); max-width: 660px;">php-src/ext/ext_skel可以生成PHP扩展的框架
./ext_skel --extname=myext [tan@tan ~/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_H extern 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_MYEXT ZEND_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; } //模块信息phpinfo PHP_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); }
本文来自Linux教程网