ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] ### 静态扩展 要开发PHP扩展,第一步要下载PHP源代码,因为里面有开发扩展需要的工具。我下载的是PHP最新版本5.3.3,格式为tar.bz2压缩包。下载地址为:http://cn.php.net/get/php-5.3.3.tar.bz2/from/a/mirror。 下载后,将源代码移动到合适的目录并解压。解压命令为: ``` $ tar -jxvf 源码包名称 ``` 若下载的是tar.gz压缩包,解压命令为 ``` $ tar -zxvf 源码包名称 ``` 解压后,在源代码目录中有个ext目录,这里便是和PHP扩展有关的目录。进入目录后用ls查看,可以看到许多已经存在的扩展。下图是在我的环境下查看的结果: ![](images/screenshot_1529481533465.png) 其中蓝色的均是扩展包目录,其中可以看到我们很熟悉的mysql、iconv和gd等等。而ext_skel是Unix环境下用于自动生成PHP扩展框架的脚本工具,后面我们马上会用到,ext_skel_win32.php是windows下对应的脚本。 * * * * * ### 开发自己的PHP扩展——say_hello #### 生成扩展组件框架 作为初学者,我们不必了解所有命令参数,实际上,大多数情况下只需要提供第一个参数就可以了,也就是扩展模块的名字。因此,我们在ext目录中键入如下命令: ``` $ ./ext_skel --extname=say_hello ``` #### say_hello 这时再用ls查看,会发现多了一个“say_hello”目录,进入这个目录,会发现ext_skel已经为我们建立好了`say_hello`的基本框架,如下: `config.m4`:这是Unix环境下的Build System配置文件,后面将会通过它生成配置和安装。 `php_say_hello.h`:这个文件是扩展模块的头文件。遵循C语言一贯的作风,这个里面可以放置一些自定义的结构体、全局变量等等。 `say_hello.c`:这个就是扩展模块的主程序文件了,最终的扩展模块各个函数入口都在这里。当然,你可以将所有程序代码都塞到这里面,也可以遵循模块化思想,将各个功能模块放到不同文件中。 * * * * * #### 下面的内容主要围绕这三个文件展开。 开发PHP扩展组件的第一步不是写实现代码,而是要先配置好`Build System` 选项。由于我们是在Linux下开发,所以这里的配置主要与`config.m4`有关。 关于`Build System`配置这一块,要是写起来能写一大堆,而且与Unix系统很多东西相关,就算我有兴趣写估计大家也没兴趣看,所以这里我们从略,只拣关键地方说一下,关于`config.m4`更多细节可以参考这里。 ` ` 打开生成的config.m4文件,内容大致如下: ``` dnl If your extension references something external, use with: dnl PHP_ARG_WITH(say_hello, for say_hello support, dnl Make sure that the comment is aligned: dnl [ --with-say_hello Include say_hello support]) dnl Otherwise use enable: dnl PHP_ARG_ENABLE(say_hello, whether to enable say_hello support, dnl Make sure that the comment is aligned: dnl [ --enable-say_hello Enable say_hello support]) ``` 我想大家也都能看明白,意思就是“如果你的扩展引用了外部组件,使用…,否则使用…”。我们的`say_hello`扩展并没有引用外部组件,所以将“Otherwise use enable”下面三行的“dnl”去掉,改为: ``` dnl Otherwise use enable: PHP_ARG_ENABLE(say_hello, whether to enable say_hello support, Make sure that the comment is aligned: [ --enable-say_hello Enable say_hello support]) ``` 保存,这样关于`Build System`配置就大功告成了。 ` ` 以上可以看成是为开发PHP扩展而做的准备工作,下面就要编写核心代码了。上文说过,编写PHP扩展是基于Zend API和一些宏的,所以如果要编写核心代码,我们首先要弄清楚`PHP Extension`的结构。因为一个PHP Extension在C语言层面实际上就是一个`zend_module_entry`结构体,这点可以从“php_say_hello.h”中得到证实。打开“php_say_hello.h”,会看到里面有怎么一行: ``` extern zend_module_entry say_hello_module_entry; ``` `say_hello_module_entry`就是say_hello扩展的C语言对应元素,而关于其类型zend_module_entry的定义可以在PHP源代码的“Zend/zend_modules.h”文件里找到。 ` ` 写到这里,我们的任务就明了了: ``` 第一,如果需要在相应时机处理一些东西,那么需要填充各个拦截函数内容; 第二,编写say_hello的功能函数,并将引用添加到say_hello_functions中。 ``` 因为say_hello扩展在各个生命周期阶段并不需要做操作,所以我们只编写info_func的内容,上文说过,这个函数将在phpinfo()执行时被自动调用,用于显示扩展的信息。编写这个函数会用到四个函数: ``` php_info_print_table_start()——开始phpinfo表格。无参数。 php_info_print_table_header()——输出表格头。第一个参数是整形,指明头的列数,然后后面的参数是与列数等量的(char*)类型参数用于指定显示的文字。 php_info_print_table_row()——输出表格内容。第一个参数是整形,指明这一行的列数,然后后面的参数是与列数等量的(char*)类型参数用于指定显示的文字。 php_info_print_table_end()——结束phpinfo表格。无参数。 ``` 下面是“say_hello.c”中需要编写的info_func的具体代码: ``` PHP_MINFO_FUNCTION(say_hello) { php_info_print_table_start(); php_info_print_table_header(2, "say_hello support", "enabled"); php_info_print_table_row(2, "author", "Zhang Yang"); /* Replace with your name */ php_info_print_table_end(); /* Remove comments if you have entries in php.ini DISPLAY_INI_ENTRIES(); */ } ``` #### 编写核心函数 编写核心函数,总共分为三步:1、使用宏PHP_FUNCTION定义函数体;2、使用宏ZEND_BEGIN_ARG_INFO和ZEND_END_ARG_INFO定义参数信息;3、使用宏PHP_FE将函数加入到say_hello_functions中。下面分步说明。 ``` PHP_FUNCTION(say_hello_func) { char *name; int name_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { return; } php_printf("Hello %s!", name); RETURN_TRUE; } ``` 上文说过,编写PHP扩展时几乎所有东西都不能裸写,而是必须使用相应的宏。从上面代码可以清楚看到这一点。总体来说,核心函数代码一般由如下几部分构成: `定义函数`,这一步通过宏PHP_FUNCTION实现,函数的外部名称就是宏后面括号里面的名称。 `声明并定义局部变量`。 `解析参数`,这一步通过zend_parse_parameters函数实现,这个函数的作用是从函数用户的输入栈中读取数据,然后转换成相应的函数参数填入变量以供后面核心功能代码使用。zend_parse_parameters的第一个参数是用户传入参数的个数,可以由宏“ZEND_NUM_ARGS() TSRMLS_CC”生成;第二个参数是一个字符串,其中每个字母代表一个变量类型,我们只有一个字符串型变量,所以第二个参数是“s”;最后各个参数需要一些必要的局部变量指针用于存储数据,下表给出了不同变量类型的字母代表及其所需要的局部变量指针。 `参数解析`完成后就是核心功能代码,我们这里只是输出一行字符,php_printf是Zend版本的printf。 最后的返回值也是通过宏实现的。RETURN_TRUE宏是返回布尔值“true”。 使用宏ZEND_BEGIN_ARG_INFO和ZEND_END_ARG_INFO定义参数信息: ``` ZEND_BEGIN_ARG_INFO(arginfo_say_hello_func, 0) ZEND_END_ARG_INFO() ``` 使用宏`PHP_FE`将函数加入到say_hello_functions中 最后,我们需要将刚才定义的函数和参数信息加入到`say_hello_functions`数组里,代码如下: ``` const zend_function_entry say_hello_functions[] = { PHP_FE(say_hello_func, arginfo_say_hello_func) {NULL, NULL, NULL} }; ``` 这一步就是通过`PHP_EF`宏实现,注意这个数组最后一行必须是{NULL, NULL, NULL} ,请不要删除。 #### 编译并安装扩展 在say_hello目录下输入下面命令: ``` $ /usr/bin/phpize $ ./configure $ make $ make install ``` 这样就完成了say_hello扩展的安装(如果没有报错的话)。 这时如果你去放置php扩展的目录下,会发现多了一个say_hello.so的文件。 下面就是将其加入到php.ini配置中,然后重启Apache(如果需要的话)。这些都是PHP基本配置的内容。 #### 扩展测试 这说明扩展已经安装成功了。然后我们编写一个测试用PHP脚本: ``` <?php say_hello_func('Zhang Yang'); ?>; ``` [原文链接](http://www.php.cn/php-weizijiaocheng-392678.html)