`

实战:用C写php扩展(二)

阅读更多
一、前言
在我的上一篇文章“实战:用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)
[c-sharp] view plaincopyprint?
/* 1)必须包含的头文件 */  
#include "php.h"  
...... 
 
PHP_MINIT_FUNCTION(myExt); 
PHP_MSHUTDOWN_FUNCTION(myExt); 
PHP_RINIT_FUNCTION(myExt); 
PHP_RSHUTDOWN_FUNCTION(myExt); 
PHP_MINFO_FUNCTION(myExt); 
 
/* 2)C格式的模块函数外部声明 */ 
PHP_FUNCTION(confirm_myExt_compiled);   /* 声明导出函数 confirm_myExt_compiled,该函数可以在php代码中使用 */ 
 
...... 
 
Zend 提供了一个宏PHP_FUNCTION(my_function),用来声明需要导出的导出函数(也就是让其成为可以被 PHP 脚本直接调用的原生函数)。 
confirm_myExt_compiled函数是自动生成的导出函数,主要是为了测试我们写的php扩展是否好使。当我们写完自己的php扩展后,可以删除该函数。 
我们可以增加如下一行来声明我们自己的导出函数myfunc: 
PHP_FUNCTION(myfunc); 


接下来,我们来看看myExt.c源文件,上述3),4),5),6)
[c-sharp] view plaincopyprint?
/** 
* 3)Zend 函数区声明,让Zend引擎知道此模块中有什么函数
* Every user visible function must have an entry in myExt_functions[].
* 需要将我们定义的需要导出的函数在此声明,从而将其引入 Zend。可以调用zend提供的PHP_FE(name, arg_types)宏来声明,arg_types参数一般被设为NULL
*/ 
const zend_function_entry myExt_functions[] = { 
    PHP_FE(confirm_myExt_compiled,  NULL)       /* For testing, remove later. 正式发布我们的代码时,需要删除 */ 
    {NULL, NULL, NULL}  /* Must be the last line in myExt_functions[] */ 
}; 
 
我们需要在myExt_functions[]中添加我们的myfunc函数的引入声明 
const zend_function_entry myExt_functions[] = { 
    PHP_FE(confirm_myExt_compiled,  NULL)       /* For testing, remove later. 正式发布我们的代码时,需要删除 */ 
    PHP_FE(myfunc, NULL) 
    {NULL, NULL, NULL}  /* Must be the last line in myExt_functions[] */ 
}; 
这个结构的最后一项是 {NULL, NULL, NULL} 。事实上,这个结构的最后一项也必须始终是 {NULL, NULL, NULL} ,因为 Zend Engine 需要靠它来确认这些导出函数的列表是否列举完毕。注意: 你不应该使用一个预定义的宏来代替列表的结尾部分(即{NULL, NULL, NULL}),因为编译器会尽量寻找一个名为 “NULL” 的函数的指针来代替 NULL ! 


接下来是Zend 模块的相关信息,保存在一个名为zend_module_entry 的结构中。
[c-sharp] view plaincopyprint?
/* 4)Zend 模块区声明  */ 
zend_module_entry myExt_module_entry = { 
#if ZEND_MODULE_API_NO >= 20010901 
    STANDARD_MODULE_HEADER, 
#endif 
    "myExt",                // 模块名称 
    myExt_functions,        // Zend 函数块的指针,就是上面我们讨论的数组myExt_functions[] 
    PHP_MINIT(myExt),       // 声明一个模块的启动函数 
    PHP_MSHUTDOWN(myExt),   // 声明一个模块的关闭函数 
    PHP_RINIT(myExt),       /* 声明一个请求的启动函数。Replace with NULL if there's nothing to do at request start */ 
    PHP_RSHUTDOWN(myExt),   /* 声明一个请求的关闭函数。Replace with NULL if there's nothing to do at request end */ 
    PHP_MINFO(myExt),       // 声明一个输出模块信息的函数,用于phpinfo()。 
#if ZEND_MODULE_API_NO >= 20010901 
    "0.1", /* Replace with version number for your extension */ 
#endif 
    STANDARD_MODULE_PROPERTIES 
}; 
 
可以在php源码 Zend/zend_modules.h中找到结构体zend_module_entry的定义。 
struct _zend_module_entry { 
    unsigned short size; 
    unsigned int zend_api; 
    unsigned char zend_debug; 
    unsigned char zts; 
    const struct _zend_ini_entry *ini_entry; 
    const struct _zend_module_dep *deps; 
    const char *name; 
    const struct _zend_function_entry *functions; 
    int (*module_startup_func)(INIT_FUNC_ARGS); 
    int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); 
    int (*request_startup_func)(INIT_FUNC_ARGS); 
    int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); 
    void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); 
    const char *version; 
    ......  // 其余的一些我们不感兴趣的信息 
}; 

字段
说明
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 。
[c-sharp] view plaincopyprint?
/* 5) get_module()函数 */ 
这个函数只用于动态可加载模块(*.so文件),而不能用于内建模块。通过宏ZEND_GET_MODULE 来创建 
#ifdef COMPILE_DL_MYEXT 
ZEND_GET_MODULE(myExt) 
#endif 
 
我们可以在php源码 Zend/zend_API.h中找到ZEND_GET_MODULE的宏定义 
#define ZEND_GET_MODULE(name) / 
    BEGIN_EXTERN_C()/ 
    ZEND_DLEXPORT zend_module_entry *get_module(void) { return &name##_module_entry; }/ 
    END_EXTERN_C() 
     
还记得我们在“实战:用C写php扩展(一)”里是调用phpize生成configure文件吗,生成的configure文件里定义了COMPILE_DL_MYEXT常量 
#define COMPILE_DL_MYEXT 1 
 
get_module() 函数在模块加载时被 Zend 所调用,你也可以认为是被你 PHP 脚本中的 dl() 函数所调用。这个函数的作用就是把模块的信息块传递 Zend 并通知 Zend 获取这个模块的相关内容。 
如果你没有在一个动态可加载模块中实现 get_module() 函数,那么当你在访问它的时候 Zend 就会向你抛出一个错误信息。 


[c-sharp] view plaincopyprint?
/* 6)模块函数功能的具体实现。*/  
导出函数的实现是我们构建扩展的最后一步。在我们的myExt例子中,函数被实现如下: 
 
PHP_FUNCTION(confirm_myExt_compiled) 

    char *arg = NULL; 
    int arg_len, len; 
    char *strg; 
 
    // 首先我们需要检查和接收这个函数的参数 
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { 
        return; 
    } 
 
    // 最后是函数的返回值 
    len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "myExt", arg); 
    RETURN_STRINGL(strg, len, 0); 


http://blog.csdn.net/sunlylorn/article/details/6227160
分享到:
评论

相关推荐

    php扩展学习PDF

    《PHP扩展学习PDF》是一本深入探讨PHP内核机制与扩展开发的专业书籍,旨在帮助读者不仅理解PHP的工作原理,还能具备编写自己PHP扩展的能力。这本书的受众群体是那些已经掌握C语言基础,并对PHP编程有一定了解的...

    PHP经典实例实战视频教程

    - **PHP概述**:PHP(Hypertext Preprocessor)是一种通用开源脚本语言,语法吸收了C语言、Java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域。 - **PHP优势**:易于学习、跨平台支持好、执行效率高、...

    php phalcon项目实战

    在PHP开发领域,Phalcon是一个高性能的框架,它以C语言编写并作为PHP扩展运行,提供了优秀的性能和低级别的资源消耗。本项目实战将深入探讨如何利用Phalcon来构建高效、稳定的企业级Web应用。 【描述】:“使用的...

    PHP开发实战密码.zip

    6. **数据库交互**:MySQL是常见的PHP数据库伴侣,包括PDO(PHP Data Objects)和mysqli扩展的使用,以及SQL语句的编写。 7. **Web框架**:可能介绍如Laravel、Symfony或CodeIgniter等流行的PHP框架,帮助提高开发...

    VSCode开发PHP环境配置手册(PHP8.1.1+PHP-xdebug+redis)

    二、VSCode支持PHP_xdebug配置 1.添加PHP所需的xdebug库 将xdebug库放入ext插件目录 备注:压缩包中的PHP8.1.1默认已经集成了php_xdebug-3.2.1-8.1-vs16-nts-x86_64.dll文件并已重命名为php_xdebug.dll。 2.配置用户...

    php1200:PHP开发实战1200例原始码

    15. **PHP扩展开发**:了解如何编写C语言的PHP扩展。 16. **服务器环境配置**:Apache、Nginx等服务器的PHP配置。 17. **性能优化**:如PHP的 opcode 缓存、内存管理。 18. **单元测试**:使用PHPUnit进行代码...

    php快速入门

    语法吸收了C语言、Java以及Perl的特点,利于学习,使用灵活,支持多种数据库操作。 - **安装配置**: - **Windows环境**:推荐使用XAMPP或WAMP等集成包,这些工具包含了Apache服务器、PHP解释器及MySQL数据库,安装...

    李炎恢php第二季下载

    - **数据库操作**:熟悉MySQL等关系型数据库的基本操作,学会使用PDO或mysqli扩展进行数据库交互。 #### 3. 框架与工具 - **常用框架**:如Laravel、Symfony等,这些框架可以帮助开发者更高效地构建复杂的Web应用。...

    php学习资料

    1. **MySQL连接**:使用mysqli或PDO扩展与MySQL数据库建立连接,掌握SQL查询语句的编写。 2. **CRUD操作**:学习如何使用PHP进行数据库的创建(Create)、读取(Retrieve)、更新(Update)和删除/Delete)操作。 3. **...

    某培训机构PHP基础培训视频

    - **PDO扩展**: 使用PHP的数据对象(PDO)扩展连接数据库,实现数据的增删改查操作。 #### 5. 面向对象编程(OOP) - **类与对象**: 掌握PHP中的类定义、对象实例化等面向对象编程的基本概念。 - **继承与多态**: 学习...

    PHP高级程序设计:模式框架与测试

    - **持续集成**:讲解如何设置持续集成(CI)环境,如Jenkins、Travis CI,实现自动化测试和构建。 - **测试驱动开发(TDD)**:阐述TDD理念,强调先写测试,再编写满足测试的代码,确保代码质量。 4. **最佳实践**:...

    PHP新手教程(初学PHP的教程)

    - **语法相似性**: PHP的语法类似于C语言、Perl等,对于熟悉这些语言的开发者来说更容易学习。 - **数据库兼容性**: 支持多种数据库连接,特别是与MySQL的集成效果出色。 - **可扩展性**: PHP支持面向对象编程,允许...

    传智播客php34期

    - **数据库交互**:使用PDO或mysqli扩展连接数据库,执行SQL语句。 ### 三、实战项目开发 #### 1. 基于PHP的Web应用设计 - **需求分析**:明确项目的目标用户群、功能需求等。 - **架构设计**:采用MVC(Model-...

    Advanced PHP Programming(高级PHP编程)

    - **C扩展**:介绍如何使用C语言编写PHP扩展来增强其功能。 - **Zephir**:探索Zephir这一领域,它是用于开发PHP扩展的一种高级语言。 #### 四、PHP内核解析 1. **内核结构** - **基本组件**:概述PHP内核的主要...

    《PHP网站开发实例教程》源代码.zip

    它的语法吸收了C语言、Java和Perl的特点,利于学习,使用广泛,主要适用于网页制作。PHP可以处理表单数据、生成动态网页内容、管理数据库、创建cookies等,是构建动态网站的利器。 在源代码中,你可能会看到以下...

Global site tag (gtag.js) - Google Analytics