编写第一个php扩展2013-09-06你的第一个扩展每一个php扩展的构建至少需要两个文件: 一个configuration文件, 它告诉编译期要构建哪些文件以及需要什么外部的库, 还需要至少一个源文件, 它执行实际的工作.剖析扩展实际上, 通常会有第二个或第三个配置文件, 以及一个或多个头文件. 对于你的第一个扩展, 你需要添加每种类型的一个文件并使用它们工作.配置文件要开始了, 首先在你的php源代码目录树的ext/目录下创建名为sample的目录. 实际上这个新的目录可以放在任何地方, 但是为了在本章后面演示win32和静态构建选项, 我们还是先建立在源代码目录下吧.下一步, 进入这个目录, 创建一个名为config.m4的文件, 键入下面内容:
PHP_ARG_ENABLE(sample,[Whether to enable the "sample" extension],[enable-sampleEnable "sample" extension support])if test $PHP_SAMPLE != "no"; thenPHP_SUBST(SAMPLE_SHARED_LIBADD)PHP_NEW_EXTENSION(sample, sample.c, $ext_shared)fi
这是./configure时能够调用enable-sample选项的最低要求.PHP_ARG_ENABLE的第二个参数将在./configure处理过程中到达这个扩展的配置文件时显示. 第三个参数将在终端用户执行./configure --help时显示为帮助信息.有没有想过为什么有的扩展配置使用enable-extname, 而有的扩展则使用with-extname? 功能上两者没有区别. 实际上, enable表示启用这个特性不需要其他任何第三方库, 相比之下, with则表示要使用这个特性还有其他先决条件现在, 你的sample扩展并不需要和其他库链接, 因此只需要使用enable版本. 在第17章"外部库"中, 我们将介绍使用with并指示编译器使用额外的CFLAGS和LDFLAGS设置.如果终端用户使用enable-sample选项调用了./configure, 那么本地的环境变量$PHP_SAMPLE, 将被设置为yes. PHP_SUBST()是标准autoconf的AC_SUBST()宏的php修改版, 它在将扩展构建为共享模块时需要.最后但并不是不重要的, PHP_NEW_EXTENSION()定义了模块并枚举了所有必须作为扩展的一部分编译的源文件. 如果需要多个文件, 它可以在第二个参数中使用空格分隔列举, 例如:PHP_NEW_EXTENSION(sample, sample.c sample2.c sample3.c, $ext_shared)最后一个参数是对应于PHP_SUBST(SAMPLE_SHARED_LIBADD)命令的, 在构建共享模块的时候同样需要它.头文件当使用C开发的时候, 将数据的类型定义放到外部的头文件中隔离起来, 由源文件包含是常见的做法. 尽管php并不要求这样, 但是这样做在模块增长到不能放到单个源文件时是有简化作用的.在你的php_sample.h头文件中, 以下面内容开始:
#ifndef PHP_SAMPLE_H/* 防止重复包含 */#define PHP_SAMPLE_H/* 定义扩展的属性 */#define PHP_SAMPLE_EXTNAME"sample"#define PHP_SAMPLE_EXTVER"1.0"/* 在php源码树外面构建时引入配置选项 */#ifdef HAVE_CONFIG_H#include "config.h"#endif/* 包含php的标准头文件 */#include "php.h"/* 定义入口点符号, Zend在加载这个模块的时候使用 */extern zend_module_entry sample_module_entry;#define phpext_sample_ptr &sample_module_entry#endif /* PHP_SAMPLE_H */
这个头文件完成了两个主要的任务: 如果扩展使用phpize工具构建(本书通常都使用这种方式), 那么HAVE_CONFG_H就是已定义的, 这样config.h就会被正常的包含进来. 无论扩展怎样编译, 都会从php源码树中包含php.h. 这个头文件中包含了php源码中访问大部分PHPAPI要使用的其他头文件.接下来, 你的扩展使用的zend_module_entry结构定义为外部的, 这样当这个模块使用extension=xxx加载时, 就可以被Zend使用dlopen和dlsym()取到.译注: 关于模块的加载过程, 请参考译者的一篇博客<从dl("xxx.so");函数分析PHP模块开发>(http://blog.csdn.net/lgg201/article/details/6584095)头文件中还会包含一些预处理, 定义将在原文件中使用的信息.源代码最后, 最重要的你需要在sample.c中创建一个简单的源码骨架:
#include "php_sample.h"zend_module_entry sample_module_entry = {#if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER,#endifPHP_SAMPLE_EXTNAME,NULL, /* Functions */NULL, /* MINIT */NULL, /* MSHUTDOWN */NULL, /* RINIT */NULL, /* RSHUTDOWN */NULL, /* MINFO */#if ZEND_MODULE_API_NO >= 20010901PHP_SAMPLE_EXTVER,#endifSTANDARD_MODULE_PROPERTIES};#ifdef COMPILE_DL_SAMPLEZEND_GET_MODULE(sample)#endif