PHP开发第一个扩展
首先声明:我们要构建的是扩展或者模块名为hello_module.该模块提供一个方法:hello_word.
一、PHP环境的搭建
1)一般使用源码包编译安装,而不是binary包安装。因为使用PHP的二进制分发包安装有些冒险,这些版本倾向于忽略./configure的两个重要选项,它们在开发过程中很便利:
第一个--enable-debug。这个选项将把附加的符号信息编译进PHP的执行文件,以便如果发生段错误,你能从中得到一个内核转储文件,使用gdb追踪并发现什么地方以及为什么会发生段错误。
2)另 一个选项依赖于你的PHP版本。在PHP 4.3中该选项名为--enable-experimental-zts,在PHP 5及以后的版本中为--enable-maintainer-zts。这个选项使PHP以为自己执行于多线程环境,并且使你能捕获通常的程序错误,然而它 们在非多线程环境中是无害的,却使你的扩展不可安全用于多线程环境。一旦你已经使用这些额外的选项编译了PHP并安装于你的开发服务器(或者工作站)中,你就可以把你的第一个扩展加入其中了。
3)Zend 引擎提供了一个内存管理器,有在扩展中跟踪内存泄漏的能力并提供详尽的调试信息。跟踪在默认情况下是被禁用的,同时也是线程安全的。要打开的话,应将 --enable-debug 和 --enable-maintainer-zts 选项与其他常用选项一起传给 configure。要获得从源代码构建 PHP 的说明,请看位于 安装前需要考虑的事项 的说明。
典型的 configure 命令行可能看起来象这样:
$ ./configure
--prefix=/usr/local/php \
--enable-debug \
--enable-maintainer-zts \
--enable-cgi \
--enable-cli \
--with-mysql=/path/to/mysql
1)一般可以把php安装在/usr/local/php目录下。
2)php的二进制可执行文件都在/usr/local/php/bin目录,包括php自带工具phpize.
phpize实际上是个shell脚本,可以用vi phpize来查看其内容.
注意:使用phpize需要安装autoconf 宏。因为config.m4 文件使用 GNU autoconf 语法编写。简而言之,就是用强大的宏语言增强的 shell脚本。注释用字符串 dnl 分隔,字符串则放在左右方括号中间(例如,[ 和 ])。字符串可按需要多次嵌套引用。完整的语法参考可参见位于http://www.gnu.org/software/autoconf/manual/
安装autoconf 宏最简单的方法:apt-get install autoconf
2、ext_skel脚本
PHP 扩展由几个文件组成,这些文件对所有扩展来说都是通用的。不同扩展之间,这些文件的很多细节是相似的,只是要费力去复制每个文件的内容。幸运的是,有脚本可以做所有的初始化工作,名为 ext_skel,自 PHP 4.0 起与其一起分发。
不带参数运行 ext_skel 在 PHP 5.3.2 中会产生以下输出:
./ext_skel --extname=module [--proto=file] [--stubs=file] [--xml[=file]]
[--skel=dir] [--full-xml] [--no-help]
--extname=module module is the name of your extension
--proto=file file contains prototypes of functions to create
--stubs=file generate only function stubs in file
--xml generate xml documentation to be added to phpdoc-cvs
--skel=dir path to the skeleton directory
--full-xml generate xml documentation for a self-contained extension
(not yet implemented)
--no-help don't try to be nice and create comments in the code
and helper functions to test if the module compiled
开发一个新扩展时,仅需关注的参数是 --extname 和 --no-help。除非已经熟悉扩展的结构,不要想去使用 --no-help; 指定此参数会造成 ext_skel 不会在生成的文件里省略很多有用的注释。
剩下的 --extname 会将扩展的名称传给 ext_skel。"name" 是一个全为小写字母的标识符,仅包含字母和下划线,在 PHP 发行包的 ext/ 文件夹下是唯一的。
--proto选项允许开发人员指定一个头文件,由此创建一系列 PHP 函数,表面上看就是要开发基于一个函数库的扩展,但对大多数头现代的文件来说很少能起作用。如果用 zlib.h 头文件来做测试,就会导致在 ext_skel 的输出文件中存在大量的空的和无意义的原型文件。--xml 和 --full-xml 选项当前完全不起作用。--skel 选项可用于指定用一套修改过的框架文件来工作,这是本节范围之外的话题了
3、扩展组成文件
不管是通过手工,通过ext_skel ,还是通过另外的扩展生成器,所有的扩展都会有以下个文件:
1) config.m4:phpize
用来准备构建系统哪些扩展的配置文件configure 选项 ,是UNIX 构建系统配置。
对应的win系统是config.w32:http://www.php.net/manual/zh/internals2.buildsys.configunix.php
2) php_hello_module.h:
包含引用的头文件当将扩展作为静态模块构建并放入PHP 二进制包时,构建系统要求用 php_ 加扩展的名称命名的 头文件包含一个对扩展模块结构的指针定义。就象其他头文件,此文件经常包含附加的宏、原型和全局量。当然你可以把头文件内容放在源文件hello_module.c顶部。分开只是让代码组织更清晰,而且是个很好的习惯。
3) hello_module.c :
包含模块函数的源码文件
扩展应包含任意数量的头文件、源文件、单元测试和其他支持文件,此四个文件仅够组成最小的扩展。hello_module扩展的文件列表如下所示:
ext/hello_module/
config.m4
config.w32
CREDITS
EXPERIMENTAL
php_hello_module.h
hello_module.c
tests/
001.phpt
config.m4 文件举例
dnl $Id$
dnl config.m4 for extension example
PHP_ARG_WITH(example, for example support,
[ --with-example[=FILE] Include example support. File is the optional path to example-config])
PHP_ARG_ENABLE(example-debug, whether to enable debugging support in example,
[ --enable-example-debug example: Enable debugging support in example], no, no)
PHP_ARG_WITH(example-extra, for extra libraries for example,
[ --with-example-extra=DIR example: Location of extra libraries for example], no, no) dnl 检测扩展是否已启用
if test "$PHP_EXAMPLE" != "no"; then dnl 检测 example-config。首先尝试所给出的路径,然后在 $PATH 中寻找
AC_MSG_CHECKING([for example-config])
EXAMPLE_CONFIG="example-config"
if test "$PHP_EXAMPLE" != "yes"; then
EXAMPLE_PATH=$PHP_EXAMPLE
else
EXAMPLE_PATH=`$php_shtool path $EXAMPLE_CONFIG`
fi dnl 如果找到可用的 example-config,就使用它
if test -f "$EXAMPLE_PATH" && test -x "$EXAMPLE_PATH" && $EXAMPLE_PATH --version > /dev/null 2>&1; then
AC_MSG_RESULT([$EXAMPLE_PATH])
EXAMPLE_LIB_NAME=`$EXAMPLE_PATH --libname`
EXAMPLE_INCDIRS=`$EXAMPLE_PATH --incdirs`
EXAMPLE_LIBS=`$EXAMPLE_PATH --libs` dnl 检测扩展库是否工作正常
PHP_CHECK_LIBRARY($EXAMPLE_LIB_NAME, example_critical_function,
[
dnl 添加所需的 include 目录
PHP_EVAL_INCLINE($EXAMPLE_INCDIRS)
dnl 添加所需的扩展库及扩展库所在目录
PHP_EVAL_LIBLINE($EXAMPLE_LIBS, EXAMPLE_SHARED_LIBADD)
],[
dnl 跳出
AC_MSG_ERROR([example library not found. Check config.log for more information.])
],[$EXAMPLE_LIBS]
)
else
dnl 没有可用的 example-config,跳出
AC_MSG_RESULT([not found])
AC_MSG_ERROR([Please check your example installation.])
fi dnl 检测是否启用调试
if test "$PHP_EXAMPLE_DEBUG" != "no"; then
dnl 是,则设置 C 语言宏指令
AC_DEFINE(USE_EXAMPLE_DEBUG,1,[Include debugging support in example])
fi dnl 检测额外的支持
if test "$PHP_EXAMPLE_EXTRA" != "no"; then
if test "$PHP_EXAMPLE_EXTRA" == "yes"; then
AC_MSG_ERROR([You must specify a path when using --with-example-extra])
fi PHP_CHECK_LIBRARY(example-extra, example_critical_extra_function,
[
dnl 添加所需路径
PHP_ADD_INCLUDE($PHP_EXAMPLE_EXTRA/include)
PHP_ADD_LIBRARY_WITH_PATH(example-extra, $PHP_EXAMPLE_EXTRA/lib, EXAMPLE_SHARED_LIBADD)
AC_DEFINE(HAVE_EXAMPLEEXTRALIB,1,[Whether example-extra support is present and requested])
EXAMPLE_SOURCES="$EXAMPLE_SOURCES example_extra.c"
],[
AC_MSG_ERROR([example-extra lib not found. See config.log for more information.])
],[-L$PHP_EXAMPLE_EXTRA/lib]
)
fi dnl 最后,将扩展及其所需文件等信息传给构建系统
PHP_NEW_EXTENSION(example, example.c $EXAMPLE_SOURCES, $ext_shared)
PHP_SUBST(EXAMPLE_SHARED_LIBADD)
fi
注意:凡是带有dnl前缀的都是注释,注释是不被解析的。
4.1 PHP_ARG_*: 赋予用户可选项
在以上的 config.m4 例子中,两条注释后,最先见到的 3 行代码,使用了 PHP_ARG_WITH() 和 PHP_ARG_ENABLE()。这些给 configure 提供了可选项,和在运行 ./configure --help 时显示的帮助文本。就象名称所暗示的,其两者的不同点在于是创建 --with-* 选项还是 --enable-* 选项。每个扩展应提供至少一个以上的选项以及扩展名称,以便用户可选择是否将扩展构建至 PHP 中。按惯例,PHP_ARG_WITH() 用于取得参数的选项,例如扩展所需库或程序的位置;而 PHP_ARG_ENABLE() 用于代表简单标志的选项。
不管你使用哪一个指令,你都应该注释掉另外一个。也就是说,如果你使用了–enable-my_module,那就应该去掉–with-my_module。反之亦然。
configue使用例子:./configure --enable-hello_module
则将
dnl PHP_ARG_ENABLE(hello_module, whether to enablehello_module support,
dnl Make sure that the comment is aligned:
dnl [ --enable-hello_module Enablehello_module support])
修改成
PHP_ARG_ENABLE(hello_module, whether to enablehello_module support,
dnl Make sure that the comment is aligned:
[ --enable-hello_module Enablehello_module support])
4.2 处理用户选择
config.m4可给用户提供一些做什么的选择,现在就是做出选择的时候了。在上例中,三个选项在未指定时显然默认为 "no"。习惯上,最好用此值作为用于启用扩展的选项的默认值,为了扩展与 PHP 分开构建则用 phpize 覆盖此值,而要构建在 PHP 中时则不应被默认值将扩展空间弄乱。处理这三个选项的代码要复杂得多。
--with-example[=FILE] 选项
首先检测是否设置了 --with-example[=FILE] 选项。如此选项未被指定,或使用否定的格式(--without-example ),或赋值为 "no" 时,它会控制整个扩展的含有物其他任何事情都不会发生。在上面的例子中所指定的值为 /some/library/path/example-config,所以第一个 test 成功了。
接下来,代码调用 AC_MSG_CHECKING(),这是一个 autoconf 宏,输出一行标准的如 "checking for ..." 的信息,并检测用户假定的 example-config 是否是一个明确的路径。在这个例子中,PHP_EXAMPLE 所取到的值为 /some/library/path/example-config,现已复制到 EXAMPLE_PATH 变量中了。只有用户指定了 --with-example ,才会执行代码 $php_shtool path $EXAMPLE_CONFIG,尝试使用用户当前的 PATH 环境变量推测 example-config 的位置。无论如何,下一步都是检测所选的EXAMPLE_PATH 是否是正常文件,是否可执行,及是否执行成功。如成功,则调用 AC_MSG_RESULT(),结束由 AC_MSG_CHECKING() 开始的输出行。否则,调用 AC_MSG_ERROR(),打印所给的信息并立即中断执行 configure。
代码现在执行几次 example-config 以确定一些站点特定的配置信息。下一步调用的是 PHP_CHECK_LIBRARY(),这是 PHP 构建系统提供的一个宏,包装了 autoconf 的 AC_CHECK_LIB() 函数。PHP_CHECK_LIBRARY() 尝试编译、链接和执行程序,在第一个参数指定的库中调用由第二个参数指定的符号,使用第五个参数给出的字符串作为额外的链接选项。如果尝试成功了,则运行第三个参数所给出的脚本。此脚本从 example-config 所提供的原始的选项字符串中取出头文件路径、库文件路径和库名称,告诉 PHP 构建系统。如果尝试失败,脚本则运行第四个参数中的脚本。此时调用 AC_MSG_ERROR() 来中断程序执行。
--enable-example-debug 选项
处理 --enable-example-debug 很简单。只简单地检测其真实值。如果检测成功,则调用 AC_DEFINE() 使 C 语言宏指令 USE_EXAMPLE_DEBUG 可用于扩展的源代码。第三个参数是给 config.h 的注释字符串,通常可放心的留空。
--with-example-extra=DIR 选项
对于此例子来说,由 --with-example-extra=DIR 选项所请求的假定的“额外”功能操作不会与假定的 example-config 程序共享,也没有默认的搜索路径。因此,用户需要在所需的库之前提供设置程序。有点不象现实中的扩展,在这里的设置仅仅起说明性的作用。
代码开始用已熟知的方式来检测 PHP_EXAMPLE_EXTRA 的真实值。如果所提供的为否定形式,则不会进行其他处理,用户也不会请求额外的功能。如果所提供的为未提供参数的肯定形式,则调用 AC_MSG_ERROR() 中止处理。下一步则再次调用PHP_CHECK_LIBRARY()。这一次,因为没有提供预定义编译选项,PHP_ADD_INCLUDE() 和 PHP_ADD_LIBRARY_WITH_PATH() 用于构建额外功能所需的头文件路径、库文件路径和库标志。也调用 AC_DEFINE() 来指示所请求的额外功能代码是可用的,设置变量来告诉以后的代码,有额外的源代码要构建。如果检测失败,则调用所熟悉的 AC_MSG_ERROR()。另一种不同的处理失败的方式是更换为调用 AC_MSG_WARNING(),例如:
4.3 PHP_NEW_EXTENSION宏
默认情况下,通过ext_skel创建的config.m4都能接受指令,并且会自动启用该扩展。启用该扩展是通过PHP_EXTENSION这个宏进行的。如果你要改变一下默认的情况,想让用户明确的使用 –enable-my_module或 –with-my_module指令来把扩展包含在PHP二进制文件当中,那么将 “if test "$PHP_MY_MODULE" != "no"”改为“if test "$PHP_MY_MODULE" == "yes"”即可。
if test "$PHP_MY_MODULE" == "yes"; thendnl
PHP_EXTENSION(hello_module, $ext_shared)
fi
这样就会导致在每次重新配置和编译PHP时都要求用户使用 –enable-my_module指令。
PHP_NEW_EXTENSION()
就是宏告诉构建系统去构建扩展本身和被其用到的文件。
PHP_NEW_EXTENSION()参数:
第一个参数是扩展的名称,和包含它的目录同名。
第二个参数是做为扩展的一部分的所有源文件的列表,参见 PHP_ADD_BUILD_DIR() 以获取将在子目录中源文件添加到构建过程的相关信息。
第三个参数总是 $ext_shared, 当为了 --with-example[=FILE] 而调用 PHP_ARG_WITH()时,由 configure 决定参数的值。
第四个参数指定一个“SAPI 类”,仅用于专门需要 CGI 或 CLI SAPI 的扩展。其他情况下应留空。
第五个参数指定了构建时要加入 CFLAGS 的标志列表。
第六个参数是一个布尔值,为 "yes" 时会强迫整个扩展使用 $CXX 代替 $CC 来构建。
第三个以后的所有参数都是可选的。最后,调用 PHP_SUBST() 来启用扩展的共享构建。
例如我们要构建扩展是hello_module
dnl $Id$
dnl config.m4 for extension hello
PHP_ARG_ENABLE(hello, whether to enable hello support,
[ --enable-hello Enable hello support])
if test "$PHP_HELLO" != "no"; then
PHP_NEW_EXTENSION(hello_module, hello_module.c, $ext_shared)
fi
5、具体生成扩展
第一步,生成代码
PHP为了扩展开发的方便,提供了一个类似代码生成器的工具ext_skel,具体可以参见说明。
首先我们创建一个文件helloworld.skel,
它的内容为
string getstring(string str) 执行:
./ext_skel --extname=helloworld--proto=helloworld.skel 1、.svnignore 当在SVN协作模式下开发时,默认使文件不被SVN识别。
2、EXPERIMENTAL 该文件用来标识并记录稳定版发布前的开发日志、敬告、提示等相关的信息。当正式发行时应该删除它。
3、config.w32 在Windows环境下用到的配置文件,通常是需要修改的。
4、config.m4 在linux/Unix环境下用到的配置文件,通常也是需要修改的。
5、phpext.c 默认包含基本的扩展示例源码,即,默认情况下,扩展的代码写到这里面。
6、php_phpext.h 扩展的源文件所包含的头文件,里面包含对函数的定义。
7、CREDITS 第一行写扩展的名字,第二行写参与者的名字,多个用逗号隔开。
8、phpext.php 一个用来较验扩展是否正常加载、工作的PHP文件,根据实际情况修改。
9、tests 文件夹内将默认存储与扩展Test相关的数据或文件。
这时候,helloworld这个扩展的代码框架就已经出来了
然后修改config.m4文件将10、11、12三行最前面的dnl删除掉,就是将
dnl PHP_ARG_WITH(helloworld, for helloworldsupport,
dnl Make sure that the comment is aligned:
dnl [ --with-helloworld Include helloworld support]) 修改为 PHP_ARG_WITH(helloworld, for helloworld support,
Make sure that the comment is aligned:
[ --with-helloworld Include helloworld support])
第三步,实现功能
修改源码helloworld.c文件,找到将getstring这个函数修改为
PHP_FUNCTION(getstring)
{
char *str = NULL;
int argc = ZEND_NUM_ARGS();
int str_len;
char *result; if (zend_parse_parameters(argc TSRMLS_CC, "s", &str, &str_len) == FAILURE)
return; str_len = spprintf(&result, 0, "%s", str);
RETURN_STRINGL(result, str_len, 0);
}
第四步,编译扩展
phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install
第五步,添加扩展
这时候,一切顺利的话,该扩展已经在modules/ccvita.so这个位置了。下面就是将这个扩展加入到PHP中去,让我们PHP程序可以调用到。
vim /usr/local/php/etc/php.ini
extension=helloworld.so #在php.ini文件最后增加这一行
service php-fpm restart #重启PHP服务
看一下鸟哥的一张图片
PHP开发第一个扩展的更多相关文章
- PHP扩展开发:第一个扩展
在上一篇文章<PHP扩展开发:安装PHP>我们已经将开发PHP扩展的PHP环境安装成功,那么接下来采用最简单直接的方式创建第一个扩展. 我们先假设业务场景,是需要有这么一个扩展,提供一个叫 ...
- iOS开发系列--App扩展开发
概述 从iOS 8 开始Apple引入了扩展(Extension)用于增强系统应用服务和应用之间的交互.它的出现让自定义键盘.系统分享集成等这些依靠系统服务的开发变成了可能.WWDC 2016上众多更 ...
- 【翻译习作】 Windows Workflow Foundation程序开发-第一章05
1.3 开发我们的第一个工作流 也许你曾经在这样的产品经理手下搞过开发:他总是在你身边转悠,并不时的问一句“你还没做完吗?”.在这一部分,我们将用一个简单的Windows Workflow程 ...
- 【翻译习作】 Windows Workflow Foundation程序开发-第一章04
1.2.3 Windows Workflow运行时 从Windows Workflow的角度看,可以将工作流活动当成是交给一个工作流处理器去执行的一系列指令或操作码.在Windows Workflo ...
- 【翻译习作】 Windows Workflow Foundation程序开发-第一章03
1.2.2.Visual Studio 2005扩展包 微软也为Windows Workflow开发者提供了Visual Studio 2005扩展包.扩展包将许多功能集成到Visual Studio ...
- 【翻译习作】 Windows Workflow Foundation程序开发-第一章02
1.2 Windows Workflow概览 微软的Windows Workflow Foundation(简称WF)是.NET框架3.0版的一部分..NET3.0其它主要部分是Window ...
- 如何用原生js开发一个Chrome扩展程序
原文地址:How to Build a Simple Chrome Extension in Vanilla JavaScript 开发一个Chrome扩展程序非常简单,只需要使用原生的js就可以完成 ...
- 关于windows下基于php7.0.2下编写的第一个扩展
网上的教程是比较多的,但是基于php7+windows的教程非常之少,通过几天的摸索及参考很多资料,终于发现如下可以运行. php7要求使用vc2015,同时安装sdk,我使用的是8.1的window ...
- Electron-使用Electron开发第一个应用
使用Electron开发第一个应用 Electron 应用的目录结构如下: app/ ├── package.json ├── main.js └── index.html 新建一个app文件夹 将这 ...
随机推荐
- BZOJ 1054 广搜
1054: [HAOI2008]移动玩具 在一个4*4的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动 时只能将玩具向上下左右四个方向移动,并且移动的位置不能有玩 ...
- eclipse svn插件安装方法
eclipse svn插件安装方法 使用dropins安装插件 从Eclipse3.5开始,安装目录下就多了一个dropins目录.只要将插件解压后拖到该目录即可安装插件.比如安装svn插件subcl ...
- java和javascript双引号嵌套的问题
双引号不能嵌套双引号,否则一对双引号就结束了. java中双引号中嵌套双引号需要转义: /** * java双引号转义符测试 * @author asus * */ public static voi ...
- 【循序渐进学Python】11.常用标准库
安装完Python之后,我们也同时获得了强大的Python标准库,通过使用这些标准库可以为我们节省大量的时间.这里是一些常用标准库的简单说明.更多的标准库的说明,可以参考Python文档 sys 模块 ...
- ViewPager的使用
在上培训课的时候,老师一直在将ViewPager是现在的主流,一直想去好好的了解一下,今天去网上学习了一下 ,做一个总结: ViewPager其实就是后来谷歌提供给我们的一个组件,就像TextVi ...
- mongodb安装与使用
一.在linux服务器中安装mongodb 1.首先你要有一台安装有linux系统的主机 2.从mongoDB官网下载安装包:http://www.mongodb.org/downloads 3.将下 ...
- C语言回滚(一)
//用循环计算输入的字符数 #include<stdio.h> #include<string.h> #include<stdlib.h> int main(){ ...
- js小数计算小数点后显示多位小数(转)
首先写一个demo 重现问题,我使用的是一个js在线测试环境[打开] 改写displaynum()函数 function displaynum(){var num = 22.77;alert(num ...
- 时间复杂度&空间复杂度
时间复杂度 参考链接: http://univasity.iteye.com/blog/1164707 空间复杂度 http://blog.csdn.net/booirror/article/deta ...
- javascript函数中的三个技巧【二】
技巧二: [惰性载入函数] 因为浏览器之间的行为的差异,我们经常会在函数中包含了大量的if语句,以检查浏览器特性,解决不同浏览器的兼容问题,比如,我们最常见的为dom节点添加时间的函数 functio ...