文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>PHP4扩展模块开发入门(一)v2005.05.19 现场开发实例

PHP4扩展模块开发入门(一)v2005.05.19 现场开发实例

时间:2007-02-17  来源:PHP爱好者

背景:网上银行支付系统,需要采用数字签名;
网上银行服务提供方,提供了C语言的验证演示程序;

前提:我目前的运作环境
:php4.3.11 + Apache2

需求:开发网上银行数字签名的模块。
不改动原有的PHPBIN,也就是不能从新安装PHP

参考:http://www.php.net/manual/zh/zend.php

因为安全原因,我这里不能列出实际的数字验证部分的代码。
但是所给出的代码是可以通过的。
为了防止以后有这种事情忘记了,所以在这里记录下来。

 HonestQiao 回复于:2005-05-19 13:18:37 首先,你要作如下准备。

1、你的系统已经安装了PHP4.3.11

2、你的PHP4.3.11的源代码还存在。
如果不存在???
没关系,查看phpinfo()里面的Configure Command
到www.php.net下载PHP4.3.11,解压到某个目录
到解压的目录之中,运行Configure Command

3、这样子就做好了基本的环境准备了

附加:
当然,GCC、vi什么的你预先准备哦,我所说的只是php的部分的

简写说明:
phpbin表示已经安装好的php
phpsrc表示愿代码的php

HonestQiao 回复于:2005-05-19 13:21:58 基本模块框架建立:

假设我们的模块叫做:
CheckInfo

[code:1:82b8db0feb]
cd phpsrc/ext
./ext_skel --extname=CheckInfo
#语法:./ext_skel --extname=模块名称
cd CheckInfo
ls -l
[/code:1:82b8db0feb]

现在可以看到,模块的基本框架相关的文件已经建立了
[code:1:82b8db0feb]
config.m4 编译相关的配置
php_CheckInfo.h PHP定义CHeckInfo模块的头文件
CheckInfo.c CheckInfo模块的实际代码文件
CheckInfo.php 这个是一个PHP测试文件,看是否可以调用这个模块
[/code:1:82b8db0feb]

这里需要提前说明的一点,CheckInfo.php里面采用了dl()这个函数,这个要求你的PHP设置可以调用,并且extension目录设置正确。
还有,我使用的Apache2工作在worker模式,他提示我说在多线程模式下不可用dl(),应该使用extension=CheckInfo.so

还有一点说明,你执行了上面的扩展模块生成工具之后,会提示你几个步骤,但是我们不那么做。
当然,你可以按照那个做一下子也可以的。

HonestQiao 回复于:2005-05-19 13:37:32 然后修改编译配置文件,打开config.m4,你从最开始可以看到:
[code:1:572f9e2f4f]
dnl $Id$
dnl config.m4 for extension CheckInfo

dnl Comments in this file start with the string 'dnl'.
dnl Remove where necessary. This file will not work
dnl without editing.

dnl If your extension references something external, use with:

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

dnl Otherwise use enable:
dnl 另外,你可以使用enable参数,来加载模块

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

[/code:1:572f9e2f4f]

在这里,dnl表示注释,加了dnl的部分不会被执行。
在php编译的时候,加载某一模块可以使用
--with-modulename或者--enable-modulename
如果采用--with-modulename的模式,那么就修改
[code:1:572f9e2f4f]
dnl PHP_ARG_WITH(CheckInfo, for CheckInfo support,
dnl Make sure that the comment is aligned:
dnl [  --with-CheckInfo             Include CheckInfo support])
[/code:1:572f9e2f4f]

[code:1:572f9e2f4f]
PHP_ARG_WITH(CheckInfo, for CheckInfo support,
Make sure that the comment is aligned:
[  --with-CheckInfo             Include CheckInfo support])
[/code:1:572f9e2f4f]

反之亦然,如果要采用--enable-modulename的模式,就修改
[code:1:572f9e2f4f]
dnl PHP_ARG_ENABLE(CheckInfo, whether to enable CheckInfo support,
dnl Make sure that the comment is aligned:
dnl [  --enable-CheckInfo           Enable CheckInfo support])
[/code:1:572f9e2f4f]

[code:1:572f9e2f4f]
PHP_ARG_ENABLE(CheckInfo, whether to enable CheckInfo support,
Make sure that the comment is aligned:
[  --enable-CheckInfo           Enable CheckInfo support])
[/code:1:572f9e2f4f]

如果你的和这个不同,应该可以根据英文的意思来设定的。

HonestQiao 回复于:2005-05-19 13:39:58 然后,配置zend扩展模块api,这个时候很简单,执行一条命令
[code:1:3b33cbbe9b]
phpbin/phpize
[/code:1:3b33cbbe9b]

HonestQiao 回复于:2005-05-19 13:47:06 然后,这行configure进行编译配置,这个也很简单
[code:1:d5cb955934]
./configure --[with|enable]-CheckInfo
#[with|enable]根据前面的config.m4设置来选择
[/code:1:d5cb955934]

这个就可以配置编译参数了。

这里有一点说明,因为我需要加载openssl的链接库,所以我加了一个参数,命令修改为:
[code:1:d5cb955934]
./configure --[with|enable]-CheckInfo LDFLAGS="-lcrypto"
[/code:1:d5cb955934]

有时候,这里还需要一个参数:
如果提示:
[code:1:d5cb955934]configure: error: Cannot find php-config. Please use --with-php-config=PATH
[/code:1:d5cb955934]
你可以这么做:
[code:1:d5cb955934]
./configure --[with|enable]-CheckInfo --with-php-config=phpbin/php-config
[/code:1:d5cb955934]

HonestQiao 回复于:2005-05-19 14:01:26 然后编译测试一下子:
[code:1:ae901f84b8]
make
......
See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------

Build complete.
(It is safe to ignore warnings about tempnam and tmpnam).
...
ls -l modules
[/code:1:ae901f84b8]

如果没有错误,看到了CheckInfo.so,那么说明我们可以开始这个模块的实际功能开发了。

HonestQiao 回复于:2005-05-19 14:17:38 因为我现在正在做,所以边做边写。

现在我们要开始写模块了哦。

模块的主要信息,与php_CheckInfo.h和CheckInfo.c有关。

php_CheckInfo是头文件信息,php需要从里面获取当前模块的资料,例如版本,所包含的函数,例如里面包含了一个测试用的函数:
[code:1:1bfd74e30d]PHP_FUNCTION(confirm_CheckInfo_compile);[/code:1:1bfd74e30d]

如果我们要添加一个自己的函数,那么可以在这个下面添加自己的定义:
[code:1:1bfd74e30d]PHP_FUNCTION(nihao);[/code:1:1bfd74e30d]

[code:1:1bfd74e30d]PHP_FUNCTION(dajiahao);[/code:1:1bfd74e30d]

为什么可以由多个?
因为我们的一个模块,是可以提供多个功能函数的

还有一点注意,函数名称都用小写好了。

HonestQiao 回复于:2005-05-19 15:00:30 定义了自己的函数之后,就需要编写函数的实际功能了。

函数的实际功能,在CheckInfo.c里面写出来。

首先我们需要定义在前面的头文件里面所定义的函数的入口。
好在康佳已经给了我们基本的定义,按照要求添加就可以了。
打开CheckInfo.c,你可以找到:
[code:1:11c51a14a8]static function_entry hello_functions[] = {[/code:1:11c51a14a8]
这个地方定义了我们的函数入口,要添加我们在前面定义的:
[code:1:11c51a14a8]
PHP_FE(nihao, NULL)
PHP_FE(dajiahao, NULL)
[/code:1:11c51a14a8]

定义了函数入口之后,就可以写函数的实际代码了。

你可以在最后添加:
[code:1:11c51a14a8]
PHP_FUNCTION(nihao)
{
RETURN_STRING("Nihao , wo shi php ext CheckInfo", 1);
}
[/code:1:11c51a14a8]
暂时我们使用返回字符串来演示,实际上可以返回其他的类型,或者直接输出。

现在我们要来测试一下子:
首先:
[code:1:11c51a14a8]
phpbin/bin/php -r "phpinfo();" | grep "php.ini"
[/code:1:11c51a14a8]
将会显示:
[code:1:11c51a14a8]
Configuration File (php.ini) Path => phpbin/etc/php.ini
[/code:1:11c51a14a8]
这个phpbin/etc/php.ini表示你的php.ini的位置
然后你要打开这个php.ini,设置好extensions的dir的位置,我的设置在:phpbin/extensions/
然后设置启用我们的扩展模块:
[code:1:11c51a14a8]extension=CheckInfo.so[/code:1:11c51a14a8]

哦,我们的模块还没有生成最新的,那我们做一下子
[code:1:11c51a14a8]
make
copy modules/CHeckInfo.so phpbin/extensions/
phpbin/bin/php -r "echo nihao();"
[/code:1:11c51a14a8]

如果这个时候出现了:[code:1:11c51a14a8]Nihao , wo shi php ext CheckInfo[/code:1:11c51a14a8]

那就说明,我们成功了。
如果没有成功,它是会告诉我们错误的。

为什么我们要使用“phpbin/bin/php -r "echo nihao();"”来进行测试???
因为我们可以在命令行直接输入我们的代码,免得还要不断从新启动服务器来调试。
当然,我们因为实在调试阶段,如果代码都写好了,调试通过了,直接加载到运行的服务器的PHP之中,就不用不断从新启动了。

HonestQiao 回复于:2005-05-19 15:08:11 下面要讲的东西:
1、如何获取传入的参数
例如传入参数的个数,类型,是否引用传递

2、如何返回结果
例如返回状态,或者给应用参数赋值

.......
简单的来说,就是所谓的黑箱:
我们的函数怎么做,使用者一般不必知道;
他所要知道的是:
我给你什么参数,你给我返回什么数据;

那我们要作的就是:
接受它的参数,返回数据给他

飞雪恨水 回复于:2005-05-19 15:58:19 偶像。。。。。

imbiss 回复于:2005-05-19 17:55:57 感谢搂主的帖子
关于扩展模块的基本问题
1.所谓扩展模块是不是指用其他语言编写的,比如C写的,提供给php调用的函数或者类的模块?是不是一旦扩展完成,就可以像使用PHP内置的函数一样使用这些功能?
2.对于普通的虚拟主机用户是不是有可能自己扩展?
3.对于Windows下使用WAMP的用户时候可以开发自己的扩展模块?

敬请赐教

HonestQiao 回复于:2005-05-19 18:02:34 [quote:25bd04455c="imbiss"]
感谢搂主的帖子
关于扩展模块的基本问题
1.所谓扩展模块是不是指用其他语言编写的,比如C写的,提供给php调用的函数或者类的模块?是不是一旦扩展完成,就可以像使用PHP内置的函数一样使用这些功能?
2.对于普通的虚拟主机用户是不是有可能自己扩展?
3.对于Windows下使用WAMP的用户时候可以开发自己的扩展模块? 
[/quote:25bd04455c]

1、对,就和php的内置模块,完全相同的用法
2、普通的用户是否可以扩展,这个要看服务器的php.ini的设置,有可能可以使用dl()来调用;
3、完全可以做到哦。不过暂时我还没有使用。但是道理应该是相同的,因为都需要遵循的PHP的扩展模块的框架

HonestQiao 回复于:2005-05-20 16:51:45 现在讲的是,传入参数的获取:
要获取传入给功能参数,一般要做如下的工作:
1、事先规划,该功能函数所需要的参数个数,每个参数的类型
2、在模块的程序之中,判断参数个数,并取道参数

如何得知参数的个数???
Zend Api给我们提供了一个函数:
[code:1:28c68f3b88] ZEND_NUM_ARGS()[/code:1:28c68f3b88]
通过该函数,可以得知,传入的参数个数;
例如:[code:1:28c68f3b88]<?php nihao(1,2,4); ?>[/code:1:28c68f3b88]
这个时候,ZEND_NUM_ARGS()的值为3;

我们也可以这么做:
[code:1:28c68f3b88]     if(ZEND_NUM_ARGS() != 3) WRONG_PARAM_COUNT; [/code:1:28c68f3b88]

表示当参数不时3个的时候,则进行参数个数出错处理.

你也可以根据参数的个数,来进行不同的操作。
这样,你可以实现一个函数,其参数个数是可变的。
例如date()函数;

HonestQiao 回复于:2005-05-20 16:57:08 得到了参数的个数,如何再得到每个参数的值呢?

为了暂时可以更好的讲解,我们传入的参数使用比较简单的。
从http://w.yi.org/ftp/FAPM/PHP/php_manual_zh/language.types.html可以得知,目前PHP的参数主要有:
[quote:1f63e74a10]
PHP 支持八种原始类型。

四种标量类型:

*

布尔型(boolean)
*

整型(integer)
*

浮点型(float)(浮点数,也作“double”)
*

字符串(string) 

两种复合类型:

*

数组(array)
*

对象(object) 

最后是两种特殊类型:

*

资源(resource)
*

NULL 

为了确保代码的易读性,本手册还介绍了一些伪类型:

*

混和(mixed)
*

数字(number)
*

回馈(callback) 
[/quote:1f63e74a10]

HonestQiao 回复于:2005-05-20 17:00:17 而在模块开发之中,可以传入的参数有以下类型:
[quote:690c7d51e6]
l - long
d - double
s - string (with possible null bytes) and its length
b - boolean
r - resource, stored in zval*
a - array, stored in zval*
o - object (of any class), stored in zval*
O - object (of class specified by class entry), stored in zval*
z - the actual zval*
[/quote:690c7d51e6]

zval据我个人认为是一个Zend的泛型,他可以代表很多种类的参数。

我们主要使用ldsb类型的参数

HonestQiao 回复于:2005-05-20 17:09:37 架设我们有四个参数,分别为:ldsb类型的,那么我们可以使用一个Zend Api函数来获取参数值;
[code:1:f5c4ed4945]
zend_parse_parameters(4,"ldsb", &l, &d, &s, &s_len, &b);
[/code:1:f5c4ed4945]
第一个参数:表示传入的参数个数,可以使用ZEND_NUM_ARGS() TSRMLS_CC来自动取得;

第二个参数:"ldsb",表示传入的参数的类型;按照传入参数的顺序来设定

随后跟这个的参数,就是用来接收传入参数的C语言变量了,这里要使用&,类似scanf的用法哦。

不过有一点特别注意的提醒大家,我也在哪里摸了好半天。
为什么呢?
我们在前面说过,可接收的参数类型,其中有一个:[quote:f5c4ed4945]s - string (with possible null bytes) and its length[/quote:f5c4ed4945]
原来,传入一个字符串的时候,会自动把其长度作为一个参数传入,这样子,"ldsb"的第三个参数是字符串,那么第四个参数就是一个l类型的参数,他表示前面字符串的长度,而"ldsb"的"b"传入参数,就成为了第五个参数了。
还有一点要澄清的,咱们通过zend_parse_parameters,实际上获取了5个值,但是ZEND_NUM_ARGS()的值还是为4个,因为我们在php里面传来的只有4个,由于多了一个s的长度,所以获取的值却有5个。

HonestQiao 回复于:2005-05-20 17:14:57 还有一个相对而言,特殊一点的传入参数类型,那就是引用;如何取得一个引用的传入句柄,使得我们可以将值赋予给他???

传入的引用的接受对象的类型,必须是"z",也就是要使用一个zval类型的变量来接受;
在模块里面,你可以这么做:
[code:1:080ab41a22]
/*
<?php
nihao(&$refVal);
?>
/*
zval refPara;
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"z", &refPara); 
[/code:1:080ab41a22]
这样子你就可以获得这个引用的传入句柄,在后面,你就可以给他复值了。

HonestQiao 回复于:2005-05-20 17:33:21 在我们模块调试过程之中,及时显示出调试信息是很有用的,例如可以看当前的参数个数,获取的参数的值是否正确等。
在c里面,使用printf()可以输出参数;
在模块里面,我们要用一个zend定义的[code:1:c9ab9647e9]zend_printf()[/code:1:c9ab9647e9]来代替printf(),其用法和printf是一样的,只不过它可以显示输出到php的输出流,一般是网页上;如果使用printf,你将得不到,因为它一般输出到了本地屏幕。

如果要明显的提示是一个错误信息,你还可以使用:
[code:1:c9ab9647e9]zend_error(参数类型,提示信息)[/code:1:c9ab9647e9]
参数类型表示提示出的错误的类型,这个需要你自己测试可以看到更好的结果;
一般有以下类型:
E_ERROR   表示错误
E_WARNING  表示警告
E_PARSE  表示语法错误
E_NOTICE  表示提示

他们的提示是不同的,你如果测试就立刻知道了。

有了他们,我们可以在模块之中及时的进行调试,获取参数和变量的中间值。

HonestQiao 回复于:2005-05-20 17:49:06 可以获取参数,可以打印信息,如果要完成我们的简单模块测试,现在需要知道,如何返回信息了。

还是前面说的黑箱子,别人可以给我们传入参数了,我们可以用几个红绿灯来表示状态了,然后需要返回给它可用的信息。

如何返回信息呢?
实际上很简单,在c里面,返回信息使用return,在模块里面,我们使用
RETURN_类型,类型可以为:
BOOL
NULL
LONG
DOUBLE
STRING
(未完待续)

飞雪恨水 回复于:2005-05-21 11:05:36 不顶不行

goodloveboys 回复于:2005-05-21 13:56:17 收藏

支持一下

myfavourite 回复于:2005-05-22 20:46:45 敬仰……
假如有一天,你有人这样对我说,那该多美妙啊…哈哈…………

kofwang 回复于:2005-05-22 22:01:01 很厉害,最近我也在看一些关于zend api方面的东西,苦于英文资料太多,而且国内讨论这方面的人好像不多。看到这篇文章,收获很大

riverfor 回复于:2005-05-24 23:05:29 好咚咚
偶以前用swig实现封装和快速开发,但是没有认真使用zend api,
在用swig时,处理一些数据结构采用大方法是以指针的形式处理,然后再需要写一个类似于私有函数根据这个指针地址获取数据结构。

用swig可以快速开发,但是问题是用了很多垃圾代码。 :(

swig同时可以开发perl, python, tcl等的扩展。

yaoyouyou 回复于:2005-06-22 10:55:21 顶!
不过如何写扩展的时候把库文件也免疫激怒起]

yaoyouyou 回复于:2005-06-22 10:58:46 如何用到静态库,怎么样一起编译进去

timwhoung 回复于:2005-06-23 11:58:27 好文!!希望多发些这类的文章!
另外请问一下:
为什么我按你的步骤做好后,命令行运行提示未定义函数!
设置了extension_dir,也把.so拷了过去,也一样
直接用dl,提示找不到so.但路径却是/web/php/lib/php/20020429,后来把.so拷到此目录,调用成功,查看php-config把相应的extensions改为来,再重编译,但还是放在/web/php/lib/php/20020429才可以正常,不解中...

timwhoung 回复于:2005-06-23 12:27:37 好文!!希望多发些这类的文章!
另外请问一下:
为什么我按你的步骤做好后,命令行运行提示未定义函数!
设置了extension_dir,也把.so拷了过去,也一样
直接用dl,提示找不到so.但路径却是/web/php/lib/php/20020429,后来把.so拷到此目录,调用成功,查看php-config把相应的extensions改为来,再重编译,但还是放在/web/php/lib/php/20020429才可以正常,不解中...

iamcm 回复于:2005-06-26 03:51:44 难得一见这种内容哦,这个斑竹,偶服!!!

太好了,有时间慢慢看,收藏先。

HonestQiao 回复于:2005-07-20 11:20:22 今天因为服务器升级到了php5,发现,php4下面的ZendAPi开发的扩展模块,把目录直接拷贝过来,然后
make clean
configure
make
^_^,扩展模块可以直接使用了,真不错。

dulao5 回复于:2005-08-01 19:30:10 cool~~

waituy 回复于:2005-08-02 08:54:19 楼主真强!

水若寒 回复于:2005-08-14 20:44:24 非常好,很早以前就想用这个方法,做一个PHP的扩展,功能是实现用PHP通过电脑连接手机发送和接收SMS,不知道楼主有没兴趣做一个呢?
php爱好者站 http://www.phpfans.net PHP|MySQL|javascript|ajax|html.
相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载