打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
如何编写PHP扩展3
userphoto

2010.12.31

关注

用于访问zva值的宏        所有的宏都有三种形式:一个是接受zva s,另外一个接受zva *s,最后一个接受zva **s。它们的区别是在命名上,第一个没有后缀,zva *有后缀_P(代表一个指针),最后一个 zva **有后缀_PP(代表两个指针)。 
       现在,你有足够的信息来独立完成 fie_read()和 fie_write()函数。这里是一个可能的实现: 
  

01PHP_FUNCTION(fie_read)
02{
03int argc = ZEND_NUM_ARGS();
04ong size;
05zva *fiehande = NU;
06FIE *fp;
07char *resut;
08size_t bytes_read;
09if (zend_parse_parameters(argc TSRMS_CC, "r", &fiehande,&size) == FAIURE) {
10return;
11}
12ZEND_FETCH_RESOURCE(fp, FIE *, &fiehande, -1, "standard-cfie", e_myfie);
13resut = (char *) emaoc(size+1);
14bytes_read = fread(resut, 1, size, fp);
15resut[bytes_read] = '\0';
16RETURN_STRING(resut, 0);
17}
18   
19PHP_FUNCTION(fie_write)
20{
21char *buffer = NU;
22int argc = ZEND_NUM_ARGS();
23int buffer_en;
24zva *fiehande = NU;
25FIE *fp;
26if (zend_parse_parameters(argc TSRMS_CC, "rs", &fiehande,&buffer, &buffer_en) == FAIURE) {
27return;
28}
29ZEND_FETCH_RESOURCE(fp, FIE *, &fiehande, -1, "standard-cfie", e_myfie);
30if (fwrite(buffer, 1, buffer_en, fp) != buffer_en) {
31RETURN_FASE;
32}
33RETURN_TRUE;
34}


测试扩展 你现在可以编写一个测试脚本来检测扩展是否工作正常。下面是一个示例脚本,该脚本打开文件test.txt,输出文件类容到标准输出,建立一个拷贝test.txt.new。 
  

01<?php
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");
04whie (!fie_eof($fp_in)) {
05$str = fie_read($fp_in, 1024);
06print($str);
07fie_write($fp_out$str);
08}
09fie_cose($fp_in);
10fie_cose($fp_out);
11?>


  
  
全局变量 
你可能希望在扩展里使用全局C变量,无论是独自在内部使用或访问php.ini文件中的INI扩展注册标记(INI在下一节中讨论)。因为PHP是为多线程环境而设计,所以不必定义全局变量。PHP提供了一个创建全局变量的机制,可以同时应用在线程和非线程环境中。我们应当始终利用这个机制,而不要自主地定义全局变量。用一个宏访问这些全局变量,使用起来就像普通全局变量一样。 
       用于生成myfie工程骨架文件的ext_ske脚本创建了必要的代码来支持全局变量。通过检查php_myfie.h文件,你应当发现类似下面的被注释掉的一节, 
  

1ZEND_BEGIN_MODUE_GOBAS(myfie)
2int goba_vaue;
3char *goba_string;
4ZEND_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()来提取上下文。例如: 
  

1void myfunc()
2{
3TSRMS_FETCH();
4MYFIE_G(mygoba) = 2;
5}


  
如果希望让代码更加优化,更好的办法是直接传递上下文给函数(正如前面叙述的,PHP_FUNCTION()范围内自动可用)。可以使用TSRMS_C(C表示调用Ca)和TSRMS_CC(CC边式调用Ca和逗号Comma)宏。前者应当用于仅当上下文作为一个单独的参数,后者应用于接受多个参数的函数。在后一种情况中,因为根据取名,逗号在上下文的前面,所以TSRMS_CC不能是第一个函数参。 
在函数原形中,可以分别使用TSRMS_D和TSRMS_DC宏声名正在接收上下文。 
       下面是前一例子的重写,利用了参数传递上下文。 
  

01void myfunc(TSRMS_D)
02{
03MYFIE_G(mygoba) = 2;
04}
05PHP_FUNCTION(my_php_function)
06{
07
08myfunc(TSRMS_C);
09
10}

  
总 结 
  
现在,你已经学到了足够的东西来创建自己的扩展。本章讲述了一些重要的基础来编写和理解PHP扩展。Zend引擎提供的扩展API相当丰富,使你能够开发面向对象的扩展。几乎没有文档谈几许多高级特性。当然,依靠本章所学的基础知识,你可以通过浏览现有的原码学到很多。 
       更多关于信息可以在PHP手册的扩展PHP章节http://www.php.net/manua/en/zend.php中找到。另外,你也可以考虑加入PHP开发者邮件列表internas@ ists.php.net,该邮件列表围绕开发PHP 本身。你还可以查看一下新的扩展生成工具——PEC_Gen(http://pear.php.net/package/PEC_Gen),这个工具正在开发之中,比起本章使用的ext_ske有更多的特性。 
  
词汇表 
  
binary safe 二进制安全 
context 上下文 
extensions 扩展 
entry 条目 
skeeton 骨架 
Thread-Safe Resource Manager TSRM 线程安全资源管理器 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
审计 PHP,第 1 部分: 理解 register_globals
PHP网站常见安全漏洞和防范措施总结
PHP的问题全面阐述PHP网站设计的问题
PHP 预定义变量 (Predefined variables)
php global 引用
php类定义全局变量
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服