一、前言
在我的上一篇文章“实战:用C写php扩展(一)”里介绍了一个最简单的php扩展myExt的创建过程。下面我们来研究一下这个扩展的源码的主要结构。
首先来了解一下PHP的三种建立功能模块的方法:建立一个外部模块;建立一个内建模块;Zend Engine扩充。
外部模块能在脚本运行时使用函数dl( )进行装载。dl函数从磁盘上装载一个模块,当脚本进行到这个模块部分时,就可获得相应的功能。当这个脚本程序结束,外部模块就从内存中清除。
内建模块是直接编译进PHP中的,并且作用于每个PHP进程;每个运行的脚本都可以直接调用她们的功能。当开发的是可以保持相对独立的、有固定功能的模块,我们需要她有更好的执行性能,或者这个模块在站点上被许多脚本频繁使用,那么内建模块是最好的选择。重新编译的代价很快会被PHP获得更快、更易使用的好处所补偿。然而,如果需要的只是一个功能不大的模块,内建模块不是理想的选择。
如果需要改变语言的解释程序或者需要在内核中直接集成特殊的功能,修改Zend Engine是首选。但是,一般来说,应该避免修改Zend 引擎。改变Zend 引擎会导致同现行的PHP不兼容,任何人都很难适应对Zend引擎改变所带来的变化。这种修改不能同PHP主力开发队伍分开,而且,随着PHP下一次“官方”升级,这种修改会被忽略掉。因此,这一扩充方法很少实际使用。
二、实战
一个php扩展的源码主要由以下几部分组成
1)头文件(包含了所有必须的宏定义,API定义等。)
2)C格式的模块函数外部声明
3)Zend 函数区声明
4)Zend 模块区声明
5)使用get_module()
6)模块函数功能的具体实现
我们先来看一下php_myExt.h头文件,上述1),2)
接下来,我们来看看myExt.c源文件,上述3),4),5),6)
接下来是Zend 模块的相关信息,保存在一个名为zend_module_entry 的结构中。
字段 | 说明 |
size, zend_api, zend_debug and zts | 通常用 "STANDARD_MODULE_HEADER" 来填充,它指定了模块的四个成员:标识整个模块结构大小的 size ,值为 ZEND_MODULE_API_NO 常量的 zend_api,标识是否为调试版本(使用 ZEND_DEBUG 进行编译)的 zend_debug,还有一个用来标识是否启用了 ZTS (Zend 线程安全,使用 ZTS 或USING_ZTS 进行编译)的 zts。 |
name | 模块名称 (像“File functions”、“Socket functions”、“Crypt”等等). 这个名字就是使用 phpinfo() 函数后在“Additional Modules”部分所显示的名称。 |
functions | Zend 函数块的指针, 这个我们在前面已经讨论过。 |
module_startup_func | 模块启动函数。这个函数仅在模块初始化时被调用,通常用于一些与整个模块相关初始化的工作(比如申请初始化的内存等等)。如果想表明模块函数调用失败或请求初始化失败请返回 FAILURE,否则请返回 SUCCESS。可以通过宏 ZEND_MINIT 来声明一个模块启动函数。如果不想使用,请将其设定为 NULL。 |
module_shutdown_func | 模块关闭函数。这个函数仅在模块卸载时被调用,通常用于一些与模块相关的反初始化的工作(比如释放已申请的内存等等)。这个函数和 module_startup_func() 相对应。如果想表明函数调用失败或请求初始化失败请返回 FAILURE,否则请返回 SUCCESS。可以通过宏ZEND_MSHUTDOWN 来声明一个模块关闭函数。如果不想使用,请将其设定为 NULL。 |
request_startup_func | 请求启动函数。这个函数在每次有页面的请求时被调用,通常用于与该请求相关的的初始化工作。如果想表明函数调用失败或请求初始化失败请返回 FAILURE,否则请返回 SUCCESS。注意: 如果该模块是在一个页面请求中被动态加载的,那么这个模块的请求启动函数将晚于模块启动函数的调用(其实这两个初始化事件是同时发生的)。可以使用宏 ZEND_RINIT 来声明一个请求启动函数,若不想使用,请将其设定为 NULL。 |
request_shutdown_func | 请求关闭函数。这个函数在每次页面请求处理完毕后被调用,正好与 request_startup_func() 相对应。如果想表明函数调用失败或请求初始化失败请返回 FAILURE,否则请返回 SUCCESS。注意: 当在页面请求作为动态模块加载时, 这个请求关闭函数先于模块关闭函数的调用(其实这两个反初始化事件是同时发生的)。可以使用宏 ZEND_RSHUTDOWN 来声明这个函数,若不想使用,请将其设定为 NULL 。 |
info_func | 模块信息函数。当脚本调用 phpinfo() 函数时,Zend 便会遍历所有已加载的模块,并调用它们的这个函数。每个模块都有机会输出自己的信息。通常情况下这个函数被用来显示一些环境变量或静态信息。可以使用宏 ZEND_MINFO 来声明这个函数,若不想使用,请将其设定为 NULL 。 |
version | 模块的版本号。如果你暂时还不想给某块设置一个版本号的话,你可以将其设定为 NO_VERSION_YET。但我们还是推荐您在此添加一个字符串作为其版本号。版本号通常是类似这样: "2.5-dev", "2.5RC1", "2.5" 或者 "2.5pl3" 等等。 |
Remaining structure elements | 这些字段通常是在模块内部使用的,通常使用宏STANDARD_MODULE_PROPERTIES 来填充。而且你也不应该将他们设定别的值。STANDARD_MODULE_PROPERTIES_EX 通常只会在你使用了全局启动函数(ZEND_GINIT)和全局关闭函数(ZEND_GSHUTDOWN)时才用到,一般情况请直接使用 STANDARD_MODULE_PROPERTIES 。 |