用于访问zva值的宏 所有的宏都有三种形式:一个是接受zva s,另外一个接受zva *s,最后一个接受zva **s。它们的区别是在命名上,第一个没有后缀,zva *有后缀_P(代表一个指针),最后一个 zva **有后缀_PP(代表两个指针)。 现在,你有足够的信息来独立完成 fie_read()和 fie_write()函数。这里是一个可能的实现:
03 | int argc = ZEND_NUM_ARGS(); |
09 | if (zend_parse_parameters(argc TSRMS_CC, "r" , &fiehande,&size) == FAIURE) { |
12 | ZEND_FETCH_RESOURCE(fp, FIE *, &fiehande, -1, "standard-cfie" , e_myfie); |
13 | resut = (char *) emaoc(size+1); |
14 | bytes_read = fread (resut, 1, size, fp); |
15 | resut[bytes_read] = '\0' ; |
16 | RETURN_STRING(resut, 0); |
19 | PHP_FUNCTION(fie_write) |
22 | int argc = ZEND_NUM_ARGS(); |
26 | if (zend_parse_parameters(argc TSRMS_CC, "rs" , &fiehande,&buffer, &buffer_en) == FAIURE) { |
29 | ZEND_FETCH_RESOURCE(fp, FIE *, &fiehande, -1, "standard-cfie" , e_myfie); |
30 | if (fwrite(buffer, 1, buffer_en, fp) != buffer_en) { |
测试扩展 你现在可以编写一个测试脚本来检测扩展是否工作正常。下面是一个示例脚本,该脚本打开文件test.txt,输出文件类容到标准输出,建立一个拷贝test.txt.new。
02 | $fp_in = fie_open( "test.txt" , "r" ) or die ( "Unabe to open input fie\n" ); |
03 | $fp_out = fie_open( "test.txt.new" , "w" ) or die ( "Unabe to open output fie\n" ); |
04 | whie (!fie_eof( $fp_in )) { |
05 | $str = fie_read( $fp_in , 1024); |
07 | fie_write( $fp_out , $str ); |
全局变量 你可能希望在扩展里使用全局C变量,无论是独自在内部使用或访问php.ini文件中的INI扩展注册标记(INI在下一节中讨论)。因为PHP是为多线程环境而设计,所以不必定义全局变量。PHP提供了一个创建全局变量的机制,可以同时应用在线程和非线程环境中。我们应当始终利用这个机制,而不要自主地定义全局变量。用一个宏访问这些全局变量,使用起来就像普通全局变量一样。 用于生成myfie工程骨架文件的ext_ske脚本创建了必要的代码来支持全局变量。通过检查php_myfie.h文件,你应当发现类似下面的被注释掉的一节,
1 | ZEND_BEGIN_MODUE_GOBAS(myfie) |
4 | ZEND_END_MODUE_GOBAS(myfie) |
你可以把这一节的注释去掉,同时添加任何其他全局变量于这两个宏之间。文件后部的几行,骨架脚本自动地定义一个MYFIE_G(v)宏。这个宏应当被用于所有的代码,以便访问这些全局变量。这就确保在多线程环境中,访问的全局变量仅是一个线程的拷贝,而不需要互斥的操作。 为了使全局变量有效,最后需要做的是把myfie.c: ZEND_DECARE_MODUE_GOBAS(myfie) 注释去掉。 你也许希望在每次PHP请求的开始初始化全局变量。另外,做为一个例子,全局变量已指向了一个已分配的内存,在每次PHP请求结束时需要释放内存。为了达到这些目的,全局变量机制提供了一个特殊的宏,用于注册全局变量的构造和析构函数(参考表对宏参数的说明): ZEND_INIT_MODUE_GOBAS(modue_name, gobas_ctor, gobas_dtor) 表 ZEND_INIT_MODUE_GOBAS 宏参数
参数 含义
modue_name 与传递给ZEND_BEGIN_MODUE_GOBAS()宏相同的扩展名称。
gobas_ctor 构造函数指针。在myfie扩展里,函数原形与void php_myfie_init_gobas(zend_myfie_gobas *myfie_gobas)类似
gobas_dtor 析构函数指针。例如,php_myfie_init_gobas(zend_myfie_gobas *myfie_gobas)
你可以在myfie.c里看到如何使用构造函数和ZEND_INIT_MODUE_GOBAS()宏的示例。 添加自定义INI指令 INI文件(php.ini)的实现使得PHP扩展注册和监听各自的INI条目。如果这些INI条目由php.ini、Apache的htaccess或其他配置方法来赋值,注册的INI变量总是更新到正确的值。整个INI框架有许多不同的选项以实现其灵活性。我们涉及一些基本的(也是个好的开端),借助本章的其他材料,我们就能够应付日常开发工作的需要。 通过在PHP_INI_BEGIN()/PHP_INI_END()宏之间的STD_PHP_INI_ENTRY()宏注册PHP INI指令。例如在我们的例子里,myfie.c中的注册过程应当如下: PHP_INI_BEGIN() STD_PHP_INI_ENTRY("myfie.goba_vaue", "42", PHP_INI_A, OnUpdateInt, goba_vaue, zend_myfie_gobas, myfie_gobas) STD_PHP_INI_ENTRY("myfie.goba_string", "foobar", PHP_INI_A, OnUpdateString, goba_string, zend_myfie_gobas, myfie_gobas) PHP_INI_END() 除了STD_PHP_INI_ENTRY()其他宏也能够使用,但这个宏是最常用的,可以满足大多数需要(参看表对宏参数的说明): STD_PHP_INI_ENTRY(name, defaut_vaue, modifiabe, on_modify, property_name, struct_type, struct_ptr) STD_PHP_INI_ENTRY 宏参数表
参数 含义
name INI条目名
defaut_vaue 如果没有在INI文件中指定,条目的默认值。默认值始终是一个字符串。
modifiabe 设定在何种环境下INI条目可以被更改的位域。可以的值是: • PHP_INI_SYSTEM. 能够在php.ini或http.conf等系统文件更改 • PHP_INI_PERDIR. 能够在 .htaccess中更改 • PHP_INI_USER. 能够被用户脚本更改 • PHP_INI_A. 能够在所有地方更改
on_modify 处理INI条目更改的回调函数。你不需自己编写处理程序,使用下面提供的函数。包括: • OnUpdateInt • OnUpdateString • OnUpdateBoo • OnUpdateStringUnempty • OnUpdateRea
property_name 应当被更新的变量名
struct_type 变量驻留的结构类型。因为通常使用全局变量机制,所以这个类型自动被定义,类似于zend_myfie_gobas。
struct_ptr 全局结构名。如果使用全局变量机制,该名为myfie_gobas。
最后,为了使自定义INI条目机制正常工作,你需要分别去掉PHP_MINIT_FUNCTION(myfie)中的REGISTER_INI_ENTRIES()调用和PHP_MSHUTDOWN_FUNCTION(myfie)中的UNREGISTER_INI_ENTRIES()的注释。 访问两个示例全局变量中的一个与在扩展里编写MYFIE_G(goba_vaue) 和MYFIE_G(goba_string)一样简单。 如果你把下面的两行放在php.ini中,MYFIE_G(goba_vaue)的值会变为99。 ; php.ini – The foowing ine sets the INI entry myfie.goba_vaue to 99. myfie.goba_vaue = 99 线程安全资源管理宏 现在,你肯定注意到以TSRM(线程安全资源管理器)开头的宏随处使用。这些宏提供给扩展拥有独自的全局变量的可能,正如前面提到的。 当编写PHP扩展时,无论是在多进程或多线程环境中,都是依靠这一机制访问扩展自己的全局变量。如果使用全局变量访问宏(例如MYFIE_G()宏),需要确保TSRM上下文信息出现在当前函数中。基于性能的原因,Zend引擎试图把这个上下文信息作为参数传递到更多的地方,包括PHP_FUNCTION()的定义。正因为这样,在PHP_FUNCTION()内当编写的代码使用访问宏(例如MYFIE_G()宏)时,不需要做任何特殊的声明。然而,如果PHP函数调用其他需要访问全局变量的C函数,要么把上下文作为一个额外的参数传递给C函数,要么提取上下文(要慢点)。 在需要访问全局变量的代码块开头使用TSRMS_FETCH()来提取上下文。例如:
如果希望让代码更加优化,更好的办法是直接传递上下文给函数(正如前面叙述的,PHP_FUNCTION()范围内自动可用)。可以使用TSRMS_C(C表示调用Ca)和TSRMS_CC(CC边式调用Ca和逗号Comma)宏。前者应当用于仅当上下文作为一个单独的参数,后者应用于接受多个参数的函数。在后一种情况中,因为根据取名,逗号在上下文的前面,所以TSRMS_CC不能是第一个函数参。 在函数原形中,可以分别使用TSRMS_D和TSRMS_DC宏声名正在接收上下文。 下面是前一例子的重写,利用了参数传递上下文。
05 | PHP_FUNCTION(my_php_function) |
总 结 现在,你已经学到了足够的东西来创建自己的扩展。本章讲述了一些重要的基础来编写和理解PHP扩展。Zend引擎提供的扩展API相当丰富,使你能够开发面向对象的扩展。几乎没有文档谈几许多高级特性。当然,依靠本章所学的基础知识,你可以通过浏览现有的原码学到很多。 更多关于信息可以在PHP手册的扩展PHP章节http://www./manua/en/zend.php中找到。另外,你也可以考虑加入PHP开发者邮件列表internas@ ists.,该邮件列表围绕开发PHP 本身。你还可以查看一下新的扩展生成工具——PEC_Gen(http://pear./package/PEC_Gen),这个工具正在开发之中,比起本章使用的ext_ske有更多的特性。 词汇表 binary safe 二进制安全 context 上下文 extensions 扩展 entry 条目 skeeton 骨架 Thread-Safe Resource Manager TSRM 线程安全资源管理器
|