PHP Windows 扩展的开发(1)
做为从事PHP开发的人来讲, 有些时候要自己写一些扩展来方便自己的应用。 网络上有很多的PHP开发的例程, 有的讲的还不错, 有的很简单,我把自己学习的过程记录下来。 开发PHP Extension的过程基本可以分为如下几步: 1. 生成扩展框架 2. dsp配置 3. 编写核
做为从事PHP开发的人来讲, 有些时候要自己写一些扩展来方便自己的应用。
网络上有很多的PHP开发的例程, 有的讲的还不错, 有的很简单,我把自己学习的过程记录下来。
开发PHP Extension的过程基本可以分为如下几步:
1. 生成扩展框架
2. dsp配置
3. 编写核心代码
4. 配置、编译
5. 配置php.ini
生成扩展框架
下载PHP源代码,我使用的是PHP 5.2.5。进入PHP源代码目录可以看到有个ext目录,这里是和PHP Extension有关可以看到很多已经存在的PHP Extension,如pdo_mysql,json等
linux和windows会使用不同的方法来生成skeleton.
本文主要讲述在windows下的开发, 有两种方法
1. 安装cygwin,然后使用上图中的windows那个文件 php ext_skel_win32.php -extname=
2. 直接修改skeleton文件的内容 。
首先将ext/skeleton拷贝一份, 将名字改为你想用的extname, 本文用hello world为例。
=>>>>
dsp 配置
再将php/dev目录下的php5t.lib拷贝到当前的目录下。
将上面4个文件中所有的extname 变成 hello_world, 将EXTNAME 变成HELLO_WORLD, 注意大小写
这样就可以进行简单的编译生成 dsw工程文件。
分析PHP Extension核心代码
为了下面更好的介绍,这里先简单的介绍PHP Extension核心代码,打开hello_world.c文件
这里初始化了一个C语言中的结构体,每个PHP Extension其实就是一个zend_module_entry结构体,在该结构体中定义了每个扩展所需的字段,大家可以通过查看 zend_module_entry源代码看到。就本例而言,我们简单的介绍一下上面的代码:
1. STANDARD_MODULE_HEADER:C语言的宏,用来初始化zend_module_entry的前几个字段,包括结构体大小等
2. hello_world:指定了扩展的名字,对应结构体中的name字段
3. hello_world_functions:一个zend_function_entry类型的数组,指向扩展的函数表,所有需要暴露给用户的函数都需要在该函数表中注册
4. PHP_MINIT(hello_world):模块初始化回调函数,在扩展被加载时调用,MINIT = Module Initialization
5. PHP_MSHUTDOWN(hello_world):模块卸载回调函数,在扩展杯卸载时调用,MSHUTDOWN = Module Shutdown
6. PHP_RINIT(hello_world):请求初始化回调函数,每个请求开始时调用,RINIT = Request Initialization
7. PHP_RSHUTDOWN(hello_world):请求结束回调函数,每个请求结束时调用,RSHUTDOWN = Request Shutdown
8. PHP_MINFO(hello_world):扩展信息函数,在phpinfo()函数中会调用,用于显示模块的自定义信息。
9. 0.1:指定了扩展的版本号,对应结构体中的version字段
10. STANDARD_MODULE_PROPERTIES:C语言的宏,用来初始化zend_module_entry的后几个字段
大家可以看到,在4-8我们指定了4个回调函数,这四个函数可以说我们提供了一种注入机制,让我们能够在这几个关键点进行资源的初始化或者资源回 收。另外,需要说明的一点是,所有的回调函数我们都是通过Zend提供的宏定义的,主要是为了防止在PHP运行时的命名冲突问题,事实上不仅仅是函数,包 括函数返回值、全局变量等我们都会使用这种方式。
编写phpinfo()回调函数
打开hello_world.c文件,在PHP_MINFO_FUNCTION里面编写如下代码:
这里主要是phpinfo()函数调用时显示自定义信息,用到了四个函数:
1. php_info_print_table_start():定义phpinfo表格开始
2. php_info_print_table_header():定义phpinfo表格头,第一个参数指定列数,后面指定与第一个参数数量相等的自定义文字信息
3. php_info_print_table_row():定义phpinfo表格内容,第一个参数指定列数,后面指定与第一个参数数量相等的自定义文字信息
4. php_info_print_table_end():定义phpinfo表格结尾
在本例中我们定义了表格头,指定扩展是否可用;另外定义了两行内容,指定扩展的作者和版本。
编写核心代码
接下来是时候编写我们的扩展核心代码了,打开php_hello_world.h文件,添加一行声明:
注意这里不是用“原生”的编写C语言函数的方式,而是通过PHP_FUNCTION宏定义(具体原因前面讲过),say_hello是我们开发的扩展模块要暴露给用户的函数名称。
打开hello_world.c,在这里实现say_hello函数:
里面的具体实现很简单,接收到参数之后,返回“Hello 参数”字符串,需要解释的是:
1. 参数接收:这里接收函数的参数需要通过zend_parse_parameter函数解析,第一个参数指定用户传入say_hello函数的参数个 数,可以通过宏ZEND_NUM_ARGS()生成,TSRMLS_CC用来确保线程安全;第二个参数是一个字符串,每个字母代表一种类型,其中”s”代 表char*或者int类型,“b”代表布尔类型,“l”代表long类型,完整的类型映射可以看这里 ;后面几个参数是我们定义的局部变量,用来接收传入的参数值
2. 函数返回值:不能使用C语言原生的return语句,而应该使用Zend API里提供的宏定义,如RETURN_STRINGL返回一个字符串;而RETURN_TRUE返回布尔类型true。
声明扩展函数参数信息,我们的函数原型为say_hello(name),声明参数方式:
这里都是Zend API提供的宏定义,在后面我会专门介绍扩展函数参数声明。实现完say_hello函数之后,我们再注册该函数到函数表 hello_world_functions(前面介绍过),第一个参数为函数名,第二个参数为函数参数数组信息,如下代码所示:
注意最后一行{NULL, NULL, NULL}是必须的,只有注册到函数表中的函数才能暴露给用户使用。
配置、编译、安装
直接用VC6生成php_hello_world.dll, 将其拷贝到php/ext目录下, 配置php.ini如下
运行
上面的步骤完成后, 运行 php -i| findstr "hello_world"
编写一个简单的测试脚本,如下所示:
会得到 如下
总结
本文通过一个简单的示例,介绍了如何使用Zend API和C语言在windows下开发一个PHP Extension。