我从 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
;