存档

文章标签 ‘PHP扩展开发之hello world’

php扩展开发之”Hello World!”

2015年7月21日 没有评论

最近在看《PHP扩展开发及内核应用》,书的内容不是特别多,但感觉都是精华,值得大家去一读的.这个系列的文章也是自己的读书的笔记或者一点心得吧,会把自己遇到的问题和踩过的坑都会提出来,大家可以引以为鉴,文章的不足和错误也请大家指出来.PHP做为现在最火的脚本语言之一,在Web方面更是当之无愧的王者,吸引大家使用PHP的原因有很多,而PHP的社区也是非常活跃和成功的,目前PHP7也预计要在十月份出来正式版本的.

其实PHP的扩展也是十分简单和便捷的,当然你在考虑用C/C++扩展你的PHP之前,建议还是考虑一下这么做是必须的或者说是必要的吗(用C/C++扩展你的PHP这篇文章开头关于扩展PHP的动机还是建议大家看一下的)?

现在我们将进入今天的整体写一个简单的整体,开发我们自己的PHP扩展.我下载的PHP版本是PHP5.6.10,也是最新版的,需要注意的是这个系列文章中的PHP都是PHP5以后版本的,之前的都是版本Zend engine都是1而PHP5之后Zend engine升级到2,因此如果你要是还在维护PHP5之前的版本,那么这个系列文章对您的帮助可能不是很大.

PHP源码提供了一个非常便捷的帮助大家创建自己的扩展工具,在ext目录下面大家可以看到它–“ext_skel”,在windows下面替代的工具是”ext_skel_win32.php”.

我们来看一下ext_skel有那些参数


./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-svn
 --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是指定我们自己扩展的名称,proto是指定包含我们想要创建的函数文件,一般文件是def格式的.

这里我们创建自己的hellworld.def文件,内容如下


void hello_world(string name)

下面我们只需要执行下面的命令,就完成了我们扩展的大部分工作了.


./ext_skel --extname=hello_world --proto=helloworld.def

Creating directory hello_world
Creating basic files: config.m4 config.w32 .gitignore hello_world.c php_hello_world.h CREDITS EXPERIMENTAL tests/001.phpt hello_world.php [done].

To use your new extension, you will have to execute the following steps:

1. $ cd ..
2. $ vi ext/hello_world/config.m4
3. $ ./buildconf
4. $ ./configure --[with|enable]-hello_world
5. $ make
6. $ ./sapi/cli/php -f ext/hello_world/hello_world.php
7. $ vi ext/hello_world/hello_world.c
8. $ make

Repeat steps 3-6 until you are satisfied with ext/hello_world/config.m4 and
step 6 confirms that your module is compiled into PHP. Then, start writing
code and repeat the last two steps as often as necessary.

上面的第1行是我们执行的命令,后面的脚本自动生成的内容,而我们开发扩展基本就按照这个流程来完成的.下面我们先去完成我们的hello_world函数去,然后我们的扩展工作基本就完成了.

进入ext/hello_world目录,编辑hello_world.c文件,这是我们扩展的主要文件了,只要我们的扩展内容不是特别多,我们可以完全把函数放在这里面的来完成.找到系统帮我们生成的代码中的”PHP_FUNCTION(hello_world)”,然后我们在此完成我们的hello_world功能.


PHP_FUNCTION(hello_world)
{
char *name = NULL;
int argc = ZEND_NUM_ARGS();
int name_len;

if (zend_parse_parameters(argc TSRMLS_CC, "s", &name, &name_len) == FAILURE)
return;

//php_error(E_WARNING, "hello_world: not yet implemented");
php_printf("Hello World!\n");
php_printf("Hello %s!\n",name);
RETURN_NULL();
}

其实我们就动了4行代码,注释掉php_error,然后和后面加上的3行代码.至此我们的功能就全部实现了.剩下就是按照扩展了.但是如果你去hello_world.c文件里面代码,你会发现ext_skel帮助我们生成很多代码,里面还是有一些东西是需要我们注意一下.

我们来看一下紧跟着hello_world函数的代码.


/* {{{ hello_world_functions[]
 *
 * Every user visible function must have an entry in hello_world_functions[].
 */
const zend_function_entry hello_world_functions[] = {
 PHP_FE(confirm_hello_world_compiled, NULL) /* For testing, remove later. */
 PHP_FE(hello_world, NULL)
 PHP_FE_END /* Must be the last line in hello_world_functions[] */
};
/* }}} */

/* {{{ hello_world_module_entry
 */
zend_module_entry hello_world_module_entry = {
 STANDARD_MODULE_HEADER,
 "hello_world",
 hello_world_functions,
 PHP_MINIT(hello_world),
 PHP_MSHUTDOWN(hello_world),
 PHP_RINIT(hello_world), /* Replace with NULL if there's nothing to do at request start */
 PHP_RSHUTDOWN(hello_world), /* Replace with NULL if there's nothing to do at request end */
 PHP_MINFO(hello_world),
 PHP_HELLO_WORLD_VERSION,
 STANDARD_MODULE_PROPERTIES
};
/* }}} */

“const zend_function_entry hello_world_functions”这个数组定义了我们扩展里面包含那些函数,当初我没有使用ext_skel的proto参数生成,就没有把我自己写的函数加入到hello_world_functions.导致我在编译安装完扩展以后,在PHP代码里面使用我在扩展里面写的函数的时候,就一直报错”Call to undefined function”,还是google好久才找到问题所在.所以如果你不是用ext_skel的proto参数生成的,在编译安装之前要注意一下这里.下面"zend_module_entry hello_world_module_entry "定义是hello_world模块.关于这些内容具体解释可以参考《PHP扩展开发与内核应用》第5章1节和4节的一些内容.

配置config.m4

现在如果我们要安装我们的扩展,还需要配置扩展目录下面的config.m4文件啦.

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

dnl Otherwise use enable:

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

dnl是m4文件的注释, 根据需要去掉相应的注释,PHP_ARG_WITH是我们在configure时”–with-xx”,此种方式一般依赖一些库的.而PHP_ARG_ENABLE是”–enable-xx”的方式,一般都是可以单独编译安装的,不依赖任何其他库支持的.我们这里选择PHP_ARG_ENABLE即可.

安装扩展

下面就是编译安装扩展了,如果你做过相关的工作就可以跳过这个小节的直接动手去尝试了.


phpize

./configure

make

sudo make install

在你的php.ini最后加上


extension=hello_world.so

//如果你的扩展是zend的,那需要改成zend_extension即可

重启你的apache或者php-fpm进行测试即可了.

测试扩展

编写一个测试的php脚本进行一下测试,查看我们的扩展是否安装成功.


<?php
hello_world("king");

?>

//最终输出如下,说明我们的扩展已经正常工作了

Hello World!
Hello king!

我们的hello world扩展开发也完成了.