原文:http://www.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

1
2
3
4
5
6
7
8
9
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文件
它提供了一个默认函数

1
2
3
4
5
6
7
8
9
10
11
12
13
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加载的头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#ifndef PHP_SIREN_H
#define PHP_SIREN_H
 
extern 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"
#endif
 
PHP_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 variables
   in php_siren_globals, call TSRMLS_FETCH(); after declaring other
   variables used by that function, or better yet, pass in TSRMLS_CC
   after the last function argument and declare your utility function
   with TSRMLS_DC after the last declared argument.  Always refer to
   the globals in your function as SIREN_G(variable).  You are
   encouraged to rename these macros something shorter, see
   examples 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文件中.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#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 >= 20010901
        STANDARD_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 */
#endif
        STANDARD_MODULE_PROPERTIES
};
/* }}} */
 
#ifdef COMPILE_DL_SIREN
ZEND_GET_MODULE(siren)
#endif
 
/* {{{ PHP_INI
 */
/* Remove comments and fill if you need to have entries in php.ini
PHP_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 entries
static 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 lines
        REGISTER_INI_ENTRIES();
        */
        return SUCCESS;
}
/* }}} */
 
/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(siren)
{
        /* uncomment this line if you have INI entries
        UNREGISTER_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.ini
        DISPLAY_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是一个结构体

1
2
3
4
5
6
7
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;
1
2
3
4
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 是一个结构体,用来保存模块信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 ZTS
        ts_rsrc_id* globals_id_ptr;
#else
        void* globals_ptr;
#endif
        void (*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对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
zend_module_entry siren_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
        STANDARD_MODULE_HEADER,
#endif
        "siren"//模块名称
        siren_functions, //函数列表 ,执行了上面定义的 zend_function_entry
        PHP_MINIT(siren), //模块开始执行的函数
        PHP_MSHUTDOWN(siren),//模块结束执行的函数
        PHP_RINIT(siren),   //PHP开始请求执行的函数
        PHP_RSHUTDOWN(siren),  //PHP请求结束执行的函数
        PHP_MINFO(siren),
#if ZEND_MODULE_API_NO >= 20010901
        "0.1", //版本
#endif
        STANDARD_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里面的配置信息.

1
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。

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

[转]用C/C++扩展PHP详解的更多相关文章

  1. 开启PHP exif扩展方法详解

    服务器配置说明: 1.在php.ini文件中找到;extension=php_exif.dll,去掉前面的分号2.在php.ini文件中找到;extension=php_mbstring.dll,去掉 ...

  2. SpringBoot各类扩展点详解

    一.前言 上篇文章我们深入分析了SpringBoot的一站式启动流程.然后我们知道SpringBoot的主要功能都是依靠它内部很多的扩展点来完成的,那毋容置疑,这些扩展点是我们应该深入了解的,那么本次 ...

  3. 中国剩余定理(CRT)及其扩展(EXCRT)详解

    问题背景   孙子定理是中国古代求解一次同余式方程组的方法.是数论中一个重要定理.又称中国余数定理.一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作<孙子算经>卷下第 ...

  4. BSGS算法_Baby steps giant steps算法(无扩展)详解

    Baby Steps-Varsity Giant Step-Astronauts(May'n・椎名慶治) 阅读时可以听听这两首歌,加深对这个算法的理解.(Baby steps少女时代翻唱过,这个原唱反 ...

  5. 欧几里德和扩展欧几里德详解 以及例题CodeForces 7C

    欧几里德定理: 对于整数a,b来说,gcd(a, b)==gcd(b, a%b)==d(a与b的最大公约数),又称为辗转相除法 证明: 因为a是d的倍数,b是d的倍数:所以a%d==0:b%d==0: ...

  6. artDialog学习之旅(二)之扩展方法详解

    名称 描述 核心方法 art.dialog.top 获取artDialog可用最高层window对象.这与直接使用window.top不同,它能排除artDialog对象不存在已经或者顶层页面为框架集 ...

  7. C#中的扩展方法详解

    “扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型.重新编译或以其他方式修改原始类型.”这是msdn上说的,也就是你可以对String,Int,DataRow,DataTable等这些类 ...

  8. windows的各种扩展名详解

    Windows系统文件按照不同的格式和用途分很多种类,为便于管理和识别,在对文件命名时,是以扩展名加以区分的,即文件名格式为: 主文件名.扩展名.这样就可以根据文件的扩展名,判定文件的种类,从而知道其 ...

  9. Docker php安装扩展步骤详解

    前言 此篇,主要是演示docker-php-source , docker-php-ext-install ,docker-php-enable-docker-configure 这四个命令到底是用来 ...

随机推荐

  1. 【第一课】WEBIX 入门自学-介绍WEBIX

    Webix是跨浏览器.跨平台的JavaScript框架,使用JavaScript.CSS,HTML5技术构建交互式web应用程序.库中提供几十个完全可定制的组件,提供了JQuery集成和可以处理任何服 ...

  2. appium过程中的问题

    1.在eclipse中点击Genymotion Virtual Device Manager ,选择虚拟设备,点击start后,无反应.    解决方法:Help/Install New Softwa ...

  3. codeforces 360 C

    C - NP-Hard Problem Description Recently, Pari and Arya did some research about NP-Hard problems and ...

  4. Node学习思维导图

    如果看不清楚图片上的内容,右键保存图片或新窗口打开.

  5. Hibernate多对多双向关联的配置

    Hibernate的双向多对多关联有两种配置方法:那我们就来看看两种方案是如何配置的.  一.创建以各自类为类型的集合来关联 1.首先我们要在两个实体类(雇员<Emploee>.工程< ...

  6. TP5验证规则

    系统内置的验证规则如下: 格式验证类 require 验证某个字段必须,例如:'name'=>'require' number 或者 integer 验证某个字段的值是否为数字(采用filter ...

  7. 简化 Web 应用程序与 Windows Azure Active Directory、ASP.NET 和 Visual Studio 的集成

    大家好! 今天的博文深入讨论我们今天推出的开发人员工具和框架中的一些新功能.我们通过与 ASP.NET 和 Visual Studio 团队合作开发了一些重大的增强功能,让开发人员能够轻松使用 Win ...

  8. C++11 并发指南七(C++11 内存模型一:介绍)

    第六章主要介绍了 C++11 中的原子类型及其相关的API,原子类型的大多数 API 都需要程序员提供一个 std::memory_order(可译为内存序,访存顺序) 的枚举类型值作为参数,比如:a ...

  9. 剑指Offer面试题:4.从尾到头打印链表

    一.题目:从尾到头打印链表 题目:输入一个链表的头结点,从尾到头反过来打印出每个结点的值. 到解决这个问题肯定要遍历链表.遍历的顺序是从头到尾的顺序,可输出的顺序却是从尾到头.也就是说第一个遍历到的结 ...

  10. MySQL 复制表结构

    200 ? "200px" : this.width)!important;} --> 介绍 有时候我们需要原封不动的复制一张表的表结构来生成一张新表,MYSQL提供了两种便 ...