PHP7扩展开发教程[1] – 怎样导出一个模块?

PHP-X项目启发,我决定在未来的一段时间编写一系列php7扩展开发教程,一方面是沉淀最近一段时间的php7扩展开发知识,另外也可以将学习成果贡献给更多需要参与到php7扩展开发中的有志之士们。

在编写该系列博客时,我力求每个章节的功能点高度聚焦,代码保持短小,将需要了解的基础知识点介绍给大家,同时也为大家自行探索更多丰富的php api提供必要的线索,”授人以渔”是该系列博客的初衷。

每一个章节的代码将维护在github项目:php7-extension-explore,后续会随着教程的逐步编写不断丰富,最后会考虑将博客的内容整理到gitbook,但暂时仍旧在wordpress中撰写与提供查阅。

  • 本博客假设你熟悉c和php开发,因此将不对语言细节、搭建方法做深入的讲解。
  • 尽量避免使用Zend的宏定义,展示原生API和数据结构,方便大家深入理解。
  • 只运行在linux+nginx+php-fpm环境下,线程安全之类的问题完全不涉及。

正式开始

本章节会搭建第一个php7扩展叫做myext,最终的目标是编译出一个扩展myext.so,并令php加载扩展,同时可以在phpinfo()中看到扩展的一些说明信息。

源代码请打开:https://github.com/owenliang/php7-extension-explore/tree/master/course1-how-to-export-a-module

myext.h

头文件myext.h中引入了php.h,它包含了我们常用的php扩展API定义。另外一个头文件是ext/standard/info.h,ext是php的扩展目录,standard是扩展的名字,我们的扩展会用到这个扩展里的方法。

TRACE宏定义是为了调试扩展临时定义的日志函数,它会打印代码位置和信息到屏幕,便于我们调试追踪。

myext.c

扩展编译产生so,php/php-fpm程序会通过dlopen加载我们的myext.so扩展,然后通过dlsym找到get_module这个符号并调用它得到我们的扩展定义信息。

zend_module_entry定义了扩展的各种信息,可以在php源码的Zend/zend_modules.h中找到:

字段好多,但是填充它的代码要短的多:

这个宏填充了结构体前面那些并不重要的字段,直到ini_entry字段为之:

这个宏填充了结构体后面那些并不重要的字段:

至于这2个宏填充了哪些字段都已经注释说明,目前还没有具体使用过这些字段。

在目前使用到的字段中,name字段是模块的名称,version是模块的版本,其他字段后面的章节再慢慢涉及。

本章要重点关注的是extension_系列函数,它们涉及到扩展的生命期概念。

module_startup_function和module_shutdown_function分别在扩展加载和销毁时回调,对CGI/FPM都是一样的,都是进程启动调用一次,进程退出调用一次。

request_startup_function和request_shutdown_function分别在请求处理前和处理后回调,对于CGI来说就是启动和退出的2个时机,对于FPM来说是一个fastcgi请求处理前和处理后2个时机。

以extension_startup函数为例,它的参数是type和module_number,其他3个函数也是一样的。

module_number是运行时分配的一个扩展唯一ID,后面章节会用得到。type表示扩展是持久的还是临时的,我们通过php.ini配置加载的扩展都是持久的,而在PHP代码里通过dl函数加载的叫做临时的,所以type对我们一般没有用处。

再看一下无关痛痒的extensin_info函数:

这个函数可以在php的phpinfo()中输出扩展的一些html描述信息,php_info_print_….这些api来自于ext/standard扩展,而这个扩展是php默认会安装的,你就不要担心这些函数符号找不到了。

这些函数的实现你可以在ext/standard/info.c中找到,这些函数符号存在于php的二进制中,myext扩展在被php加载时可以自动在php中找到符号并完成调用,你大可放心。

makefile

理解makefile非常重要,我们利用php-config程序获得php的头文件和库文件的所在位置,在编译扩展myext.so的时候包含进来即可:

你可以运行php-config系列命令,看看这些变量具体的内容。

最终我们将myext.c编译成myext.so,通过make && make install便会自动安装到PHP_EXTENSION_DIR目录下了。

为了加载so,我们根据php –ini找到php.ini的所在位置,修改它将myext.so扩展配置进去:

然后执行php -m | grep myext确认扩展已成功加载:

正常情况下会看到这些信息,因为php -m 属于CLI执行,因此它会经历完整的php生命期,程序里打印的TRACE日志都得以输出。

你也可以通过make test来重复测试,它相当于执行php test.php。

结语

通过本章,掌握:

  1. 导出扩展
  2. 编译扩展
  3. 加载扩展

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~