设为首页 收藏本站
查看: 481|回复: 0

[经验分享] 原:用C/C++扩展PHP

[复制链接]

尚未签到

发表于 2017-3-27 06:48:37 | 显示全部楼层 |阅读模式
声明:本文为
斯人
原创,全部为作者一一分析得之,有不对的地方望赐教。

欢迎转载,转载请注明出处 。

  
本文地址:
http://imsiren.com/archives/547
  
一个简单的扩展模块

PHP非常容易扩展,因为它提供了我们想用的所有API.

如果要新建一个扩展,需要在PHP源码中执行ext_skel

位置 PHP源码目录/ext/ext_skel

它有几个参数

–extname=module module is the name of your extension

–proto=file file contains prototypes of functions to create

–stubs=file generate only function stubs in file

–xml generate xml documentation to be added to phpdoc-cvs

–skel=dir path to the skeleton directory

–full-xml generate xml documentation for a self-contained extension

(not yet implemented)

–no-help don’t try to be nice and create comments in the code

and helper functions to test if the module compiled

如果我们要建一个 扩展名称为siren的模块,那么我们只要执行

ext_skel –extname=siren 它就会在 ext/目录下生成以 模块名称为名的文件夹.而且还会创建一些文件:

config.m4 config.w32 CREDITS EXPERIMENTAL php_siren.h siren.c siren.php tests

config.m4 和config.w32是我们的配置文件,我是在linux下编译的 所以要修改config.m4文件

两种加载方式 with 和 enable
<!--EndFragment-->

  

dnl PHP_ARG_WITH(siren, for siren support,dnl Make sure that the comment is aligned:dnl [  --with-siren             Include siren support])dnl Otherwise use enable:dnl PHP_ARG_ENABLE(siren, whether to enable siren support,dnl Make sure that the comment is aligned:dnl [  --enable-siren           Enable siren support])
enable方式 需要重新编译PHP ,这样是非常浪费时间的,所以我把它编译为so模块..

所以就用 with啦

dnl PHP_ARG_WITH(siren, for siren support,

dnl Make sure that the comment is aligned:

dnl [ --with-siren Include siren support])


PHP_ARG_WITH(siren, for siren support,

Make sure that the comment is aligned:

[ --with-siren Include siren support])

这样在编译PHP的时候 –with-siren就可以加载此模块,也可以在php.ini中extension 模块.

在ext/siren目录下有一个siren.c文件

它提供了一个默认函数
<!--EndFragment-->
  

PHP_FUNCTION(confirm_siren_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.", "siren", arg);RETURN_STRINGL(strg, len, 0);}
如果看过 我之前的文章,你肯定明白 如果不知道 那就看看这篇文章
http://imsiren.com/archives/196
下面看看如何编译到PHP
1. /usr/local/php53/bin/phpize
2../configure –with-php-config=/usr/local/php53/bin/php-config
3.make && make install
这样 就会在/usr/local/php53/lib/php/extensions/no-debug-non-zts-20090626/目录下生成一个siren.so文件
这样 一个简单的扩展模块就完成了..我们在PHP.INI里面开启此模块
重启apache/nginx, 这样 在php文件里 就可以 执行 confirm_siren_compiled函数了.

下面我们就详细讲解一下里面的东西
首先是 php_siren.h
它是siren.c加载的头文件

<!--EndFragment-->#ifndef PHP_SIREN_H#define PHP_SIREN_Hextern zend_module_entry siren_module_entry;#define phpext_siren_ptr &siren_module_entry#ifdef PHP_WIN32#       define PHP_SIREN_API __declspec(dllexport)#elif defined(__GNUC__) && __GNUC__ >= 4#       define PHP_SIREN_API __attribute__ ((visibility("default")))#else#       define PHP_SIREN_API#endif#ifdef ZTS#include "TSRM.h"#endifPHP_MINIT_FUNCTION(siren);PHP_MSHUTDOWN_FUNCTION(siren);PHP_RINIT_FUNCTION(siren);PHP_RSHUTDOWN_FUNCTION(siren);PHP_MINFO_FUNCTION(siren);PHP_FUNCTION(confirm_siren_compiled);   /*这是一个测试函数*//*如果要声明全局变量,就在这里声明     如果要启用 全局变量,那还要把siren.c中的ZEND_DECLARE_MODULE_GLOBALS(siren)注释去掉ZEND_BEGIN_MODULE_GLOBALS(siren)long  global_value;char *global_string;ZEND_END_MODULE_GLOBALS(siren)*//* In every utility function you add that needs to use variablesin php_siren_globals, call TSRMLS_FETCH(); after declaring othervariables used by that function, or better yet, pass in TSRMLS_CCafter the last function argument and declare your utility functionwith TSRMLS_DC after the last declared argument.  Always refer tothe globals in your function as SIREN_G(variable).  You areencouraged to rename these macros something shorter, seeexamples in any other php module directory.*/#ifdef ZTS#define SIREN_G(v) TSRMG(siren_globals_id, zend_siren_globals *, v)#else#define SIREN_G(v) (siren_globals.v)#endif#endif  /* PHP_SIREN_H */
上面有几个 PHP_*的函数,他们的作用如下

PHP_MINIT_FUNCTION() 当PHP被装载时,模块启动函数即被Zend引擎调用,这里可以做一些初始化操作
PHP_MSHUTDOWN_FUNCTION()当PHP完全关闭时,Zend引擎调用的函数,
PHP_RINIT_FUNCTION() 在每次PHP请求开始,请求前启动函数被调用。通常用于管理请求前逻辑。
PHP_RSHUTDOWN_FUNCTION()在每次PHP请求结束后,请求前关闭函数被调用。经常应用在清理请求前启动函数的逻辑。
PHP_MINFO_FUNCTION() 调用phpinfo()时模块信息函数被呼叫,从而打印出模块信息。
这些函数的代码都定义在siren.c文件中.

<!--EndFragment-->#ifdef HAVE_CONFIG_H#include "config.h"#endif#include "php.h"#include "php_ini.h"#include "ext/standard/info.h"#include "php_siren.h"/* 如果php_siren.h中开启了全局变量,那就去掉注释ZEND_DECLARE_MODULE_GLOBALS(siren)*//* True global resources - no need for thread safety here */static int le_siren;/* {{{ siren_functions[]** Every user visible function must have an entry in siren_functions[].*/const zend_function_entry siren_functions[] = {PHP_FE(confirm_siren_compiled,  NULL)           /* For testing, remove later. */PHP_FE_END      /* Must be the last line in siren_functions[] */};/* }}} *//* {{{ siren_module_entry*/zend_module_entry siren_module_entry = {#if ZEND_MODULE_API_NO >= 20010901STANDARD_MODULE_HEADER,#endif"siren",siren_functions,PHP_MINIT(siren),PHP_MSHUTDOWN(siren),PHP_RINIT(siren),               /* Replace with NULL if there's nothing to do at request start */PHP_RSHUTDOWN(siren),   /* Replace with NULL if there's nothing to do at request end */PHP_MINFO(siren),#if ZEND_MODULE_API_NO >= 20010901"0.1", /* Replace with version number for your extension */#endifSTANDARD_MODULE_PROPERTIES};/* }}} */#ifdef COMPILE_DL_SIRENZEND_GET_MODULE(siren)#endif/* {{{ PHP_INI*//* Remove comments and fill if you need to have entries in php.iniPHP_INI_BEGIN()STD_PHP_INI_ENTRY("siren.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_siren_globals, siren_globals)STD_PHP_INI_ENTRY("siren.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_siren_globals, siren_globals)PHP_INI_END()*//* }}} *//* {{{ php_siren_init_globals*//* Uncomment this function if you have INI entriesstatic void php_siren_init_globals(zend_siren_globals *siren_globals){siren_globals->global_value = 0;siren_globals->global_string = NULL;}*//* }}} *//* {{{ PHP_MINIT_FUNCTION*/PHP_MINIT_FUNCTION(siren){/* If you have INI entries, uncomment these linesREGISTER_INI_ENTRIES();*/return SUCCESS;}/* }}} *//* {{{ PHP_MSHUTDOWN_FUNCTION*/PHP_MSHUTDOWN_FUNCTION(siren){/* uncomment this line if you have INI entriesUNREGISTER_INI_ENTRIES();*/return SUCCESS;}/* }}} *//* Remove if there's nothing to do at request start *//* {{{ PHP_RINIT_FUNCTION*/PHP_RINIT_FUNCTION(siren){return SUCCESS;}/* }}} *//* Remove if there's nothing to do at request end *//* {{{ PHP_RSHUTDOWN_FUNCTION*/PHP_RSHUTDOWN_FUNCTION(siren){return SUCCESS;}/* }}} *//* {{{ PHP_MINFO_FUNCTION*/PHP_MINFO_FUNCTION(siren){php_info_print_table_start();php_info_print_table_header(2, "siren support", "enabled");php_info_print_table_end();/* Remove comments if you have entries in php.iniDISPLAY_INI_ENTRIES();*/}/* Every user-visible function in PHP should document itself in the source *//* {{{ proto string confirm_siren_compiled(string arg)Return a string to confirm that the module is compiled in */PHP_FUNCTION(confirm_siren_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.", "siren", arg);RETURN_STRINGL(strg, len, 0);}/* }}} */
第21行 zend_function_entry是一个结构体
  
<!--EndFragment-->

typedef struct _zend_function_entry {const char *fname; //函数名称void (*handler)(INTERNAL_FUNCTION_PARAMETERS);  //指向对应 C 函数的句柄const struct _zend_arg_info *arg_info;//函数的参数信息zend_uint num_args;//参数个数zend_uint flags;} zend_function_entry;
const zend_function_entry siren_functions[] = {PHP_FE(confirm_siren_compiled,  NULL)           /* For testing, remove later. */PHP_FE_END      /* Must be the last line in siren_functions[] */};
上面就是定义了一个函数数组
PHP_FE是一个宏.
等于
#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags },
只是做了一些初始化.
PHP_FE_END 等于 { NULL, NULL, NULL, 0, 0 }
用来结束数组

zend_module_entry 是一个结构体,用来保存模块信息

<!--EndFragment-->struct _zend_module_entry {unsigned short size;  //模块结构的大小unsigned int zend_api;unsigned char zend_debug; //是否wie调试版本unsigned char zts;  //是否启用了 线程安全const struct _zend_ini_entry *ini_entry;       //不详const struct _zend_module_dep *deps;  //不详const char *name; //模块名称const struct _zend_function_entry *functions;  //Zend 函数块的指针 指向zend_function_entry结构数组int (*module_startup_func)(INIT_FUNC_ARGS);   //模块启动函数int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);        //模块停止函数int (*request_startup_func)(INIT_FUNC_ARGS);  //php请求开始执行的函数int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);//php请求结束执行的函数void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);//模块信息函数。当脚本调用 phpinfo() 函数时,Zend 便会遍历所有已加载的模块,并调用它们的这个函数。每个模块都有机会输出自己的信息。const char *version;//模块版本号size_t globals_size;#ifdef ZTSts_rsrc_id* globals_id_ptr;#elsevoid* globals_ptr;#endifvoid (*globals_ctor)(void *global TSRMLS_DC);void (*globals_dtor)(void *global TSRMLS_DC);int (*post_deactivate_func)(void);int module_started;unsigned char type;void *handle;int module_number;char *build_id;};
主要字段都在代码里注释了

创建一个 zend_module_entry对象
  
<!--EndFragment-->

zend_module_entry siren_module_entry = {#if ZEND_MODULE_API_NO >= 20010901STANDARD_MODULE_HEADER,#endif"siren",  //模块名称siren_functions, //函数列表 ,执行了上面定义的 zend_function_entryPHP_MINIT(siren), //模块开始执行的函数PHP_MSHUTDOWN(siren),//模块结束执行的函数PHP_RINIT(siren),   //PHP开始请求执行的函数PHP_RSHUTDOWN(siren),  //PHP请求结束执行的函数PHP_MINFO(siren),#if ZEND_MODULE_API_NO >= 20010901"0.1", //版本#endifSTANDARD_MODULE_PROPERTIES};
STANDARD_MODULE_HEADER宏:

sizeof(zend_module_entry), ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS

用来填充 前面四个参数

第48行:

只有你的模块编译成 动态模块的时候才会被调用.这个函数的作用就是把模块的信息块传递 给Zend 并通知 Zend 获取这个模块的相关内容

54-57行:

我们在写PHP的时候,php.ini里面的配置都会影响我们PHP代码的执行,比如register_global 等.

此处代码的作用就是获取php.ini里面的配置信息.
  
<!--EndFragment-->

STD_PHP_INI_ENTRY("siren.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_siren_globals, siren_globals)
STD_PHP_INI_ENTRY宏:注册php INI的指令:
接受的参数列表如下
name:php.ini里面的名称
default_value://默认值,永远都是字符串

modifiable:ini可以被改变的地方 值如下
PHP_INI_SYSTEM. 能够在php.ini或http.conf等系统文件更改
PHP_INI_PERDIR. 能够在 .htaccess中更改
PHP_INI_USER. 能够被用户脚本更改
PHP_INI_ALL. 能够在所有地方更改
on_modify:处理INI条目更改的回调函数。你不需自己编写处理程序,使用下面提供的函数。包括:

OnUpdateInt
OnUpdateString
OnUpdateBool
OnUpdateStringUnempty
OnUpdateReal
property_name:应当被更新的变量名
struct_type:变量驻留的结构类型。因为通常使用全局变量机制,所以这个类型自动被定义,类似于zend_myfile_globals。
struct_ptr:全局结构名。如果使用全局变量机制,该名为myfile_globals。

剩下的东西就是我们上面提到的一些 启动模块时执行的函数…
明白了这些,再去写模块头就不会大啦…

原文出处:http://imsiren.com/archives/547
<!--EndFragment-->

<!--EndFragment-->

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-355780-1-1.html 上篇帖子: php getmypid函数-PHP 选项及相关信息函数库 下篇帖子: php 给图片加水印
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表