Home  >  Article  >  Backend Development  >  php扩张开发

php扩张开发

WBOY
WBOYOriginal
2016-06-13 10:39:01750browse

php扩展开发

一. 摘要
PHP Extension是扩展PHP的主要手段,如数据库访问,序列化,或者远程过程调用,使用过PHP的人,其实都使用过PHP Extension,PHP里面很多的函数也是通过扩展实现的,而在PHP源码中包含了几十个扩展,PECL仓库中也提供了上百个扩展,所以只要使用 PHP开发,就不可避免的要开发PHP Extension。本文会用图文并茂的方式一步一步为大家介绍如何开发一个PHP Extension。

开发环境:Ubuntu 10.10,PHP 5.3.5,有一个可运行PHP的Web服务器(Nginx或Apache)
要求:了解C语言基础,了解PHP编程
需求:开发一个名为fetion_echo的Extension,只有一个简单的say_goodbye()函数,输入一个字符串,该函数返回"Goodbye xxx"。

开发PHP Extension的过程基本可以分为如下几步:?
1. 生成扩展框架?
2. Unix Build System配置?
3. 编写phpinfo()回调函数?
4. 编写核心代码?
5. 配置、编译?
6. 配置php.ini

二. 生成扩展框架
下载PHP源代码,我使用的是PHP 5.3.5。进入PHP源代码目录可以看到有个ext目录,这里是和PHP Extension有关,使用ls命令查看,可以看到很多已经存在的PHP Extension,如pdo_mysql,json等,如图1所示:

注意在该目录下有一个ext_skel脚本文件,接下来我们将会用它来生成Linux环境下PHP Extension代码框架,而ext_skel_win32.php是windows下的生成脚本。ext_skel完整的命令格式为:
./ext_skel --extname=module [--proto=file] [--stubs=file] [--xml[=file]]<br>[--skel=dir] [--full-xml] [--no-help]
对其中的参数解释如下:?
--extname:参数指定了Extension的名字?
--no-help:指定在生成的代码框架中不加入注释等,除非我们对于开发PHP Extension非常有经验,否则还是不要指定该参数。其他的参数我们暂时可以不用考虑。

接下来在ext目录下输入如下命令:
$ ./ext_skel --extname=fetion_echo
再次使用ls命令查看ext目录,可以看到多了一个名为fetion_echo的目录,进入该目录可以看到ext_skel已经为我们建立好了PHP Extension的基本框架,如图2所示:

在这里有几个比较重要的文件,我们需要介绍一下:?
config.m4:Linux下的Build System配置文件,会使用它来生成configure文件和makefile。?
php_fetion_echo.h:扩展模块的头文件。?
fetion_echo.c:扩展模块的主程序文件,如果我们的扩展模块中有多个函数,最终所有的函数入口都在该文件中。

三. Unix Build System配置
config.m4文件告诉Unix Build System我们的扩展支持什么configure选项,使用Emacs或者vim打开该文件,大家可以看到一堆不认识的配置,不过不用担心,因为该文件 中以"dnl"开头的都是注释,暂时不用考虑。我们能用到只有下面几行:
dnl If your extension references something external, use with:<br>dnl PHP_ARG_WITH(fetion_echo, for fetion_echo support,<br>dnl Make sure that the comment is aligned:<br>dnl [ --with-fetion_echo Include fetion_echo support])<br>dnl Otherwise use enable:<br><span style="color: #ff0000;">dnl PHP_ARG_ENABLE(fetion_echo, whether to enable fetion_echo support,<br>dnl Make sure that the comment is aligned:<br>dnl [ --enable-fetion_echo Enable fetion_echo support])</span>
其实大家都能看明白,如果扩展使用了一些外部的引用,就使用下面的三行,否则使用最后面的三行,由于我们只是开发一个简单的echo扩展,没有使用任何外部引用,只要取消掉最后三行的注释就行:
PHP_ARG_ENABLE(fetion_echo, whether to enable fetion_echo support,<br>Make sure that the comment is aligned:<br>[ --enable-fetion_echo Enable fetion_echo support])<span class="Apple-converted-space">?</span>
保存退出,至此配置文件就完成了。

四. 分析PHP Extension核心代码
为了下面更好的介绍,这里先简单的介绍PHP Extension核心代码,打开fetion_echo.c文件,可以看到如下一段代码:
/* {{{ fetion_echo_module_entry */<br>zend_module_entry fetion_echo_module_entry = {<br>#if ZEND_MODULE_API_NO >= 20010901<br>STANDARD_MODULE_HEADER,<br>#endif<br>"fetion_echo",<br>fetion_echo_functions,<br>PHP_MINIT(fetion_echo),<br>PHP_MSHUTDOWN(fetion_echo),<br>PHP_RINIT(fetion_echo),<br>PHP_RSHUTDOWN(fetion_echo),<br>PHP_MINFO(fetion_echo),<br>#if ZEND_MODULE_API_NO >= 20010901<br>"0.1",<br>#endif<br>STANDARD_MODULE_PROPERTIES<br>};
这里初始化了一个C语言中的结构体,每个PHP Extension其实就是一个zend_module_entry结构体,在该结构体中定义了每个扩展所需的字段,大家可以通过查看 zend_module_entry源代码看到。就本例而言,我们简单的介绍一下上面的代码:
1. STANDARD_MODULE_HEADER:C语言的宏,用来初始化zend_module_entry的前几个字段,包括结构体大小等?
2. fetion_echo:指定了扩展的名字,对应结构体中的name字段?
3. fetion_echo_functions:一个zend_function_entry类型的数组,指向扩展的函数表,所有需要暴露给用户的函数都需要在该函数表中注册?
4. PHP_MINIT(fetion_echo):模块初始化回调函数,在扩展被加载时调用,MINIT = Module Initialization?
5. PHP_MSHUTDOWN(fetion_echo):模块卸载回调函数,在扩展杯卸载时调用,MSHUTDOWN = Module Shutdown?
6. PHP_RINIT(fetion_echo):请求初始化回调函数,每个请求开始时调用,RINIT = Request Initialization?
7. PHP_RSHUTDOWN(fetion_echo):请求结束回调函数,每个请求结束时调用,RSHUTDOWN = Request Shutdown?
8. PHP_MINFO(fetion_echo):扩展信息函数,在phpinfo()函数中会调用,用于显示模块的自定义信息?
9. 0.1:指定了扩展的版本号,对应结构体中的version字段?
10. STANDARD_MODULE_PROPERTIES:C语言的宏,用来初始化zend_module_entry的后几个字段
大家可以看到,在4-8我们指定了4个回调函数,这四个函数可以说我们提供了一种注入机制,让我们能够在这几个关键点进行资源的初始化或者资源回 收。另外,需要说明的一点是,所有的回调函数我们都是通过Zend提供的宏定义的,主要是为了防止在PHP运行时的命名冲突问题,事实上不仅仅是函数,包 括函数返回值、全局变量等我们都会使用这种方式。

五. 编写phpinfo()回调函数?
打开fetion_echo.c文件,在PHP_MINFO_FUNCTION里面编写如下代码:
PHP_MINFO_FUNCTION(fetion_echo) {<br>php_info_print_table_start();<br>php_info_print_table_header(2, "fetion_echo support", "enabled");<br><span style="color: #ff0000;">php_info_print_table_row(2, "author", "TerryLee");<br>php_info_print_table_row(2, "version", "0.1");</span><br>php_info_print_table_end();<br>/* Remove comments if you have entries in php.ini<br>DISPLAY_INI_ENTRIES();<br>*/<br>}
这里主要是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_fetion_echo.h文件,添加一行声明:
PHP_FUNCTION(say_goodbye);
注意这里不是用"原生"的编写C语言函数的方式,而是通过PHP_FUNCTION宏定义(具体原因前面讲过),say_goodbye是我们开发的扩展模块要暴露给用户的函数名称。

打开fetion_echo.c,在这里实现say_goodbye函数:
PHP_FUNCTION(say_goodbye) {<br>char *arg = NULL;<br>int arg_len, len;<br>char *strg;<br>if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",<br>&arg, &arg_len) == FAILURE) {<br>return;<br>}<br>len = spprintf(&strg, 0, "Goodbye %s\n", arg);<br>RETURN_STRINGL(strg, len, 0);<br>}
里面的具体实现很简单,接收到参数之后,返回"Goodybye 参数"字符串,需要解释的是:
1. 参数接收:这里接收函数的参数需要通过zend_parse_parameter函数解析,第一个参数指定用户传入say_goodbye函数的参数个 数,可以通过宏ZEND_NUM_ARGS()生成,TSRMLS_CC用来确保线程安全;第二个参数是一个字符串,每个字母代表一种类型,其中"s"代 表char*或者int类型,"b"代表布尔类型,"l"代表long类型,完整的类型映射可以看这里;后面几个参数是我们定义的局部变量,用来接收传入的参数值
2. 函数返回值:不能使用C语言原生的return语句,而应该使用Zend API里提供的宏定义,如RETURN_STRINGL返回一个字符串;而RETURN_TRUE返回布尔类型true。

声明扩展函数参数信息(要写在头文件),我们的函数原型为say_goodbye(name),声明参数方式:
ZEND_BEGIN_ARG_INFO(arg_say_goodbye, 0)<br>ZEND_ARG_INFO(0, name)<br>ZEND_END_ARG_INFO()
这里都是Zend API提供的宏定义,在后面我会专门介绍扩展函数参数声明。实现完say_goodbye函数之后,我们再注册该函数到函数表 fetion_echo_functions(前面介绍过),第一个参数为函数名,第二个参数为函数参数数组信息,如下代码所示:
const zend_function_entry fetion_echo_functions[] = {<br><span style="color: #ff0000;">PHP_FE(say_goodbye, arg_say_goodbye)<br>{NULL, NULL, NULL}</span><br>};
注意最后一行{NULL, NULL, NULL}是必须的,只有注册到函数表中的函数才能暴露给用户使用。

七. 配置、编译、安装?
在fetion_echo目录下输入如下命令,具体路径请根据自己安装的PHP路径设置:
/usr/local/php5/bin/phpize
注意如果没有安装过m4和autoconf,请先使用下列命令安装 :
sudo apt-get install m4<br>sudo apt-get install autoconf
运行phpize之后,再用ls命令可以看到在fetion_echo目录生成了很多的文件,包括configure文件:

下面就可以安装该扩展了,使用Linux下面的"标准"三步安装模式:
./configure <br>或者: ./configure --with-php-config=/usr/local/php/bin/php-config<br>make<br>make install
在安装完成后会提示具体的扩展安装路径,然后就可以把该扩展加入到php.ini配置中,注意extension_dir的设置,重启Web服务器。

/etc/init.d/php-fpm restart

八. 运行?
上面步骤都完成后,运行phpinfo()应该可以看到:

编写一个简单的测试脚本,如下所示:
<?php <br>echo say_goodbye("Foo");<br>?><span class="Apple-converted-space">?</span>
在浏览器里面查看:

如果您能够看到该界面,说明扩展已经工作正常了。

九. 总结
本文通过一个简单的示例,为大家介绍了如何使用Zend API和C语言在Linux下开发一个PHP Extension。下一篇我们看一下如何开发一个简单的类扩展。

参考资料
1. http://www.php.net/manual/en/internals2.buildsys.configunix.php?
2. http://www.php.net/manual/en/internals2.buildsys.skeleton.php

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:怎么在本地搭建PHPWIND论坛Next article:php 除去notice