[转]用C/C++扩展PHP详解

原文: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。

剩余的东西就是是我们地方提到的片 启动模块时实行的函数…
解了这些,再去描绘模块头就未会见杀啦…

相关文章