现在的位置: 首页 > 综合 > 正文

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

2013年05月10日 ⁄ 综合 ⁄ 共 10889字 ⁄ 字号 评论关闭

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

2008-02-17 17:27:25|  分类:

PHP
|  标签:
|字号 订阅

引用本文请注明出处:Just Do IT (http://www.toplee.com) < Michael Lee @ toplee.com >

我从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
找到里面有类似这样几行

dnl PHP_ARG_WITH(toplee, for toplee support,
dnl Make sure that the comment is aligned:
dnl [  --with-toplee             Include toplee support])

dnl Otherwise use enable:

dnl PHP_ARG_ENABLE(toplee, whether to enable toplee support,
dnl Make sure that the comment is aligned:
dnl [  --enable-toplee           Enable toplee support])

上面的几行意思是说告诉php编译的使用使用那种方式加载我们的扩展模块toplee,我们使用–with-toplee的方式,于是我们修改为下面的样子

PHP_ARG_WITH(toplee, for toplee support,
Make sure that the comment is aligned:
[  --with-toplee             Include toplee support])

dnl Otherwise use enable:

dnl PHP_ARG_ENABLE(toplee, whether to enable toplee support,
dnl Make sure that the comment is aligned:
dnl [  --enable-toplee           Enable toplee support])

然后我们要做的关键事情就是编写toplee.c,这个是我们编写模块的主要文件,如果您什么都不修改,其实也完成了一个php扩展模块的编写,里面有类似下面的几行代码

PHP_FUNCTION(confirm_toplee_compiled)
{
        
char *arg =
NULL;
        
int arg_len,
len;
        
char string[256];
 
        
if
(zend_parse_parameters(ZEND_NUM_ARGS()
TSRMLS_CC,
"s", &arg, &arg_len)
==
FAILURE)
{
                
return;
        
}
 
        
len =
sprintf(string,
"Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.",
"toplee",
arg);
        
RETURN_STRINGL(string,
len, 1);
}

如果我们在后面完成php的编译时把新的模块编译进去,那么我们就可以在php脚本中调用函数toplee(),它会输出一段字符串“Congratulations! You have successfully modified ext/toplee/config.m4. Module toplee is now compiled into PHP.”

下面是我们对toplee.c的修改,让其支持我们预先规划的功能和接口,下面是toplee.c的源代码

/*
  +----------------------------------------------------------------------+
  | PHP Version 4                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2002 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 2.02 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available at through the world-wide-web at                           |
  | http://www.php.net/license/2_02.txt.                                 |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author:                                                              |
  +----------------------------------------------------------------------+
 
 
$Id: header,v 1.10 2002/02/28 08:25:27 sebastian Exp $

*/

 
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
 
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_gbk.h"
#include "toplee_util.h"
 
/* If you declare any globals in php_gbk.h uncomment this:
ZEND_DECLARE_MODULE_GLOBALS(gbk)
*/

 
/* True global resources - no need for thread safety here */
static int
le_gbk;
 
/* {{{ gbk_functions[]
 *
 * Every user visible function must have an entry in gbk_functions[].
 */

function_entry
gbk_functions[] =
{
    
PHP_FE(toplee_decode_utf,   
NULL)
    
PHP_FE(toplee_decode_utf_gb,   
NULL)
    
PHP_FE(toplee_decode_utf_big5,   
NULL)
    
PHP_FE(toplee_encode_utf_gb,   
NULL)
 
    
PHP_FE(toplee_big52gbk,   
NULL)
    
PHP_FE(toplee_gbk2big5,   
NULL)
    
PHP_FE(toplee_fan2jian,   
NULL)
    
PHP_FE(toplee_normalize_name,   
NULL)
    
{NULL,
NULL, NULL}   
/* Must be the last line in gbk_functions[] */
};
/* }}} */
 
/* {{{ gbk_module_entry
 */

zend_module_entry
gbk_module_entry =
{
#if ZEND_MODULE_API_NO >=
20010901
    
STANDARD_MODULE_HEADER,
#endif
    
"gbk",
    
gbk_functions,
    
PHP_MINIT(gbk),
    
PHP_MSHUTDOWN(gbk),
    
PHP_RINIT(gbk),       
/* Replace with NULL if there's nothing to do at request start */
    
PHP_RSHUTDOWN(gbk),   
/* Replace with NULL if there's nothing to do at request end */
    
PHP_MINFO(gbk),
#if ZEND_MODULE_API_NO >=
20010901
    
"0.1",
/* Replace with version number for your extension */
#endif
    
STANDARD_MODULE_PROPERTIES
};
/* }}} */
 
#ifdef COMPILE_DL_GBK
ZEND_GET_MODULE(gbk)
#endif
 
/* {{{ PHP_INI
 */

/* Remove comments and fill if you need to have entries in php.ini*/
PHP_INI_BEGIN()
    
PHP_INI_ENTRY("gbk2uni",           
"",       
PHP_INI_SYSTEM,   
NULL)
    
PHP_INI_ENTRY("uni2gbk",           
"",       
PHP_INI_SYSTEM,   
NULL)
    
PHP_INI_ENTRY("uni2big5",           
"",       
PHP_INI_SYSTEM,   
NULL)
    
PHP_INI_ENTRY("big52uni",           
"",       
PHP_INI_SYSTEM,   
NULL)
    
PHP_INI_ENTRY("big52gbk",           
"",       
PHP_INI_SYSTEM,   
NULL)
    
PHP_INI_ENTRY("gbk2big5",           
"",       
PHP_INI_SYSTEM,   
NULL)
//    STD_PHP_INI_ENTRY("gbk.global_value",      "42", PHP_INI_ALL, OnUpdateInt, global_value, zend_gbk_globals, gbk_globals)
//    STD_PHP_INI_ENTRY("gbk.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_gbk_globals, gbk_globals)
PHP_INI_END()
 
/* }}} */
 
/* {{{ php_gbk_init_globals
 */

/* Uncomment this function if you have INI entries
static void php_gbk_init_globals(zend_gbk_globals *gbk_globals)
{
    gbk_globals->global_value = 0;
    gbk_globals->global_string = NULL;
}
*/

/* }}} */
 
char gbk2uni_file[256];
char uni2gbk_file[256];
char big52uni_file[256];
char uni2big5_file[256];
char gbk2big5_file[256];
char big52gbk_file[256];
 
//utf file init flag
static int
initutf=0;
 
/* {{{ PHP_MINIT_FUNCTION
 */

PHP_MINIT_FUNCTION(gbk)
{
    
/* If you have INI entries, uncomment these lines
    ZEND_INIT_MODULE_GLOBALS(gbk, php_gbk_init_globals, NULL);*/

    
REGISTER_INI_ENTRIES();
    
memset(gbk2uni_file,
0, sizeof(gbk2uni_file));
    
memset(uni2gbk_file,
0, sizeof(uni2gbk_file));
    
memset(big52uni_file,
0, sizeof(big52uni_file));
    
memset(uni2big5_file,
0, sizeof(uni2big5_file));
    
memset(gbk2big5_file,
0, sizeof(gbk2big5_file));
    
memset(big52gbk_file,
0, sizeof(big52gbk_file));
    
    
strncpy(gbk2uni_file,
INI_STR("gbk2uni"),
sizeof(gbk2uni_file)-1);
    
strncpy(uni2gbk_file,
INI_STR("uni2gbk"),
sizeof(uni2gbk_file)-1);
    
strncpy(big52uni_file,
INI_STR("big52uni"),
sizeof(big52uni_file)-1);
    
strncpy(uni2big5_file,
INI_STR("uni2big5"),
sizeof(uni2big5_file)-1);
    
strncpy(gbk2big5_file,
INI_STR("gbk2big5"),
sizeof(uni2big5_file)-1);
    
strncpy(big52gbk_file,
INI_STR("big52gbk"),
sizeof(uni2big5_file)-1);
 
    
//InitMMResource();
    
InitResource();
    
if ((uni2gbk_file[0]
== '\
0') ||
(uni2big5_file[0] == '\0')
      ||
(gbk2big5_file[0] == '\0')
||
(big52gbk_file[0] == '\0')
      ||
(gbk2uni_file[0] == '\0')
||
(big52uni_file[0] == '\0'))
    
{
        
return
FAILURE;
    
}
 
    
if (gbk2uni_file[0]
!= '\
0')
    
{
        
if
(LoadOneCodeTable(CODE_GBK2UNI,
gbk2uni_file) !=
NULL)
        
{
            
toplee_cleanup_mmap(NULL);
            
return
FAILURE;
        
}
    
}
 
    
if (uni2gbk_file[0]
!= '\
0')
    
{
        
if
(LoadOneCodeTable(CODE_UNI2GBK,
uni2gbk_file) !=
NULL)
        
{
            
toplee_cleanup_mmap(NULL);
            
return
FAILURE;
        
}
    
}
 
    
if (big52uni_file[0]
!= '\
0')
    
{
        
if
(LoadOneCodeTable(CODE_BIG52UNI,
big52uni_file) !=
NULL)
        
{
            
toplee_cleanup_mmap(NULL);
            
return
FAILURE;
        
}
    
}
 
    
if (uni2big5_file[0]
!= '\
0')
    
{
        
if
(LoadOneCodeTable(CODE_UNI2BIG5,
uni2big5_file) !=
NULL)
        
{
            
toplee_cleanup_mmap(NULL);
            
return
FAILURE;
        
}
    
}
    
    
if (gbk2big5_file[0]
!= '\
0')
    
{
        
if
(LoadOneCodeTable(CODE_GBK2BIG5,
gbk2big5_file) !=
NULL)
        
{
            
toplee_cleanup_mmap(NULL);
            
return
FAILURE;
        
}
    
}
 
    
if (big52gbk_file[0]
!= '\
0')
    
{
        
if
(LoadOneCodeTable(CODE_BIG52GBK,
big52gbk_file) !=
NULL)
        
{
            
toplee_cleanup_mmap(NULL);
            
return
FAILURE;
        
}
    
}
    
    
initutf =
1;
    
return
SUCCESS;
}
/* }}} */
 
/* {{{ PHP_MSHUTDOWN_FUNCTION
 */

PHP_MSHUTDOWN_FUNCTION(gbk)
{
    
/* uncomment this line if you have INI entries*/
    
UNREGISTER_INI_ENTRIES();
    
    
toplee_cleanup_mmap(NULL);
    
return
SUCCESS;
}
/* }}} */
 
/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
 */

PHP_RINIT_FUNCTION(gbk)
{
    
return
SUCCESS;
}
/* }}} */
 
/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
 */

PHP_RSHUTDOWN_FUNCTION(gbk)
{
    
return
SUCCESS;
}
/* }}} */
 
/* {{{ PHP_MINFO_FUNCTION
 */

PHP_MINFO_FUNCTION(gbk)
{
    
php_info_print_table_start();
    
php_info_print_table_header(2,
"gbk support",
"enabled");
    
php_info_print_table_end();
 
    
/* Remove comments if you have entries in php.ini*/
    
DISPLAY_INI_ENTRIES();
    
}
/* }}} */
 
 
/* Remove the following function when you have succesfully modified config.m4
   so that your module can be compiled into PHP, it exists only for testing
   purposes. */

 
/* {{{ proto  toplee_decode_utf(string s)
    */

PHP_FUNCTION(toplee_decode_utf)
{
    
char *s =
NULL, *t=NULL;
    
int argc =
ZEND_NUM_ARGS();
    
int s_len;
 
    
if (zend_parse_parameters(argc
TSRMLS_CC,
"s", &s, &s_len)
==
FAILURE)

        
return;
 
    
if (!initutf)
        
RETURN_FALSE
    
t = strdup(s);
    
if (t==NULL)
        
RETURN_FALSE
 
 
    
DecodePureUTF(t,
KEEP_UNICODE);
    
RETVAL_STRING(t,1);
    
free(t);
    
return;
}
/* }}} */
 
/* {{{ proto  toplee_decode_utf_gb(string s)
    */

PHP_FUNCTION(toplee_decode_utf_gb)
{
    
char *s =
NULL, *t=NULL;
    
int argc =
ZEND_NUM_ARGS();
    
int s_len;
 
    
if (zend_parse_parameters(argc
TSRMLS_CC,
"s", &s, &s_len)
==
FAILURE)

        
return;
 
    
if (!initutf)
        
RETURN_FALSE
    
t = strdup(s);
    
if (t==NULL)
        
RETURN_FALSE
 
    
DecodePureUTF(t,
DECODE_UNICODE);
    
RETVAL_STRING(t,1);
    
free(t);
    
return;
 
}
/* }}} */
 
/* {{{ proto  toplee_decode_utf_big5(string s)
    */

PHP_FUNCTION(toplee_decode_utf_big5)
{
    
char *s =
NULL, *t=NULL;
    
int argc =
ZEND_NUM_ARGS();
    
int s_len;
 
    
if (zend_parse_parameters(argc
TSRMLS_CC,
"s", &s, &s_len)
==
FAILURE)

        
return;
 
    
if (!initutf)
        
RETURN_FALSE
    
t = strdup(s);
    
if (t==NULL)
        
RETURN_FALSE
 
 
    
DecodePureUTF(t,
DECODE_UNICODE |
DECODE_BIG5);
    
RETVAL_STRING(t,1);
    
free(t);
    
return;
}
/* }}} */
int EncodePureUTF(unsigned
char* strSrc,

    
unsigned char*
strDst, int
nDstLen, int
nFlag)
{
    
int nRet;
    
int pos;
    
unsigned short
c;
    
unsigned short*
uBuf;
    
int nSize;
    
int nLen;
    
int nReturn;
 
    
nLen=strlen((const
char*)strSrc);
    
if(nDstLen <
nLen*2+1)
        
return
0;
 
    
nSize=nLen+1;
    
uBuf=(unsigned
short*)emalloc(sizeof(unsigned
short

抱歉!评论已关闭.