Linux/FreeBSD下用C语言开发PHP的so扩展模块例解

我从 97 年接触互联网的 web 开发,至今已经过去 9 年了,从最初的 frontpage html 页面到学会 ASP+access+IIS 开始,就跟 web 开发干上了,后来又依次使用了 ASP+SQLServer+IIS JSP+Oracle+Jrun(Resin/Tomcat) PHP+Syabse(MySQL)+Apache … 最后我定格到了 PHP+MySQL+Apache+Linux(BSD) 的架构上,也就是大家常说的 LAMP 架构,这说来有很多理由,网上也有很多人讨论各种架构和开发语言之间的优劣,我就不多说了,简单说一下我喜欢 LAMP 的几个主要原因:

1 、全开放的免费平台;
2 、简单易上手、各种资源丰富;
3 PHP MySQL Apache Linux(BSD) 系统底层以及彼此间无缝结合,非常高效;
4 、均使用最高效的语言 C/C++ 开发,性能可靠;
5 PHP 语言和 C 的风格基本一致,还吸取了 Java C++ 的诸多架构优点;
6 、这是最关键的一点,那就是 PHP 可以非常方便的使用 C/C++ 开发扩展模块,给了 PHP 无限的扩张性!

基于以上原因,我非常喜欢基于 PHP 语言的架构,其中最关键的一点就是最后一点,以前在 Yahoo mop 均推广使用这个平台,在 C 扩展 php 方面也有一些经验,在此和大家分享一下,希望可以抛砖引玉。
C 语言编写 PHP 的扩展模块的方法有几种,根据最后的表现形式有两种,一种是直接编译进 php ,一种是编译为 php so 扩展模块来被 php 调用,另外根据编译的方式有两种,一种使用 phpize 工具( php 编译后有的),一种使用 ext_skel 工具( php 自带的),我们使用最多,也是最方便的方式就是使用 ext_skel 工具来编写 php so 扩展模块,这里也主要介绍这种方式。
我们在 php 的源码目录里面可以看到有个 ext 目录(我这里说的 php 都是基于 Linux 平台的 php 来说的,不包括 windows 下的),在 ext 目录下有个工具 ext_skel ,这个工具可以让我们简单的开发出 php 的扩展模块,它提供了一个通用的 php 扩展模块开发步骤和模板。下面我们以开发一个在 php 里面进行 utf8/gbk/gb2312 三种编码转换的扩展模块为例子进行说明。在这个模块中,我们要最终提供以下几个函数接口:

(1) string toplee_big52gbk(string s)
将输入字符串从 BIG5 码转换成 GBK
(2) string toplee_gbk2big5(string s)
将输入字符串从 GBK 转换成 BIG5
(3) string toplee_normalize_name(string s)
将输入字符串作以下处理:全角转半角, strim ,大写转小写
(4) string toplee_fan2jian(int code, string s)
将输入的 GBK 繁体字符串转换成简体
(5) string toplee_decode_utf(string s)
utf 编码的字符串转换成 UNICODE
(6) string toplee_decode_utf_gb(string s)
utf 编码的字符串转换成 GB
(7) string toplee_decode_utf_big5(string s)
utf 编码的字符串转换成 BIG5
(8) string toplee_encode_utf_gb(string s)
将输入的 GBKf 编码的字符串转换成 utf 编码

首先,我们进入 ext 目录下,运行下面命令:
#./ext_skel –extname=toplee
这时, php 会自动在 ext 目录下为我们生成一个目录 toplee ,里面包含下面几个文件
.cvsignore
CREDITS
EXPERIMENTAL
config.m4
php_toplee.h
tests
toplee.c
toplee.php

 

其中最有用的就是 config.m4 toplee.c 文件
接下来我们修改 config.m4 文件
#vi ./config.m4
找到里面有类似这样几行

1.       dnl PHP_ARG_WITH(toplee, for toplee support,
2.       dnl Make sure that the comment is aligned:
3.       dnl [ --with-toplee Include toplee support])
4.        
5.       dnl Otherwise use enable:
6.        
7.       dnl PHP_ARG_ENABLE(toplee, whether to enable toplee support,
8.       dnl Make sure that the comment is aligned:
9.       dnl [ --enable-toplee Enable toplee support])
上面的几行意思是说告诉 php 编译的使用使用那种方式加载我们的扩展模块 toplee ,我们使用 –with-toplee 的方式,于是我们修改为下面的样子
1.       PHP_ARG_WITH(toplee, for toplee support,
2.       Make sure that the comment is aligned:
3.       [ --with-toplee Include toplee support])
4.        
5.       dnl Otherwise use enable:
6.        
7.       dnl PHP_ARG_ENABLE(toplee, whether to enable toplee support,
8.       dnl Make sure that the comment is aligned:
9.       dnl [ --enable-toplee Enable toplee support])
然后我们要做的关键事情就是编写 toplee.c ,这个是我们编写模块的主要文件,如果您什么都不修改,其实也完成了一个 php 扩展模块的编写,里面有类似下面的几行代码
1.       PHP_FUNCTION ( confirm_toplee_compiled )
2.       {
3.               char * arg = NULL ;
4.               int arg_len , len ;
5.               char string [ 256 ] ;
6.        
7.               if ( zend_parse_parameters ( ZEND_NUM_ARGS () TSRMLS_CC , " s " , & arg , & arg_len ) == FAILURE ) {
8.                       return ;
9.               }
10.    
11.           len = sprintf ( string , " Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP. " , " toplee " , arg ) ;
12.           RETURN_STRINGL ( string , len , 1 ) ;
13.   }
如果我们在后面完成 php 的编译时把新的模块编译进去,那么我们就可以在 php 脚本中调用函数 toplee() ,它会输出一段字符串 “Congratulations! You have successfully modified ext/toplee/config.m4. Module toplee is now compiled into PHP.”
下面是我们对 toplee.c 的修改,让其支持我们预先规划的功能和接口,下面是 toplee.c 的源代码
1.       /*
2.         +----------------------------------------------------------------------+
3.         | PHP Version 4                                                        |
4.         +----------------------------------------------------------------------+
5.         | Copyright (c) 1997-2002 The PHP Group                                |
6.         +----------------------------------------------------------------------+
7.         | This source file is subject to version 2.02 of the PHP license,      |
8.         | that is bundled with this package in the file LICENSE, and is        |
9.         | available at through the world-wide-web at                           |
10.     | http://www.php.net/license/2_02.txt.                                 |
11.     | If you did not receive a copy of the PHP license and are unable to   |
12.     | obtain it through the world-wide-web, please send a note to          |
13.     | license@php.net so we can mail you a copy immediately.               |
14.     +----------------------------------------------------------------------+
15.     | Author:                                                              |
16.     +----------------------------------------------------------------------+
17.    
18.     $Id: header,v 1.10 2002/02/28 08:25:27 sebastian Exp $  
19.   */
20.    
21.   #ifdef HAVE_CONFIG_H
22.   #include " config.h "
23.   #endif
24.    
25.   #include " php.h "
26.   #include " php_ini.h "
27.   #include " ext/standard/info.h "
28.   #include " php_gbk.h "
29.   #include " toplee_util.h "
30.    
31.   /* If you declare any globals in php_gbk.h uncomment this:
32.   ZEND_DECLARE_MODULE_GLOBALS(gbk)
33.   */
34.    
35.   /* True global resources - no need for thread safety here */
36.   static int le_gbk ;
37.    
38.   /* {{{ gbk_functions[]
39.   *
40.   * Every user visible function must have an entry in gbk_functions[].
41.   */
42.   function_entry gbk_functions [] = {
43.       PHP_FE ( toplee_decode_utf ,    NULL )
44.       PHP_FE ( toplee_decode_utf_gb ,    NULL )
45.       PHP_FE ( toplee_decode_utf_big5 ,    NULL )
46.       PHP_FE ( toplee_encode_utf_gb ,    NULL )
47.    
48.       PHP_FE ( toplee_big52gbk ,    NULL )
49.       PHP_FE ( toplee_gbk2big5 ,    NULL )
50.       PHP_FE ( toplee_fan2jian ,    NULL )
51.       PHP_FE ( toplee_normalize_name ,    NULL )
52.       { NULL , NULL , NULL }     /* Must be the last line in gbk_functions[] */
53.   } ;
54.   /* }}} */
55.    
56.   /* {{{ gbk_module_entry
57.   */
58.   zend_module_entry gbk_module_entry = {
59.   #if ZEND_MODULE_API_NO >= 20010901
60.       STANDARD_MODULE_HEADER ,
61.   #endif
62.       " gbk " ,
63.       gbk_functions ,
64.       PHP_MINIT ( gbk ) ,
65.       PHP_MSHUTDOWN ( gbk ) ,
66.       PHP_RINIT ( gbk ) ,        /* Replace with NULL if there's nothing to do at request start */
67.       PHP_RSHUTDOWN ( gbk ) ,    /* Replace with NULL if there's nothing to do at request end */
68.       PHP_MINFO ( gbk ) ,
69.   #if ZEND_MODULE_API_NO >= 20010901
70.       " 0.1 " , /* Replace with version number for your extension */
71.   #endif
72.       STANDARD_MODULE_PROPERTIES
73.   } ;
74.   /* }}} */
75.    
76.   #ifdef COMPILE_DL_GBK
77.   ZEND_GET_MODULE ( gbk )
78.   #endif
79.    
80.   /* {{{ PHP_INI
81.   */
82.   /* Remove comments and fill if you need to have entries in php.ini*/
83.   PHP_INI_BEGIN ()
84.       PHP_INI_ENTRY ( " gbk2uni " ,            "" ,        PHP_INI_SYSTEM ,    NULL )
85.       PHP_INI_ENTRY ( " uni2gbk " ,            "" ,        PHP_INI_SYSTEM ,    NULL )
86.       PHP_INI_ENTRY ( " uni2big5 " ,            "" ,        PHP_INI_SYSTEM ,    NULL )
87.       PHP_INI_ENTRY ( " big52uni " ,            "" ,        PHP_INI_SYSTEM ,    NULL )
88.       PHP_INI_ENTRY ( " big52gbk " ,            "" ,        PHP_INI_SYSTEM ,    NULL )
89.       PHP_INI_ENTRY ( " gbk2big5 " ,            "" ,        PHP_INI_SYSTEM ,    NULL )
90.   //    STD_PHP_INI_ENTRY("gbk.global_value",      "42", PHP_INI_ALL, OnUpdateInt, global_value, zend_gbk_globals, gbk_globals)

91.   //    STD_PHP_INI_ENTRY("gbk.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_gbk_globals, gbk_globals)

92.   PHP_INI_END ()
93.    
94.   /* }}} */
95.    
96.   /* {{{ php_gbk_init_globals
97.   */
98.   /* Uncomment this function if you have INI entries
99.   static void php_gbk_init_globals(zend_gbk_globals *gbk_globals)
100.    {
101.        gbk_globals->global_value = 0;
102.        gbk_globals->global_string = NULL;
103.    }
104.    */
105.    /* }}} */
106.     
107.    char gbk2uni_file [ 256 ] ;
108.    char uni2gbk_file [ 256 ] ;
109.    char big52uni_file [ 256 ] ;
110.    char uni2big5_file [ 256 ] ;
111.    char gbk2big5_file [ 256 ] ;
112.    char big52gbk_file [ 256 ] ;
113.     
114.    //utf file init flag
115.    static int initutf = 0 ;
116.     
117.    /* {{{ PHP_MINIT_FUNCTION
118.    */
119.    PHP_MINIT_FUNCTION ( gbk )
120.    {
121.        /* If you have INI entries, uncomment these lines
122.        ZEND_INIT_MODULE_GLOBALS(gbk, php_gbk_init_globals, NULL);*/
123.        REGISTER_INI_ENTRIES () ;
124.        memset ( gbk2uni_file , 0 , sizeof ( gbk2uni_file )) ;
125.        memset ( uni2gbk_file , 0 , sizeof ( uni2gbk_file )) ;
126.        memset ( big52uni_file , 0 , sizeof ( big52uni_file )) ;
127.        memset ( uni2big5_file , 0 , sizeof ( uni2big5_file )) ;
128.        memset ( gbk2big5_file , 0 , sizeof ( gbk2big5_file )) ;
129.        memset ( big52gbk_file , 0 , sizeof ( big52gbk_file )) ;
130.       
131.        strncpy ( gbk2uni_file , INI_STR ( " gbk2uni " ) , sizeof ( gbk2uni_file ) - 1 ) ;
132.        strncpy ( uni2gbk_file , INI_STR ( " uni2gbk " ) , sizeof ( uni2gbk_file ) - 1 ) ;
133.        strncpy ( big52uni_file , INI_STR ( " big52uni " ) , sizeof ( big52uni_file ) - 1 ) ;
134.        strncpy ( uni2big5_file , INI_STR ( " uni2big5 " ) , sizeof ( uni2big5_file ) - 1 ) ;
135.        strncpy ( gbk2big5_file , INI_STR ( " gbk2big5 " ) , sizeof ( uni2big5_file ) - 1 ) ;
136.        strncpy ( big52gbk_file , INI_STR ( " big52gbk " ) , sizeof ( uni2big5_file ) - 1 ) ;
137.     
138.        //InitMMResource();
139.        InitResource () ;
140.        if (( uni2gbk_file [ 0 ] == '\ 0 ' ) || ( uni2big5_file [ 0 ] == '\ 0 ' )
141.          || ( gbk2big5_file [ 0 ] == '\ 0 ' ) || ( big52gbk_file [ 0 ] == '\ 0 ' )
142.          || ( gbk2uni_file [ 0 ] == '\ 0 ' ) || ( big52uni_file [ 0 ] == '\ 0 ' ))
143.        {  
144.            return FAILURE ;
145.        }
146.     
147.        if ( gbk2uni_file [ 0 ] != '\ 0 ' )
148.        {
149.            if ( LoadOneCodeTable ( CODE_GBK2UNI , gbk2uni_file ) != NULL )
150.            {
151.                toplee_cleanup_mmap ( NULL ) ;
152.                return FAILURE ;
153.            }
154.        }
155.     
156.        if ( uni2gbk_file [ 0 ] != '\ 0 ' )
157.        {
158.            if ( LoadOneCodeTable ( CODE_UNI2GBK , uni2gbk_file ) != NULL )
159.            {
160.                toplee_cleanup_mmap ( NULL ) ;
161.                return FAILURE ;
162.            }
163.        }
164.     
165.        if ( big52uni_file [ 0 ] != '\ 0 ' )
166.        {
167.            if ( LoadOneCodeTable ( CODE_BIG52UNI , big52uni_file ) != NULL )
168.            {
169.                toplee_cleanup_mmap ( NULL ) ;
170.                return FAILURE ;
171.            }
172.        }
173.     
174.        if ( uni2big5_file [ 0 ] != '\ 0 ' )
175.        {
176.            if ( LoadOneCodeTable ( CODE_UNI2BIG5 , uni2big5_file ) != NULL )
177.            {
178.                toplee_cleanup_mmap ( NULL ) ;
179.                return FAILURE ;
180.            }
181.        }
182.       
183.        if ( gbk2big5_file [ 0 ] != '\ 0 ' )
184.        {
185.            if ( LoadOneCodeTable ( CODE_GBK2BIG5 , gbk2big5_file ) != NULL )
186.            {
187.                toplee_cleanup_mmap ( NULL ) ;
188.                return FAILURE ;
189.            }
190.        }
191.     
192.        if ( big52gbk_file [ 0 ] != '\ 0 ' )
193.        {
194.            if ( LoadOneCodeTable ( CODE_BIG52GBK , big52gbk_file ) != NULL )
195.            {
196.                toplee_cleanup_mmap ( NULL ) ;
197.                return FAILURE ;
198.            }
199.        }
200.       
201.        initutf = 1 ;
202.        return SUCCESS ;
203.    }
204.    /* }}} */
205.     
206.    /* {{{ PHP_MSHUTDOWN_FUNCTION
207.    */
208.    PHP_MSHUTDOWN_FUNCTION ( gbk )
209.    {
210.        /* uncomment this line if you have INI entries*/
211.        UNREGISTER_INI_ENTRIES () ;
212.       
213.        toplee_cleanup_mmap ( NULL ) ;
214.        return SUCCESS ;
215.    }
216.    /* }}} */
217.     
218.    /* Remove if there's nothing to do at request start */
219.    /* {{{ PHP_RINIT_FUNCTION
220.    */
221.    PHP_RINIT_FUNCTION ( gbk )
222.    {
223.        return SUCCESS ;
224.    }
225.    /* }}} */
226.     
227.    /* Remove if there's nothing to do at request end */
228.    /* {{{ PHP_RSHUTDOWN_FUNCTION
229.    */
230.    PHP_RSHUTDOWN_FUNCTION ( gbk )
231.    {
232.        return SUCCESS ;
233.    }
234.    /* }}} */
235.     
236.    /* {{{ PHP_MINFO_FUNCTION
237.    */
238.    PHP_MINFO_FUNCTION ( gbk )
239.    {
240.        php_info_print_table_start () ;
241.        php_info_print_table_header ( 2 , " gbk support " , " enabled " ) ;
242.        php_info_print_table_end () ;
243.     
244.        /* Remove comments if you have entries in php.ini*/
245.        DISPLAY_INI_ENTRIES () ;
246.       
247.    }
248.    /* }}} */
249.     
250.     
251.    /* Remove the following function when you have succesfully modified config.m4
252.       so that your module can be compiled into PHP, it exists only for testing
253.       purposes. */
254.     
255.    /* {{{ proto  toplee_decode_utf(string s)
256.        */
257.    PHP_FUNCTION ( toplee_decode_utf )
258.    {
259.        char * s = NULL , * t = NULL ;
260.        int argc = ZEND_NUM_ARGS () ;
261.        int s_len ;
262.     
263.        if ( zend_parse_parameters ( argc TSRMLS_CC , " s " , & s , & s_len ) == FAILURE )  
264.            return ;
265.     
266.        if ( ! initutf )
267.            RETURN_FALSE
268.        t = strdup ( s ) ;
269.        if ( t == NULL )
270.            RETURN_FALSE
271.     
272.     
273.        DecodePureUTF ( t , KEEP_UNICODE ) ;
274.        RETVAL_STRING ( t , 1 ) ;
275.        free ( t ) ;
276.        return ;
277.    }
278.    /* }}} */
279.     
280.    /* {{{ proto  toplee_decode_utf_gb(string s)
281.        */
282.    PHP_FUNCTION ( toplee_decode_utf_gb )
283.    {
284.        char * s = NULL , * t = NULL ;
285.        int argc = ZEND_NUM_ARGS () ;
286.        int s_len ;
287.     
288.        if ( zend_parse_parameters ( argc TSRMLS_CC , " s " , & s , & s_len ) == FAILURE )  
289.            return ;
290.     
291.        if ( ! initutf )
292.            RETURN_FALSE
293.        t = strdup ( s ) ;
294.        if ( t == NULL )
295.            RETURN_FALSE
296.     
297.        DecodePureUTF ( t , DECODE_UNICODE ) ;
298.        RETVAL_STRING ( t , 1 ) ;
299.        free ( t ) ;
300.        return ;
301.     
302.    }
303.    /* }}} */
304.     
305.    /* {{{ proto  toplee_decode_utf_big5(string s)
306.        */
307.    PHP_FUNCTION ( toplee_decode_utf_big5 )
308.    {
309.        char * s = NULL , * t = NULL ;
310.        int argc = ZEND_NUM_ARGS () ;
311.        int s_len ;
312.     
313.        if ( zend_parse_parameters ( argc TSRMLS_CC , " s " , & s , & s_len ) == FAILURE )  
314.            return ;
315.     
316.        if ( ! initutf )
317.            RETURN_FALSE
318.        t = strdup ( s ) ;
319.        if ( t == NULL )
320.            RETURN_FALSE
321.     
322.     
323.        DecodePureUTF ( t , DECODE_UNICODE | DECODE_BIG5 ) ;
324.        RETVAL_STRING ( t , 1 ) ;
325.        free ( t ) ;
326.        return ;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值