奥门新浦京官方网站深入理解php内核 编写扩展 II:参数、数组和ZVALs

QQ会员活动运行平台(AMS),是QQ会员增值运转工作的关键载体之风度翩翩,负担海量活动运行的Web系统。AMS是多个最主要利用PHP语言达成的活动运维平台,
CGI日呼吁3亿左右,高峰期抵达8亿。但是,在前面相比长的风流倜傥段时间里,大家都施用了比较老旧的底子软件版本,便是PHP5.2+Apache2.0(贰零壹零年的手艺)。特别从2018年启幕,随着AMS业务随着QQ会员增值业务的火速增加,质量压力日益变大。

前言

原文:

奥门新浦京官方网站 1

在这里篇博文中大家将演示怎么样在PHP扩充中创立叁个变量。示例代码如下:

Part II: Parameters,Arrays, and ZVALs

于是,自二〇一四年1月,大家就起初酌量PHP底层进级,最终的目的是升级到PHP7。此时,PHP7尚处在研究开发阶段,而笔者辈谈谈和预备性商讨就已经带头了。

‘b’);$obj =new
demo();var_dump($str);var_dump($arr);var_dump($obj);?>

原文:

风流浪漫、PHP7的上学和预备性钻探 1. HHVM和JIT

二零一五年就PHP品质优化的方案,有此外叁个极度主要的剧中人物,就是由Facebook开源的HHVM(HipHop
Virtual Machine,HHVM是二个推特(TWTR.USState of Qatar(Facebook卡塔尔开源的PHP设想机)。HHVM使用JIT(Just
In
Time,即时编写翻译是种软件优化技艺,指在运作时才会去编写翻译字节码为机器码)的编写翻译形式以致任何技艺,让PHP代码的举行质量大幅升高。据传,能够将PHP5版本的原生PHP代码升高5-10倍的实施品质。

HHVM源点于推特集团,Instagram早起的洋洋代码是运用PHP来开采的,不过,随着专门的学业的极快前行,PHP奉行功效成为特别名扬四海的难题。为了优化实施效用,Twitter(推特卡塔尔(قطر‎在二〇一〇年就开首使用HipHop,那是意气风发种PHP实施引擎,最早是为了将
Fackbook的汪洋PHP代码转成
C++,以压实质量和节约能源。使用HipHop的PHP代码在品质上有数倍的晋升。后来,推文(Tweet卡塔尔(قطر‎将HipHop平台开源,慢慢发展为几近来的
HHVM。

HHVM成为贰个PHP质量优化技术方案时,PHP7还处在研究开发阶段。曾经看过一些同学对于HHVM的沟通,质量能够获取可观的升级,不过服务运转和PHP语法包容有必然资本。有说话,JIT成为一个意见相当的高的东西,非常多本领同学建议PHP7也理应通过JIT来优化品质。

二零一四年4月,作者在场了中华PHPCON,听了惠新宸关于PHP7内核的才具分享。实际上,在二零一三年的时候,惠新宸(PHP7内核开垦者)和Dmitry(另一个人PHP语言内核开辟者之黄金年代)就已经在PHP5.5的版本上做过三个JIT的品味(并不曾宣布)。PHP5.5的原本的施行流程,是将PHP代码通过词法和语法深入分析,编写翻译成opcode字节码(格式和汇编有一点像),然后,Zend引擎读取那几个opcode指令,逐一深入分析试行。

而她们在opcode环节后引进了花色推测(TypeInf),然后通过JIT生成ByteCodes,然后再推行。

奥门新浦京官方网站 2

于是乎,在benchmark(测验程序)中拿走非常好的结果,完毕JIT后品质比PHP5.5进步了8倍。然则,当她们把这几个优化放入到骨子里的项目WordPress(二个开源博客项目)中,却大约看不见品质的进级。原因在于测量试验项指标代码量超少,通过JIT发生的机器码也相当小,而实在的WordPress项目转移的机器码太大,引起CPU缓存命中率下落(CPU
Cache Miss)。

简单来说,JIT实际不是在每种现象下都以冯谖三窟的利器,而脱离业务场景的性质测量检验结果,并不一定具有代表性。

从官方放出Wordpress的PHP7和HHVM的质量比较能够看来,两者基本处于相像档期的顺序。

奥门新浦京官方网站 3

2.PHP7在品质方面包车型客车优化

PHP7是四个相比底层进级,比起PHP5.6的更换比异常的大,而就品质优化层面,差不离能够聚焦如下:

(1)将底子变量从struct(布局体)变为union(联合体),节外省部存款和储蓄器空间,直接减弱CPU在内存分配和治本上的开采。

(2)部分底工变量(zend_array、zend_string等)接受内存空间三番三回分配的艺术,减弱CPU
Cache Miss的发出的票房价值。CPU从CPU
Cache获取数据和从内部存款和储蓄器获取,它们中间成效相差能够高达100倍。举二个近乎的例证,系统从内部存款和储蓄器读取数据和从磁盘读取数据的成效差异相当的大,CPU
Cache Miss相符蒙受缺页中断。

(3)通过宏定义和内联函数(inline),让编写翻译器提前完毕都部队分职业。无需在程序运转时分配内存,能够达成相像函数的效应,却还未函数调用的压栈、弹栈开销,成效会比较高。

… …

越来越多更详尽关于PHP7的介绍,风乐趣的校友可以查看:《 PHP7创新与品质优化 》

3.AMS平台本事选型的背景

就升高PHP的习性来说,能够选用的是二零一五年就可径直行使的HHVM恐怕是2016年初才透露规范版的PHP7。会员AMS是三个做客流量级非常的大的二个Web系统,经过四年持续的升级和优化,积存了800四个事情效率组件,还应该有各样PHP编写的公物功底库和本子,代码规模也异常的大。

咱俩对此PHP版本对代码的向下宽容的须要是相比高的,因而,就我们业务场景来讲,PHP7优良的语法向下包容,正是大家所需求的。由此,大家选择以PHP7为晋级的方案。

个中的三行我们将用PHP扩充来兑现。

Part II: Parameters,Arrays, and ZVALs [continued]

二、PHP7升级面没错风险和挑衅

对此贰个曾经现网在线的大型国有Web服务来讲,底蕴公共软件进级,平日是意气风发件劳而无功的劳作,做得好,不自然被世家感知到,可是,晋级出了难点,则须要负责相当重的权利。为了尽量裁减晋级的高危害,大家必需先弄精通我们的升官存在挑衅微风险。

于是,大家收拾了晋级挑战和高危害列表:

(1)Apache2.0和PHP5.2那七个2009-二零零六年的底子软件版本比较古老,晋级到Apache2.4和PHP7,版本进级跨度一点都相当的大,时间跨度相差7-8年,由此,宽容性难题挑战相比高。实际上,我们集团的现网PHP服务,超多都停留在PHP5.2和PHP5.3的本子,版本偏低。

(2)AMS多量施用自行研制tphplib扩张,tphplib很早在商铺里面就从不人爱戴了,这么些扩充在此之前独有PHP5.3和PHP5.2的编写翻译so版本,而且,部分扩展未有帮衬线程安全。帮衬线程安全,是因为我们原先的Apache使用了prefork情势,而大家希望能够采纳Apache2.4的Event形式(二零一五年中,在prefork和worker之后,推出的多进程线程管理情势,对于辅助高产出,有更了不起的表现)。

(3)语法宽容性难点,从PHP5.2到PHP7的跨迈过大,即便PHP官方称得上在向下包容方面达成99%,可是,大家的代码规模十分大,它仍是二个未知的高风险。

(4)新软件面前境遇的风险,将Apache和PHP这种根底软件进级到最新的本子,而那一个本子的大器晚成对功用可能存在未知的高风险和瑕玷。

局地同班或者会提出使用Nginx会是更优的选料,的确,单纯比较Nginx和Apache在高并发方面包车型客车习性,Nginx的表现更优。不过就PHP的CGI来讲,Nginx+php-ftpm和Apache+mod_php两个并未比较大的距离。另一面,大家因为时期久远接受Apache,在才能熟稔和经历方面储存更加多,因而,它或者不是拔尖的精选,然则,具体到我们工作场景,算是比较适宜的三个筛选。

代码

2编辑扩张 II:参数、数组和ZVALs

介绍
接到数值
ZVAL
创建ZVALs
数组
标识表作为数组
援用计数
. 拷贝 vs 引用
. 核对(代码)完整性
. 下一步是什么?

三、版本进级施行进程 1.高跨度版本晋级格局

从三个2009年的Apache2.0一贯接升学级到二零一六年的Apache2.4,那么些跨渡过于大,以致接收的http.conf的配置文件都有众多的比不上,这里的急需更新的地点超多,未知的高风险也是存在的。于是,大家的做法,是先品尝将Apache2.0升格到Apach2.2,调治陈设、观望稳固性,然后再进一层尝试到Apach2.4。所幸的是,Apache(httpd)是三个相比极度的开源社区,他们前面从来同一时间吝惜那三个分支版本的Apache(2.2和2.4),因而,就算是Apache2.2也可以有相比较新的版本。

奥门新浦京官方网站 4

于是,大家先进级了三个PHP5.2+Apache2.2,对包容性举行了测量检验和注重,确认两个之间是能够比较平缓进级后,大家最初进行Apache2.4的进级方案。

奥门新浦京官方网站 5

PHP5.2的进步,大家也选取相仿的思路,大家先将PHP5.2进级至PHP5.6(这时,PHP7依旧beta版本),然后再将PHP5.6晋级到PHP7,以更平整的办法,稳步解决差别的主题素材。

于是乎,大家的进级布署变为:

奥门新浦京官方网站 6

Apache2.4编写翻译为动态MPM的情势(帮衬通过httpd配置切换prefork/worker/event方式),依据现网风险等实时降级。

奥门新浦京官方网站 7

Prefork、Worker、伊芙nt三者粗略介绍:

(1)prefork,多进度格局,1个经过服务于1个客户诉求,花销比较高。不过,牢固性最高,无需支持线程安全。

(2)worker,多进度三十二线程格局,1个经过含有多少个worker线程,1个worker线程服务于1个客商诉求,因为线程更轻量,开销非常的低。可是,在KeepAlive场景下,worker财富会被client占有,不恐怕响应别的恳求(空等待)。

(3)event,多进度多线程格局,1个进度也包蕴多个worker线程,1个worker线程服务于1个客户诉求。不过,它解决了KeepAlive场景下的worker线程被攻下问题,它经过特意的线程来管理那一个KeepAlive连接,然后再分配“专业”给现实管理的worker,职业worker不会因为KeepAlive而造成空等待。

有关伊芙nt情势的法定介绍:

(部分同学或许会有event方式不扶植https的纪念,那三个说法实际上是2年多在先的本国一些本事博客的说法,近日的本子是协理的,实际情况能够浏览官方介绍)

打开动态切换形式的点子,正是在编写翻译httpd的时候增多:

–enable-mpms-shared=all

奥门新浦京官方网站 8

从PHP5.2进级到PHP5.6绝比较比较容易于,我们根本的做事如下:

(1)清理了朝气蓬勃部分不再动用的老扩大

(2)化解掉线程安全难题

(3)将cmem等api编写翻译到新的版本

(4)PHP代码语法基于PHP5.6的合作(实际上变化十分的小)

(5)部分增加的一同调治。apc扩张变为zend_opcache和apcu,在此此前的apc是带有了编写翻译缓存和客户内存操作的作用,在PHP相比新本子里,被讲授为单身的四个增加。

从PHP5.6晋级到PHP7.0的职业量就比超级多,也相对比较复杂,因而,咱们制订了每二个阶段的晋级布置:

(1)手艺预备性探讨,PHP7晋级盘算。

(2)遭逢编写翻译和搭建,下载相关的编写翻译包,搭建完整的编译意况和测量试验蒙受。(编写翻译景况依然要求相当多的信赖so)

(3)宽容进级和测验。PHP7扩充的重新编写翻译和代码包容性职业,AMS效用验证,质量压测。

(4)线上灰度。打包为pkg的安装包,编写相关的装置shell安装实行代码(满含软链接、息灭一些so信任)。然后,灰度安装到现网,阅览。

(5)正式公布。扩充灰度范围,全量晋级。

奥门新浦京官方网站 9

因为从PHP5.2升级到PHP5.6的经过中,相当多难题风华正茂度被大家提前化解了,所以,PHP7的进级换代注重难点在于tphplib扩充的编写翻译升级。

涉及首要的劳作包罗:

(1)PHP5.6的恢宏到PHP7.0的相比较小幅面改动升高(职业量非常大的地点)

(2)包容apcu的内部存款和储蓄器操作函数的化名。PHP5的时候,大家利用的apc前缀的函数不可用了,同步变为apcu前缀的函数(需求apcu扩张)。

奥门新浦京官方网站 10

(3)语法兼容晋级。实际上中国人民解放军海军工程大学业作量不算大,从PHP5.6进级到PHP7变化并十分的少。

我们大概在二零一六年十二月中旬份落成了PHP7和Apache的编译专业,
八月下旬实行现网灰度,一月首全量公布到里面贰个现网集群。

2.升任进度中的错误调节和测量试验方法

在晋级和另行编写翻译PHP7扩充时,若是举行理并了结果不相符预期或然经过core掉,相当多不当都以心余力绌从error日志里看到的,不方便人民群众解析难题。能够选拔以下二种形式,能够用来稳固和深入分析抢先四分三的标题:

(1)var_dump/exit

从PHP代码层稳步输出新闻和实施exit,能够慢慢固化到足够施行的PHP函数地点,然后再依照PHP函数名,反查扩大内的达成函数,找到问题。这种艺术比较简单,然则作用不高。

(2)gdb –p/gdb c

这种措施主要用于解析进度core的情形,大家应用的编写翻译方式,是将mod_php(PHP产生Apache的子或块的法门),使用gdb
–p来监察和控制Apache的劳务进度。

命令:ps aux|grep httpd

奥门新浦京官方网站 11

gdb调节和测验钦定进程:

命令:gdb -p

奥门新浦京官方网站 12

使用c进行捕获,然后构造能够形成core的web诉求:

奥门新浦京官方网站 13

Apache平时是多进度方式,为了让难题比较容易复现,能够在http.con里校勘参数,将运营进度数改良为1个(下图中的七个参数都亟需调解,以完结只运转单进程单线程的指标)。

奥门新浦京官方网站 14

自然还有风度翩翩种更轻易的办法,因为Apache自身就扶助单进度调试情势的。

./apachectl -k start -X -e debug

接下来再通过gdb –p来调整就更简美赞臣(MeadjohnsonState of Qatar些。

(3)通过strace命令查看Apache进度实际在做了些什么职业,依据个中的执行内容,深入分析和定位难题。

strace -Ttt -v -s1024 -f -p pid(进程id)

备注:实施这几个命令,注意权限难题,很或者须求root权限。

底工代码

 2.1介绍

在本种类的率先有的/part%201.html卡塔尔,你打探了PHP扩展的基本布局。你申明了向调用脚本重临静态也许动态值的大约函数,定义INI选项,表明内部数值(全局的)。本课程中,你将见到哪些接受从调用脚本传入函数的数值,以至PHPZend引擎怎么操作内部的变量。

四、PHP5.6到PHP7.0扩大晋级试行记录 1. 数据类型的变化 (1)zval

php7的诞生始于zval构造的扭转,PHP7不再须要指针的指针,绝超越二分一zval**内需修正成zval*。若是PHP7直接操作zval,那么zval*也急需改成zval,Z_*P(State of Qatar也要改成Z_*(),ZVAL_*(var,
…卡塔尔须要改成ZVAL_*(&var,
…卡塔尔(قطر‎,必要求严谨采纳&符号,因为PHP7大概不要求接受zval*,那么非常多地点的&也是要去掉的。

ALLOC_ZVAL,ALLOC_INIT_ZVAL,MAKE_STD_ZVAL那多少个分配内部存款和储蓄器的宏已经被移除了。大多数景观下,zval*相应修正为zval,而INIT_PZVAL宏也被移除了。

/* 7.0zval结构源码 */  
/* value字段,仅占一个size_t长度,只有指针或double或者long */  
typedef union _zend_value {  
    zend_long         lval;                /* long value */  
    double            dval;                /* double value */  
    zend_refcounted  *counted;  
    zend_string      *str;  
    zend_array       *arr;  
    zend_object      *obj;  
    zend_resource    *res;  
    zend_reference   *ref;  
    zend_ast_ref     *ast;  
    zval             *zv;  
    void             *ptr;  
    zend_class_entry *ce;  
    zend_function    *func;  
    struct {  
        uint32_t w1;  
        uint32_t w2;  
    } ww;  
} zend_value;  

struct _zval_struct {  
    zend_value        value;            /* value */  
    union {  
        。。。  
    } u1;/* 扩充字段,主要是类型信息 */  
    union {  
        … …  
    } u2;/* 扩充字段,保存辅助信息 */  
};

(2)整型

直白切换就能够:

long->zend_long

/* 定义 */  
typedef int64_t zend_long;  
/* else */  
typedef int32_t zend_long;

(3)字符串类型

PHP5.6版本中接纳char* +
len的方法意味着字符串,PHP7.0中做了包装,定义了zend_string类型:

struct _zend_string {  
    zend_refcounted_h gc;  
    zend_ulong        h;                /* hash value */  
    size_t            len;  
    char              val[1];  
};

zend_string和char*的转换:

zend_string *str;  
char *cstr = NULL;  
size_t slen = 0;  
//...  
/* 从zend_string获取char* 和 len的方法如下 */  
cstr = ZSTR_VAL(str);  
slen = ZSTR_LEN(str);  
/* char* 构造zend_string的方法 */  
zend_string * zstr = zend_string_init("test",sizeof("test"), 0);

扩大方法,深入解析参数时,使用字符串的地点,将‘s’替换成‘S’:

/* 例如 */  
zend_string *zstr;  
if (zend_parse_parameters(ZEND_NUM_ARGS() , "S", &zstr) == FAILURE)  
{  
    RETURN_LONG(-1);  
}

(4)自定义对象

源代码:

/* php7.0 zend_object 定义 */  
struct _zend_object {  
    zend_refcounted_h gc;  
    uint32_t          handle;  
    zend_class_entry  *ce;  
    const zend_object_handlers  *handlers;  
    HashTable        *properties;  
    zval              properties_table[1];  
};

zend_object是三个可变长度的布局。因而在自定义对象的构造中,zend_object要求放在最后生机勃勃项:

/* 例子 */  
struct clogger_object {  
    CLogger *logger;  
    zend_object  std;// 放在后面  
};  
/* 使用偏移量的方式获取对象 */  
static inline clogger_object *php_clogger_object_from_obj(zend_object *obj) {  
    return (clogger_object*)((char*)(obj) - XtOffsetOf(clogger_object, std));  
}  
#define Z_USEROBJ_P(zv) php_clogger_object_from_obj(Z_OBJ_P((zv)))  
/* 释放资源时 */  
void tphp_clogger_free_storage(zend_object *object TSRMLS_DC)  
{  
    clogger_object *intern = php_clogger_object_from_obj(object);  
    if (intern->logger)  
    {  
        delete intern->logger;  
        intern->logger = NULL;  
    }  
    zend_object_std_dtor(&intern->std);  
}

(5)数组

7.0中的hash表定义如下,给出了一些注释:  
/* 7.0中的hash表结构 */  
typedef struct _Bucket { /* hash表中的一个条目 */  
zval              val;   /* 删除元素zval类型标记为IS_UNDEF */  
zend_ulong        h;                /* hash value (or numeric index)   */  
zend_string      *key;              /* string key or NULL for numerics */  
} Bucket;          
typedef struct _zend_array HashTable;      
struct _zend_array {  
    zend_refcounted_h gc;  
    union {  
        struct {  
            ZEND_ENDIAN_LOHI_4(  
                zend_uchar    flags,  
                zend_uchar    nApplyCount,  
                zend_uchar    nIteratorsCount,  
                zend_uchar    reserve)  
        } v;  
        uint32_t flags;  
    } u;  
    uint32_t          nTableMask;  
    Bucket           *arData; /* 保存所有数组元素 */  
    uint32_t          nNumUsed; /* 当前用到了多少长度, */  
    uint32_t          nNumOfElements; /* 数组中实际保存的元素的个数,一旦nNumUsed的值到达nTableSize,PHP就会尝试调整arData数组,让它更紧凑,具体方式就是抛弃类型为UDENF的条目 */  
    uint32_t          nTableSize; /* 数组被分配的内存大小为2的幂次方(最小值为8) */  
    uint32_t          nInternalPointer;  
    zend_long         nNextFreeElement;  
    dtor_func_t       pDestructor;  
};

其中,PHP7在zend_hash.h中定义了生机勃勃体系宏,用来操作数组,饱含遍历key、遍历value、遍历key-value等,上面是多个总结例子:

/* 数组举例 */  
zval *arr;  
zend_parse_parameters(ZEND_NUM_ARGS() , "a", &arr_qos_req);  
if (arr)  
{  
    zval *item;  
    zend_string *key;  
    ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(arr), key, item) {  
        /* ... */  
    }  
}  
/* 获取到item后,可以通过下面的api获取long、double、string值 */  
zval_get_long(item)   
zval_get_double(item)   
zval_get_string(item)

PHP5.6版本中是因而zend_hash_find查找key,然后将结果给到zval
**变量,并且询问不届时索要团结分配内部存款和储蓄器,开头化二个item,设置暗许值。

  1. PHP7中的api变化 (1)duplicate参数

PHP5.6中非常多API中都亟待填写贰个duplicate参数,表澳优个变量是还是不是须要复制风流倜傥份,特别是string类的操作,PHP7.0中收回duplicate参数,对于string相关操作,只要有duplicate参数,直接删掉就可以。因为PHP7.0中定义了zval_string布局,对字符串的操作,不再要求duplicate值,底层直接行使zend_string_init伊始化叁个zend_string就能够,而在PHP5.6中string是贮存在在zval中的,而zval的内部存款和储蓄器需求手动分配。

涉及的API汇总如下:

add_index_string、add_index_stringl、add_assoc_string_ex、add_assoc_stringl_ex、add_assoc_string、add_assoc_stringl、add_next_index_string、add_next_index_stringl、add_get_assoc_string_ex、add_get_assoc_stringl_ex、add_get_assoc_string、add_get_assoc_stringl、add_get_index_string、add_get_index_stringl、add_property_string_ex、add_property_stringl_ex、add_property_string、add_property_stringl、ZVAL_STRING、ZVAL_STRINGL、RETVAL_STRING、RETVAL_STRINGL、RETURN_STRING、RETURN_STRINGL

(2)MAKE_STD_ZVAL

PHP5.6中,zval变量是在堆上分配的,创建贰个zval变量须求先注解风华正茂(Wissu卡塔尔个指南针,然后选拔MAKE_STD_ZVAL实行分配空间。PHP7.0中,这么些宏已经撤除,变量在栈上分配,间接定义三个变量就可以,不再须要MAKE_STD_ZVAL,使用到的地点,直接去掉就好。

(3)ZEND_RSRC_DTOR_FUNC

改革参数名rsrc为res

/* PHP5.6 */  
typedef struct _zend_rsrc_list_entry {  
    void *ptr;  
    int type;  
    int refcount;  
} zend_rsrc_list_entry;  
typedef void (*rsrc_dtor_func_t)(zend_rsrc_list_entry *rsrc TSRMLS_DC);  
#define ZEND_RSRC_DTOR_FUNC(name)        void name(zend_rsrc_list_entry *rsrc TSRMLS_DC)  

/* PHP7.0 */  
struct _zend_resource {  
    zend_refcounted_h gc;/*7.0中对引用计数做了结构封装*/  
    int               handle;  
    int               type;  
    void             *ptr;  
};  
typedef void (*rsrc_dtor_func_t)(zend_resource *res);  
#define ZEND_RSRC_DTOR_FUNC(name) void name(zend_resource *res)

PHP7.0中,将zend_rsrc_list_entry构造晋级为zend_resource,在新本子中只需求改良一下参数名称就可以。

(4)二级指针宏,即Z_*_PP

PHP7.0中收回了拥有的PP宏,大多数情况一向行使相应的P宏就能够。

(5)zend_object_store_get_object被取消

听别人讲官方wiki,能够定义如下宏,用来获取object,实情看,这几个宏用的依然相比较频仍的:

static inline user_object *user_fetch_object(zend_object *obj) {  
    return (user_object *)((char*)(obj) - XtOffsetOf(user_object, std));  
}  
/* }}} */   
#define Z_USEROBJ_P(zv) user_fetch_object(Z_OBJ_P((zv)))

(6)zend_hash_exists、zend_hash_find

对具有供给字符串参数的函数,PHP5.6中的情势是传递四个参数(char* +
len),而PHP7.0中定义了zend_string,由此只须求五个zend_string变量就可以。

再次回到值形成了zend_bool类型:

/* 例子 */  
zend_string * key;    
key = zend_string_init("key",sizeof("key"), 0);  
zend_bool res_key = zend_hash_exists(itmeArr, key);

【参谋资料】

  1. php5 to phpng:

  2. PHP扩张开辟及水源应用:

  3. PHP
    7中新的Hashtable完结和特性改正:

4.
深深通晓PHP7之zval:

  1. 官方wiki:

  2. PHP手册:

  3. PHP7
    使用财富包裹第三方扩张的得以达成及其源码解读:

其意气风发扩展,大家就要say扩张上平添define_var方法。say扩张相关代码我们请看那篇博文。PHP7增添开辟之hello
word 文中已经详尽介绍了哪些创制多少个扩展和提供了源码下载。

 2.1接到数值

与客户空间的代码不一致,内部函数的参数实际上并非在函数尾部证明的,而是将参数列表的地点传入每一个函数-无论是不是传入了参数-并且,函数能够让Zend引擎将它们转为便于使用的东西。

大家经过定义新函数hello_greetme(State of Qatar来看一下,它将吸收接纳贰个参数然后把它与一些致敬的文本一同输出。和原先同样,我们将要四个地点扩张代码:

在php_hello.h中,挨近别的的函数原型证明处:

                  

PHP_FUNCTION(hello_greetme);

在hello.c中,hello_functions布局的底层:

                  

PHP_FE(hello_bool, NULL)
PHP_FE(hello_null, NULL)
PHP_FE(hello_greetme, NULL)
{NULL, NULL, NULL}
};

以至hello.c底部邻近其余函数的末尾:

                  

PHP_FUNCTION(hello_greetme)
{
char *name;
int name_len;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
RETURN_NULL();
}

php_printf("Hello %sn", name);

RETURN_TRUE;
}

大多数zend_parse_parameters(卡塔尔国块看起来是连连相通的。ZEND_NUM_AEvoqueGS(卡塔尔(قطر‎告诉Zend引擎要博得的参数的新闻,TSRMLS_CC用来保险线程安全,再次回到值将被检查是SUCCESS依然FAILURE。平日情形下,zend_parse_parameters(卡塔尔国将回到SUCCESS;然则,要是调用脚本试图传入太多或太少的参数,大概传播的参数没办法被转为适当的项目,Zend会自动输出一条错误消息并温婉地将调节权还给调用脚本。

本例钦点s注明此函数期待只传入四个参数,何况该参数应该被转为string数据类型并装入通过地点传入的char*变量(相当于因此name)。

瞩目,还会有叁个int变量通过地点被盛传zend_parse_parameters(卡塔尔国。那使Zend引擎提供字符串的字节长度,如此二进制安全的函数不再须求借助strlen(name卡塔尔(قطر‎分明字符串的长度。实际上选用strlen(name卡塔尔(قطر‎以至得不到科学的结果,因为name恐怕在字符串结束在此之前饱含叁个或八个NULL字符。

只要您的函数确切地获得了name参数,接下去要做的正是把它看成标准问安语的少年老成部分输出。注意,用的是php_printf(卡塔尔国实际不是更熟识的printf(卡塔尔。使用这几个函数是有举足轻重的说辞的。首先,它同意字符串通过PHP的缓冲机制的拍卖,该机制除了能够缓冲数据,还可实行额外的拍卖,举个例子gzip压缩。其次,尽管stdout是极佳的输出目的,使用CLI或CGI时,好些个SAPI期望通过一定的pipe或socket传来输出。所以,试图只是透过printf(卡塔尔(قطر‎写入stdout恐怕导致数据遗失、次序颠倒也许被磨损,因为它绕过了预管理。

谈到底,函数通过再次回到TRUE将调控权还给调用程序。你能够未有显式地再次来到值(暗中认可是NULL)而是让决定达到您的函数的末梢,但那是坏习贯。函数假诺不传播任何有含义的结果,应该回到TRUE以表明:“达成职分,一切经常”。

PHP字符串实际恐怕蕴含NULL值,所以,输出含有NULL的二进制安全的字符串甚至后跟NULL的四个字符的方式是,使用上边包车型大巴代码块替换php_printf()指令:

                  

php_printf("Hello ");
PHPWRITE(name, name_len);
php_printf("n");

这段代码应用php_printf(State of Qatar管理确信未有NULL的字符串,但利用别的的宏-PHPW途睿欧ITE-管理客商提供的字符串。这一个宏采纳zend_parse_parameters(卡塔尔国提供的长度(name_len)参数以便能够打字与印刷name的全部内容,无论它是还是不是带有NULL。

zend_parse_parameters(卡塔尔(قطر‎也会管理可选参数。下叁个事例中,你将成立三个函数,它仰望八个long(PHP的整数类型)、二个double(浮点)和三个可选的Boolean值。那个函数在客户空间的扬言大概像这样:

function hello_add($a, $b, $return_long = false) {

$sum =(int)$a +(float)$b;
if ($return_long) {
return intval($sum);
} else {
return floatval($sum);
}
}

在C语言中,那一个函数相似下边包车型客车代码(不忘记在php_hello.h和hello.c的hello_functions[]中参加相关条文以启用它):

                  

PHP_FUNCTION(hello_add)
{
long a;
double b;
zend_bool return_long = 0;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ld|b", &a, &b, &return_long) == FAILURE) {
RETURN_NULL();
}

if (return_long) {
RETURN_LONG(a + b);
} else {
RETURN_DOUBLE(a + b);
}
}

这一次你的数据类型字符串读起来像:“作者要一个long(l),贰个double(d)”。下多少个管道字符表示其余的参数是可选的。假如函数调用时没有传到可选参数,那么zend_parse_parameters()将不会转移传给它的附和变量。最后的b当然是用于Boolean。数据类型字符串前面包车型地铁是a、b和return_long,它们按地址传递,那样zend_parse_parameters(卡塔尔国能够将值装入它们。

警报:在34位平桃园时常不加区分地应用int和long,不过,当您的代码在60个人硬件上编写翻译时,在应该使用三个的地点采纳另一个是很凶险的。所以记住要把long用于整型,把int用于字符串的长度。


1显示不一样的品种和相应的假名代码,以至可用于zend_parse_parameters()的C类型:

                  

表 1:类型和用在zend_parse_parameters()中的字母代码

你大概马上注意到表
1中的最终八个项目都以zval*。待会儿你将见到,PHP中其实选取zval数据类型存款和储蓄全体的客商空间变量。二种“复杂”数据类型,财富、数组和指标,当它们的数据类型代码被用于zend_parse_parameters(卡塔尔(قطر‎时,Zend引擎会实行项目检查,然而因为在C中一贯不与它们对应的数据类型,所以不会实行类型转变。

五、AMS平台进级PHP7的质量优化成果

现网服务是一个可怜重大而又趁机的条件,轻则影响客商体验,重则发生现网事故。因而,大家二月下旬成功PHP7编写翻译和测验专门的学业之后,就在AMS在那之中意气风发台机器进行了灰度上线,阅览了几天后,然后逐步扩大灰度范围,在四月中完毕进级。

这几个是我们压测AMS两个询问八个运动流速计的压测结果,甚至现网CGI机器,在山上相似TGW流量场景下的CPU负载数据:

奥门新浦京官方网站 15

就我们的业务压测和现网结果来看,和法定所说的习性进步豆蔻年华倍,基本生龙活虎致。

奥门新浦京官方网站 16

AMS平台具备多数的CGI机器,PHP7的晋级和应用给我们带给了质量的进级,能够使得节约硬件能源开销。何况,通过Apache2.4的Event方式,大家也增进了Apache在接济并发方面的技艺。

实现define_var方法

2.2ZVAL

相通来讲,zval和PHP顾客空间变量是要你费脑筋(wrap your head
around)的最艰苦的定义。它们也将是第风姿浪漫的。首先大家侦察zval的布局:

                  

struct {
union {
long lval;
double dval;
struct {
char *val;
int len;
} str;
HashTable *ht;
zend_object_value obj;
} value;
zend_uint refcount;
zend_uchar type;
zend_uchar is_ref;
} zval;

如你所见,经常每一种zval具备五个基本的因素:type、is_ref和refcount。is_ref和refcount将要本教程的稍候讲授;未来让我们关心type。

到今后您应当早已深谙了PHP的两种数据类型。它们是表1种列出的四种,再加多NULL-固然事实上的字面意思是怎么着也并未有(或然那就是原因),是独特(unto
its
own)的种类。给定三个切实可行的zval,可用四个有利的宏中的二个测量试验它的类型:Z_TYPE(zval)、Z_TYPE_P(zval*)或Z_TYPE_PP(zval**卡塔尔。三者之间仅局部效益上的界别在于传入的变量所期待的直接的等级。别的的宏也坚决守护相仿的有关_P和_PP的使用约定,举例你将要见到的宏*VAL。

type的值决定zval的value联合的哪些部分被设置。上边包车型大巴代码片断演示了一个缩微版本的var_dump():

                  

PHP_FUNCTION(hello_dump)
{
zval *uservar;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &uservar) == FAILURE) {
RETURN_NULL();
}

switch (Z_TYPE_P(uservar)) {
case IS_NULL:
php_printf("NULLn");
break;
case IS_BOOL:
php_printf("Boolean: %sn", Z_LVAL_P(uservar) ? "TRUE" : "FALSE");
break;
case IS_LONG:
php_printf("Long: %ldn", Z_LVAL_P(uservar));
break;
case IS_DOUBLE:
php_printf("Double: %fn", Z_DVAL_P(uservar));
break;
case IS_STRING:
php_printf("String: ");
PHPWRITE(Z_STRVAL_P(uservar), Z_STRLEN_P(uservar));
php_printf("n");
break;
case IS_RESOURCE:
php_printf("Resourcen");
break;
case IS_ARRAY:
php_printf("Arrayn");
break;
case IS_OBJECT:
php_printf("Objectn");
break;
default:
php_printf("Unknownn");
}

RETURN_TRUE;
}

如你所见,数据类型Boolean和long分享相像的中间因素。就像本体系第大器晚成某当中用的RETURubiconN_BOOL(),FALSE用0表示,TRUE用1表示。

当使用zend_parse_parameters(卡塔尔国需要一个一定的数据类型时,譬喻string,Zend引擎检查输入变量的数据类型。假设协作,Zend只是通过将其扩散zval的对应部分来赢得准确的数据类型。假若是例外的项目,Zend使用普通的类型调换法则将其转为适当的和/或可能的花色。

改进前边实现的hello_greetme(卡塔尔(قطر‎函数,将它分成小一些的(功能)片断:

                  

PHP_FUNCTION(hello_greetme)
{
zval *zname;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zname) == FAILURE) {
RETURN_NULL();
}

convert_to_string(zname);

php_printf("Hello ");
PHPWRITE(Z_STRVAL_P(zname), Z_STRLEN_P(zname));
php_printf("n");

RETURN_TRUE;
}

六、小结

大家PHP7进级研究开发项目组,在过去相比较长的叁个日子段里,经过持续地努力和推动,终于在二零一五年十月下旬现网灰度,2月首在集群中全量晋级,为大家的AMS活动运行平台带给品质上宏大的进级。

PHP7的改变,对于PHP语言本人来讲,具有出色的含义和价值,那让自个儿进一层确信一点,PHP会是叁个尤为好的言语。同有时间,感激PHP社区的开辟者们,为大家专门的学问带给的性质升高。

define_var方法的PHP扩充源码:

这次,zend_parse_parameters(卡塔尔只是获得叁个PHP变量(zval),忽视其连串,接着显式地将该变量转为字符串(肖似于$zname

(string卡塔尔国$zname;),然后使用zname结构的字符串值调用php_printf(State of Qatar。正如您所猜想的那样,存在别的可用来bool、long和double的convert_to_*()函数。

PHP_FUNCTION(define_var){    zval var_value;//变量的值   
zend_string *var_name = NULL;//变量名称//创设整型变量   
ZVAL_LONG(&var_value,2);    zend_set_local_var_str(“lng”,3 ,
&var_value,0卡塔尔国;//设置本地变量   
ZVAL_NULL(&var_value卡塔尔;//创设字符串变量    zend_string *str = NULL; 
  char content[4] =”abc”;    var_name =
zend_string_init(“str”,3,0卡塔尔;//设置变量名称    str =
zend_string_init(content, sizeof(content) -1,0);   
ZVAL_STR(&var_value, str卡塔尔(قطر‎;//设置变量的值   
zend_set_local_var(var_name, &var_value,0卡塔尔国;//设置本地变量   
zend_string_release(var_name);   
ZVAL_NULL(&var_value卡塔尔(قطر‎;//创造数组变量    var_name =
zend_string_init(“arr”,3,0卡塔尔国;//设置变量名称   
array_init(&var_value);    add_index_long(&var_value,0,1);   
add_assoc_stringl_ex(&var_value,”a”,1,”b”,1);   
zend_set_local_var(var_name, &var_value,0State of Qatar;//设置当地变量   
zend_string_release(var_name);   
ZVAL_NULL(&var_valueState of Qatar;//成立对象变量    zend_class_entry *ce;   
zend_string *class_name;class_name =
zend_string_init(“demo”,4,0);    ce = zend_fetch_class(class_name,
ZEND_FETCH_CLASS_AUTO);//获取类       
zend_string_release(class_name);object_init_ex(&var_value, ce);   
zend_set_local_var_str(“obj”,3, &var_value,0State of Qatar;//设置本地变量   
ZVAL_NULL(&var_value);

2.3创建ZVAL

现今甘休,你用到的zval已由Zend引擎分配空间,也通过同样的路线释放。可是一时供给制造你和睦的zval。思索上面包车型大巴代码段:

                  

{
zval *temp;

ALLOC_INIT_ZVAL(temp);

Z_TYPE_P(temp) = IS_LONG;
Z_LVAL_P(temp) = 1234;

zval_ptr_dtor(&temp);
}

ALLOC_INIT_ZVAL(卡塔尔国,如名所示,为zval*分配内部存款和储蓄器并把它开端化为三个新变量。那样之后,可用Z_*_P(卡塔尔(قطر‎设置该变量的品类和值。zval_ptr_dtor(卡塔尔管理费劲的清理变量内部存款和储蓄器工作。

那两个Z_*_P(卡塔尔调用实际能够被归为生龙活虎尺度但的说话:

                  

ZVAL_LONG(temp, 1234);

对此别的项目也设有常常的宏,並且遵照和本类别第风流洒脱有的中现身的RETU奥德赛N_*(卡塔尔相仿的语准绳则。实际上宏RETU传祺N_*()只是对RETVAL_*(State of Qatar薄薄的生机勃勃层包装,再深切则是ZVAL_*(卡塔尔。上边包车型地铁四个版本都以一模一样的:

                  

RETURN_LONG(42);

RETVAL_LONG(42);
return;

ZVAL_LONG(return_value, 42);
return;

Z_TYPE_P(return_value) = IS_LONG;
Z_LVAL_P(return_value) = 42;
return;

return_value->type = IS_LONG;
return_value->value.lval = 42;
return;

假使您很乖巧,你会思索什么定义它们技巧实今后相近hello_long(卡塔尔(قطر‎函数中的使用方法。“return_value从哪里来?为何它不用ALLOC_INIT_ZVAL(State of Qatar分配内部存储器?”,你可能会纳闷。

在平凡的扩充开拓中,你或然不知道return_value实际是在各类PHP_FUNCTION(卡塔尔(قطر‎原型定义中定义的函数参数。Zend引擎给它分配内部存款和储蓄器并将其初步化为NULL,那样纵然你的函数未有显式地设置它,再次来到值仍是可用的。当你的里边函数实行完结,该值被重返到调用程序,可能被放飞-即便调用程序被写为忽视重返值。

}

2.4数组

因为您早前用过PHP,你早就承认了数组作为运载别的变量的变量。这种措施在其间得以达成上选取了一览无余的HashTable。要成立将被重返PHP的数组,最简便的办法涉及使用表第22中学罗列的函数:

                  

表 2:zval数组创建函数

同RETURN_STRING()宏一样,add_*_string(卡塔尔(قطر‎函数的结尾八个参数选用1或0来指明字符串内容是还是不是被拷贝。它们分别都有形如add_*_stringl(State of Qatar的照望版本。l表示会显式提供字符串长度(并非让Zend引擎调用strval(卡塔尔国来拿到这几个值,该函数不是二进制安全的)。

行使二进制安全的情势一点也不细略,只须求在(表示)复制的参数前边钦定长度,像那样:

                  

add_assoc_stringl(arr, "someStringVar", "baz", 3, 1);

使用add_assoc_*(卡塔尔国函数,数组的入眼字假定不分包NULL-add_assoc_*(State of Qatar函数自个儿对于举足轻重字不是二进制安全的。不可使用带有NULL的尤为重要字(实际上对象的受保证的和民用的属性已经运用了这种技巧),不过如果非得这么做,当大家稍候使用zend_hash_*(卡塔尔函数时,你将立即明白怎样达成。

要施行学到的东西,成立上边包车型大巴函数,它回到三个数组到调用程序。显明向php_hello.h和hello_functions[]中扩大条款以使该函数到手适本地声称。

                  

PHP_FUNCTION(hello_array)
{     char *mystr;
    zval *mysubarray;

    array_init(return_value);

    add_index_long(return_value, 42, 123);

    add_next_index_string(return_value, "I should now be found at index 43", 1);

    add_next_index_stringl(return_value, "I’m at 44!", 10, 1);

    mystr = estrdup("Forty Five");
    add_next_index_string(return_value, mystr, 0);

    add_assoc_double(return_value, "pi", 3.1415926535);

    ALLOC_INIT_ZVAL(mysubarray);
    array_init(mysubarray);
    add_next_index_string(mysubarray, "hello", 1);
    add_assoc_zval(return_value, "subarray", mysubarray);    
}

构建增添并查阅var_dump(hello_array());的结果:

array(6) {  [42]=>   int(123)   [43]=>  string(33) “I should
now be found at index 43”   [44]=>  string(10) “I’m at
44!”  [45]=>   string(10) “FortyFive”   [“pi”]=>  
float(3.1415926535)   [“subarray”]=>   array(1) {    
[0]=>     string(5) “hello”   } }

从数组中取回值意味着使用ZENDAPI的zend_hash族函数直接从HashTable中把它们当作zval**抽出出来。大家以接收三个数组为参数的简易函数开首:

                  

function hello_array_strings($arr) {

if (!is_array($arr)) return NULL;

printf("The array passed contains %d elementsn", count($arr));

foreach($arr as $data) {
if (is_string($data)) echo "$datan";
}
}

或者,在C中:

                  

PHP_FUNCTION(hello_array_strings)
{
zval *arr, **data;
HashTable *arr_hash;
HashPosition pointer;
int array_count;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
RETURN_NULL();
}

arr_hash = Z_ARRVAL_P(arr);
array_count = zend_hash_num_elements(arr_hash);

php_printf("The array passed contains %d elementsn", array_count);

for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) {

if (Z_TYPE_PP(data) == IS_STRING) {
     PHPWRITE(Z_STRVAL_PP(data), Z_STRLEN_PP(data));
      php_printf("n");
    }
}
RETURN_TRUE;
}

为了保全函数的总结,只输出了字符串类型的数组成分。你可能会意外,为何不用早先在hello_greetme(卡塔尔函数中用过的convert_to_string(State of Qatar?我们来探视那样做哪些;用上面包车型大巴代码替换上边的for循环:

                  

for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) {

convert_to_string_ex(data);
PHPWRITE(Z_STRVAL_PP(data), Z_STRLEN_PP(data));
php_printf("n");
}

当今重新编写翻译增添并运营下边包车型客车客商空间代码:

                  

<?php

$a = array(‘foo’,123);
var_dump($a);
hello_array_strings($a);
var_dump($a);

?>

瞩目,原始数组被改造了!记住,convert_to_*(卡塔尔函数具备与调用set_type(卡塔尔(قطر‎相仿的效果与利益。由于拍卖的数组与传播的是同二个,此处更改它的项目将改换原本变量。要制止则供给首先制作蓬蓬勃勃份zval的副本。为此,再度将for循环改成上边包车型地铁代码:

                  

for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) {

zval temp;

temp = **data;
zval_copy_ctor(&temp);
convert_to_string(&temp);
PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
php_printf("n");
zval_dtor(&temp);
}

本次更显然的-temp =
**data-只是拷贝了原zval的数码,但由于zval大概包含相仿char*字符串或HashTable*数组等额外已分配能源,这么些相关的财富须要用zval_copy_ctor(卡塔尔国实行复制。之后正是平时的更改、打字与印刷,以致尾声用zval_dtor(卡塔尔(قطر‎去除那几个别本用到的能源。

要是你倍感意外:为何第叁遍引进convert_to_string()时(参见hello_greetme(卡塔尔国的第二个本子,功效被细分为小片断-译注)没做zval_copy_ctor(卡塔尔(قطر‎?那是因为向函数字传送入变量会活动地从原本变量抽离出zval,拷贝二个别本。那始终只效劳于zval的表层(onthe
base),所以,任何次级能源(比如数组元素和指标属性)在选取前照例要求展开抽离。

既然已经看过了数组的值,大家多少扩大下本次演练,也来探视(数组的)关键字:

                  

for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) {

zval temp;
char *key;
int key_len;
long index;

if (zend_hash_get_current_key_ex(arr_hash, &key, &key_len, &index, 0, &pointer) == HASH_KEY_IS_STRING) {
PHPWRITE(key, key_len);
} else {
php_printf("%ld", index);
}

php_printf(" => ");

temp = **data;
zval_copy_ctor(&temp);
convert_to_string(&temp);
PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
php_printf("n");
zval_dtor(&temp);
}

纪事数组能够享有数字索引、关联字符串关键字或兼有双方。调用zend_hash_get_current_key_ex(卡塔尔(قطر‎使得不仅能得到数组当前任务的目录(原来的文章是type-译注),也能够依据重临值分明它的品种,大概为HASH_KEY_IS_STRING、HASH_KEY_IS_LONG或HASH_KEY_NON_EXISTANT。由于zend_hash_get_current_data_ex(卡塔尔能够回来zval**,你能够显明它不会回到HASH_KEY_NON_EXISTANT,所以只必要检验IS_STRING和IS_LONG的恐怕性。

遍历HashTable还应该有任何情势。Zend引擎针对那么些任务展露了多个特别相同的函数:zend_hash_apply()、zend_hash_apply_with_argument()和zend_hash_apply_with_arguments(State of Qatar。第意气风发种样式仅仅遍历HashTable,第二种样式允许传入单个void*参数,第三种样式通过vararg列表允许数量不限的参数。hello_array_walk(卡塔尔(قطر‎展现了它们各自的表现:

                  

static int php_hello_array_walk(zval **element TSRMLS_DC)
{
zval temp;

temp = **element;
zval_copy_ctor(&temp);
convert_to_string(&temp);
PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
php_printf("n");
zval_dtor(&temp);

return ZEND_HASH_APPLY_KEEP;
}

static int php_hello_array_walk_arg(zval **element, char *greeting TSRMLS_DC)
{
php_printf("%s", greeting);
php_hello_array_walk(element TSRMLS_CC);

return ZEND_HASH_APPLY_KEEP;
}

static int php_hello_array_walk_args(zval **element, int num_args, va_list args, zend_hash_key *hash_key)
{
char *prefix = va_arg(args, char*);
char *suffix = va_arg(args, char*);
TSRMLS_FETCH();

php_printf("%s", prefix);
php_hello_array_walk(element TSRMLS_CC);
php_printf("%sn", suffix);

return ZEND_HASH_APPLY_KEEP;
}

PHP_FUNCTION(hello_array_walk)
{
zval *zarray;
int print_newline = 1;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zarray) == FAILURE) {
RETURN_NULL();
}

zend_hash_apply(Z_ARRVAL_P(zarray), (apply_func_t)php_hello_array_walk TSRMLS_CC);
zend_hash_apply_with_argument(Z_ARRVAL_P(zarray), (apply_func_arg_t)php_hello_array_walk_arg, "Hello " TSRMLS_CC);
zend_hash_apply_with_arguments(Z_ARRVAL_P(zarray), (apply_func_args_t)php_hello_array_walk_args, 2, "Hello ", "Welcome to my extension!");

RETURN_TRUE;
}

上述代码相当多精晓易懂,你应当对有关函数的用法丰裕领会了。传入hello_array_walk(卡塔尔国的数组被遍历了一回,一次不带参数,二遍带单个参数,第二遍带多少个参数。本次的筹算中,walk_arg()和walk_args(卡塔尔国函数信赖于不带参的walk(卡塔尔国函数处理调换和打字与印刷zval的做事,因为那项专门的职业在三者中是通用的。

仿佛好多用到zend_hash_apply(卡塔尔(قطر‎的地点,在这里段代码中,walk(State of Qatar(原来的文章是“apply(卡塔尔”-译注)函数重临ZEND_HASH_APPLY_KEEP。这告诉zend_hash_apply(卡塔尔函数离开HashTable中的(当前)成分,继续管理下八个。那儿也得以回到别的值:ZEND_HASH_APPLY_REMOVE-如名所示,删除当前因素并持续利用到下二个;ZEND_HASH_APPLY_STOP-在最近因素中止数组的遍历并脱离zend_hash_apply()函数。

当中不太熟谙的预制零件大概是TSRMLS_FETCH(State of Qatar。回顾第风流罗曼蒂克有个别,TSRMLS_*宏是TSRM层的一片段,用于防止各线程的效能域被此外的侵犯。因为zend_hash_apply(State of Qatar的四线程版本用了vararg列表,tsrm_ls标志没有传来walk(卡塔尔国函数。为了在回调php_hello_array_walk(卡塔尔时找回并运用它,你的函数调用TSRMLS_FETCH(State of Qatar从财富池中找到精确的线程。(注意:该方法比从来传参慢相当多,所以非必须毫无选拔。)

用foreach的款型遍历数组是大规模的天职,可是常常供给经过数字索引或涉及关键字查找数组中的特定值。下多少个函数重回由第一个参数钦点的数组的一个值,该值基于第二个参数内定的偏移量或根本字获得。

                  

PHP_FUNCTION(hello_array_value)
{
zval *zarray, *zoffset, **zvalue;
long index = 0;
char *key = NULL;
int key_len = 0;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &zarray, &zoffset) == FAILURE) {
RETURN_NULL();
}

switch (Z_TYPE_P(zoffset)) {
case IS_NULL:
index = 0;
break;
case IS_DOUBLE:
index = (long)Z_DVAL_P(zoffset);
break;
case IS_BOOL:
case IS_LONG:
case IS_RESOURCE:
index = Z_LVAL_P(zoffset);
break;
case IS_STRING:
key = Z_STRVAL_P(zoffset);
key_len = Z_STRLEN_P(zoffset);
break;
case IS_ARRAY:
key = "Array";
key_len = sizeof("Array") – 1;
break;
case IS_OBJECT:
key = "Object";
key_len = sizeof("Object") – 1;
break;
default:
key = "Unknown";
key_len = sizeof("Unknown") – 1;
}

if (key && zend_hash_find(Z_ARRVAL_P(zarray), key, key_len + 1, (void**)&zvalue) == FAILURE) {
RETURN_NULL();
} else if (!key && zend_hash_index_find(Z_ARRVAL_P(zarray), index, (void**)&zvalue) == FAILURE) {
RETURN_NULL();
}

*return_value = **zvalue;
zval_copy_ctor(return_value);
}

该函数起始于switch块,它用和Zend引擎雷同的艺术管理类型调换。NULL视为0,Boolean据值视为0或1,double转变为long(也开展截断),resource转化为它的数字值。对resource类型的拍卖是PHP3的余留,这个时候资源确实只是在检索中用的数字,实际不是特种的门类(unto
themselves)。

数组和目的只不过正是字符串字面量“Array”或“Object”,因尚未什么样调换具有实际的含义。最终插入缺省条件一点都不大心地拍卖别的情形,防止PHP的将来版本只怕引进别的数据类型而使该扩充发生编写翻译难题。

借使函数查找的是关系关键字,那么key只会被安装为非NULL,所以可用它来明确查找是依附关联也许索引。若是因为根本字不设有使选定的物色未果了,函数由此回到NULL注明战败。不然找到的zval被复制到return_value。

最近,大家在PHP代码中调用那几个艺术,看下效果。

 2.6标识表作为数组

假若原先用过$GLOBALS数组,你应当精通在PHP脚本的大局效率域注明和接受的种种变量也都设有于这一个数组中。回顾下,数组在个中是用HashTable表示的,想到个难题:“是或不是存在非常的地点能够找到GLOBALS数组?”答案是“存在”,正是EG(symbol_table卡塔尔国-ExecutorGlobals布局,它的系列是HashTable(不是HashTable*,留心,只是HashTable)。

早已明白了怎么着搜索数组中涉及于入眼字的因素,今后又知道了哪里能够找到全局符号表,应该能够在扩展的代码中检索变量了:

                  

PHP_FUNCTION(hello_get_global_var)
{
char *varname;
int varname_len;
zval **varvalue;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &varname, &varname_len) == FAILURE) {
RETURN_NULL();
}

if (zend_hash_find(&EG(symbol_table), varname, varname_len + 1, (void**)&varvalue) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined variable: %s", varname);
RETURN_NULL();
}

*return_value = **varvalue;
zval_copy_ctor(return_value);
}

现今那几个对您的话应该丰盛纯熟了。那么些函数选用二个字符串参数,用它从大局功用域找到二个变量並且重回其别本。

那个时候有个新剧情php_error_docref(卡塔尔(قطر‎。你会发掘该函数或是它的近亲分布PHP源码树的种种角落。第二个参数是个可选的文书档案援引(缺省是用当下的函数)。其次是所在都现身的TSRMLS_CC,前面随着关于错误的深重等第,最后是printf(卡塔尔国样式的描述错误音讯的格式字符串及连锁的参数。让你的函数在失败意况下接二连三提供一些有含义的荒诞是很主要的。实际上,今后是个很好的时机,回头向hello_array_value(State of Qatar参预一条错误语句。本学科结尾的核对(代码)完整性黄金年代节也将含有它们(指错误语句-译注)。

除了那个之外全局符号表,Zend引擎也保持叁个到一些符号表的援引。由于此中等学园函授数未有和谐的符号表(为啥须求这几个啊?),局地符号表实际上援引了调用当前里边函数的客户函数的有的成效域。看一个简易的函数,它设置了一部分作用域的变量:

                  

PHP_FUNCTION(hello_set_local_var)
{
zval *newvar;
char *varname;
int varname_len;
zval *value;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &varname, &varname_len, &value) == FAILURE) {
RETURN_NULL();
}

ALLOC_INIT_ZVAL(newvar);
*newvar = *value;
zval_copy_ctor(newvar);
zend_hash_add(EG(active_symbol_table), varname, varname_len + 1, &newvar, sizeof(zval*), NULL);

RETURN_TRUE;
}

那儿绝未有怎么新东西。继续升高,营造于今获得的(代码),针对它运转一些测验脚本。确信拿到了盼望的结果,确实得到了。

试行据守如下:

 2.7援用计数

现今,我们向HashTables中步入的zval要么是新建的,要么是刚拷贝的。它们都是独自的,只占用自个儿的财富且只设有于有个别HashTable中。作为一个言语设计的定义,成立和拷贝变量的措施是“很好”的,不过习贯了C程序设计就能够精通,通过制止拷贝大块的数据-除非相对必需,来节外省部存款和储蓄器和CPU时间并不菲见。思考这段顾客代码:

                  

<?php

$a = file_get_contents(‘fourMegabyteLogFile.log’);
$b = $a;
unset($a);

?>

比如实践zval_copy_ctor(卡塔尔国(将会对字符串内容奉行estrndup(卡塔尔)将$a拷贝给$b,那么那个轻松的剧本实际会用掉8M内部存款和储蓄器来存款和储蓄同风流罗曼蒂克4M文件的两份相近的别本。在最终一步撤消$a只会更糟,因为原始字符串被efree(State of Qatar了。用C做那几个将会非常轻巧,大约是那样:b
= a; a = NULL;。

刚好的是,Zend引擎微微聪明些。当创造$a时,会成立贰个秘密的string类型的zval,它含有日至文件的剧情。这一个zval通过调用zend_hash_add(卡塔尔(قطر‎被赋给
$a变量。当$a被拷贝给$b,引擎做近似上边包车型地铁政工:

                  

{
zval **value;

zend_hash_find(EG(active_symbol_table), "a", sizeof("a"), (void**)&value);

ZVAL_ADDREF(*value);

zend_hash_add(EG(active_symbol_table), "b", sizeof("b"), value, sizeof(zval*));
}

不容争辩,实际代码会更头昏眼花,但是那时候的要点是ZVAL_ADDREF(卡塔尔。记住zval含有四个成分。你曾经理解了type和value;此次拍卖的是refcount。如名所示,refcount是特定的zval在符号表中、数组中或任何地方被引述次数的计数器。

使用ALLOC_INIT_ZVAL(卡塔尔国会把refcount设为1,所以,如若要把它回到或到场HashTable二回,你如何也不用去做。在地点的代码中,你从HashTable中收获一个zval可是从未去除它,所以,它的refcount相配引用它的岗位的数量。为了从其余岗位援引该值,你须要追加它的援引计数。

当顾客空间代码调用unset($a卡塔尔国,引擎对该变量试行zval_ptr_dtor(卡塔尔国。在前头用到的zval_ptr_dtor(卡塔尔(قطر‎中,你看不到的谜底是,那个调用不供给销毁该zval和它的内容。实际专门的工作是减少refcount。假设,且唯有是假如,refcount产生了0,Zend引擎会销毁该zval…

 

$ php ./test.phpint(2)string(3)”abc”array(2) {  [0]=>  int(1) 
[“a”]=>  string(1)”b”}object(demo)#1 (0) {

2.8拷贝 vs 引用

有三种办法援用zval。第生机勃勃种,如上文示范的,被可以称作写复制援用(copy-on-write
referencing)。第二种情势是全然援引(full
referencing);当提起“援用”时,客户空间代码的编者更熟知这种,
以客户空间代码的样式现身就好像于:$a = &$b;。

在zval中,这两体系型的分别在于它的is_ref成员的值,0代表写复制援用,非0表示完全引用。注意,三个zval不容许同期具有三种援引类型。所以,倘使变量起头是is_ref(即完全引用-译注),然后以拷贝的措施赋给新的变量,那么一定实行贰个截然拷贝。寻思下边包车型客车顾客空间代码:

                  

<?php

$a = 1;
$b = &$a;
$c = $a;

?>

在此段代码中,为$a创设并初步化了三个zval,将is_ref设为0,将refcount设为1。当$a被$b引用时,is_ref变为1,refcount依次增加到2。当拷贝至$c时,Zend引擎不能只是星罗棋布refcount至3,因为如此则$c形成了$a的通通引用。关闭is_ref也丰盛,因为这么会使$b看起来疑似$a的生龙活虎份拷贝实际不是引用。所以当时分配了二个新的zval,并接收zval_copy_ctor(State of Qatar把原来(zval)的值拷贝给它。原始zval仍然是is_ref==1、refcount==2,同时新zval则为is_ref=0、refcount=1。以后来看另一块内容相仿的代码块,只是顺序稍有例外:

                  

<?php

$a = 1;
$c = $a;
$b = &$a;

?>

}

末尾结出不改变,$b是$a的完全援用,并且$c是$a的大器晚成份拷贝。然则这一次的中间职能稍有分别。如前,早先时为$a成立一个is_ref==0并且refcount=1的新zval。$c

代码解读

$a;语句将同一个zval赋给$c变量,同一时间将refcount增加到2,is_ref仍然是0。当Zend引擎碰到$b

&$a;,它想要只是将is_ref设为1,不过当然特别,因为那将震慑到$c。所以改为开立异的zval并用zval_copy_ctor(卡塔尔(قطر‎将原来(zval)的开始和结果拷贝给它。然后依次减少原始zval的refcount以申明$a不再行使该zval。替代地,(Zend)设置新zval的is_ref为1、refcount为2,并且更新$a和$b变量指向它(新zval)。

2**.9 核对(代码)完整性**

如前,下边是大家的多个荦荦大者文件的欧洲经济共同体代码:

config.m4

                  

PHP_ARG_ENABLE(hello, [whether to enable Hello World support],
[ –enable-hello   Enable Hello World support])

if test "$PHP_HELLO" = "yes"; then
  AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
  PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi

php_hello.h

                  

#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1

#ifdef ZTS
#include "TSRM.h"
#endif

ZEND_BEGIN_MODULE_GLOBALS(hello)
long counter;
zend_bool direction;
ZEND_END_MODULE_GLOBALS(hello)

#ifdef ZTS
#define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
#else
#define HELLO_G(v) (hello_globals.v)
#endif

#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"

PHP_MINIT_FUNCTION(hello);
PHP_MSHUTDOWN_FUNCTION(hello);
PHP_RINIT_FUNCTION(hello);

PHP_FUNCTION(hello_world);
PHP_FUNCTION(hello_long);
PHP_FUNCTION(hello_double);
PHP_FUNCTION(hello_bool);
PHP_FUNCTION(hello_null);
PHP_FUNCTION(hello_greetme);
PHP_FUNCTION(hello_add);
PHP_FUNCTION(hello_dump);
PHP_FUNCTION(hello_array);
PHP_FUNCTION(hello_array_strings);
PHP_FUNCTION(hello_array_walk);
PHP_FUNCTION(hello_array_value);
PHP_FUNCTION(hello_get_global_var);
PHP_FUNCTION(hello_set_local_var);

extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry

#endif

hello.c

                  

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "php_hello.h"

ZEND_DECLARE_MODULE_GLOBALS(hello)

static function_entry hello_functions[] = {
PHP_FE(hello_world, NULL)
PHP_FE(hello_long, NULL)
PHP_FE(hello_double, NULL)
PHP_FE(hello_bool, NULL)
PHP_FE(hello_null, NULL)
PHP_FE(hello_greetme, NULL)
PHP_FE(hello_add, NULL)
PHP_FE(hello_dump, NULL)
PHP_FE(hello_array, NULL)
PHP_FE(hello_array_strings, NULL)
PHP_FE(hello_array_walk, NULL)
PHP_FE(hello_array_value, NULL)
PHP_FE(hello_get_global_var, NULL)
PHP_FE(hello_set_local_var, NULL)
{NULL, NULL, NULL}
};

zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
PHP_HELLO_WORLD_EXTNAME,
hello_functions,
PHP_MINIT(hello),
PHP_MSHUTDOWN(hello),
PHP_RINIT(hello),
NULL,
NULL,
#if ZEND_MODULE_API_NO >= 20010901
PHP_HELLO_WORLD_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

PHP_INI_BEGIN()
PHP_INI_ENTRY("hello.greeting", "Hello World", PHP_INI_ALL, NULL)
STD_PHP_INI_ENTRY("hello.direction", "1", PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals)
PHP_INI_END()

static void php_hello_init_globals(zend_hello_globals *hello_globals)
{
hello_globals->direction = 1;
}

PHP_RINIT_FUNCTION(hello)
{
HELLO_G(counter) = 0;

return SUCCESS;
}

PHP_MINIT_FUNCTION(hello)
{
ZEND_INIT_MODULE_GLOBALS(hello, php_hello_init_globals, NULL);

REGISTER_INI_ENTRIES();

return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(hello)
{
UNREGISTER_INI_ENTRIES();

return SUCCESS;
}

PHP_FUNCTION(hello_world)
{
RETURN_STRING("Hello World", 1);
}

PHP_FUNCTION(hello_long)
{
if (HELLO_G(direction)) {
HELLO_G(counter)++;
} else {
HELLO_G(counter)–;
}

RETURN_LONG(HELLO_G(counter));
}

PHP_FUNCTION(hello_double)
{
RETURN_DOUBLE(3.1415926535);
}

PHP_FUNCTION(hello_bool)
{
RETURN_BOOL(1);
}

PHP_FUNCTION(hello_null)
{
RETURN_NULL();
}

PHP_FUNCTION(hello_greetme)
{
zval *zname;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zname) == FAILURE) {
RETURN_NULL();
}

convert_to_string(zname);

php_printf("Hello ");
PHPWRITE(Z_STRVAL_P(zname), Z_STRLEN_P(zname));
php_printf("n");

RETURN_TRUE;
}

PHP_FUNCTION(hello_add)
{
long a;
double b;
zend_bool return_long = 0;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ld|b", &a, &b, &return_long) == FAILURE) {
RETURN_NULL();
}

if (return_long) {
RETURN_LONG(a + b);
} else {
RETURN_DOUBLE(a + b);
}
}

PHP_FUNCTION(hello_dump)
{
zval *uservar;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &uservar) == FAILURE) {
RETURN_NULL();
}

switch (Z_TYPE_P(uservar)) {
case IS_NULL:
php_printf("NULLn");
break;
case IS_BOOL:
php_printf("Boolean: %sn", Z_LVAL_P(uservar) ? "TRUE" : "FALSE");
break;
case IS_LONG:
php_printf("Long: %ldn", Z_LVAL_P(uservar));
break;
case IS_DOUBLE:
php_printf("Double: %fn", Z_DVAL_P(uservar));
break;
case IS_STRING:
php_printf("String: ");
PHPWRITE(Z_STRVAL_P(uservar), Z_STRLEN_P(uservar));
php_printf("n");
break;
case IS_RESOURCE:
php_printf("Resourcen");
break;
case IS_ARRAY:
php_printf("Arrayn");
break;
case IS_OBJECT:
php_printf("Objectn");
break;
default:
php_printf("Unknownn");
}

RETURN_TRUE;
}

PHP_FUNCTION(hello_array)
{
char *mystr;
zval *mysubarray;

array_init(return_value);

add_index_long(return_value, 42, 123);

add_next_index_string(return_value, "I should now be found at index 43", 1);

add_next_index_stringl(return_value, "I’m at 44!", 10, 1);

mystr = estrdup("Forty Five");
add_next_index_string(return_value, mystr, 0);

add_assoc_double(return_value, "pi", 3.1415926535);

ALLOC_INIT_ZVAL(mysubarray);
array_init(mysubarray);
add_next_index_string(mysubarray, "hello", 1);
php_printf("mysubarray->refcount = %dn", mysubarray->refcount);
mysubarray->refcount = 2;
php_printf("mysubarray->refcount = %dn", mysubarray->refcount);
add_assoc_zval(return_value, "subarray", mysubarray);

php_printf("mysubarray->refcount = %dn", mysubarray->refcount);
}

PHP_FUNCTION(hello_array_strings)
{
zval *arr, **data;
HashTable *arr_hash;
HashPosition pointer;
int array_count;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
RETURN_NULL();
}

arr_hash = Z_ARRVAL_P(arr);
array_count = zend_hash_num_elements(arr_hash);

php_printf("The array passed contains %d elementsn", array_count);

for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) {

zval temp;
char *key;
int key_len;
long index;

if (zend_hash_get_current_key_ex(arr_hash, &key, &key_len, &index, 0, &pointer) == HASH_KEY_IS_STRING) {
PHPWRITE(key, key_len);
} else {
php_printf("%ld", index);
}

php_printf(" => ");

temp = **data;
zval_copy_ctor(&temp);
convert_to_string(&temp);
PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
php_printf("n");
zval_dtor(&temp);
}

RETURN_TRUE;
}

static int php_hello_array_walk(zval **element TSRMLS_DC)
{
zval temp;

temp = **element;
zval_copy_ctor(&temp);
convert_to_string(&temp);
PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp));
php_printf("n");
zval_dtor(&temp);

return ZEND_HASH_APPLY_KEEP;
}

static int php_hello_array_walk_arg(zval **element, char *greeting TSRMLS_DC)
{
php_printf("%s", greeting);
php_hello_array_walk(element TSRMLS_CC);

return ZEND_HASH_APPLY_KEEP;
}

static int php_hello_array_walk_args(zval **element, int num_args, var_list args, zend_hash_key *hash_key)
{
char *prefix = va_arg(args, char*);
char *suffix = va_arg(args, char*);
TSRMLS_FETCH();

php_printf("%s", prefix);
php_hello_array_walk(element TSRMLS_CC);
php_printf("%sn", suffix);

return ZEND_HASH_APPLY_KEEP;
}

PHP_FUNCTION(hello_array_walk)
{
zval *zarray;
int print_newline = 1;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zarray) == FAILURE) {
RETURN_NULL();
}

zend_hash_apply(Z_ARRVAL_P(zarray), (apply_func_t)php_hello_array_walk TSRMLS_CC);
zend_hash_internal_pointer_reset(Z_ARRVAL_P(zarray));
zend_hash_apply_with_argument(Z_ARRVAL_P(zarray), (apply_func_arg_t)php_hello_array_walk_arg, "Hello " TSRMLS_CC);
zend_hash_apply_with_arguments(Z_ARRVAL_P(zarray), (apply_func_args_t)php_hello_array_walk_args, 2, "Hello ", "Welcome to my extension!");

RETURN_TRUE;
}

PHP_FUNCTION(hello_array_value)
{
zval *zarray, *zoffset, **zvalue;
long index = 0;
char *key = NULL;
int key_len = 0;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &zarray, &zoffset) == FAILURE) {
RETURN_NULL();
}

switch (Z_TYPE_P(zoffset)) {
case IS_NULL:
index = 0;
break;
case IS_DOUBLE:
index = (long)Z_DVAL_P(zoffset);
break;
case IS_BOOL:
case IS_LONG:
case IS_RESOURCE:
index = Z_LVAL_P(zoffset);
break;
case IS_STRING:
key = Z_STRVAL_P(zoffset);
key_len = Z_STRLEN_P(zoffset);
break;
case IS_ARRAY:
key = "Array";
key_len = sizeof("Array") – 1;
break;
case IS_OBJECT:
key = "Object";
key_len = sizeof("Object") – 1;
break;
default:
key = "Unknown";
key_len = sizeof("Unknown") – 1;
}

if (key && zend_hash_find(Z_ARRVAL_P(zarray), key, key_len + 1, (void**)&zvalue) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined index: %s", key);
RETURN_NULL();
} else if (!key && zend_hash_index_find(Z_ARRVAL_P(zarray), index, (void**)&zvalue) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined index: %ld", index);
RETURN_NULL();
}

*return_value = **zvalue;
zval_copy_ctor(return_value);
}

PHP_FUNCTION(hello_get_global_var)
{
char *varname;
int varname_len;
zval **varvalue;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &varname, &varname_len) == FAILURE) {
RETURN_NULL();
}

if (zend_hash_find(&EG(symbol_table), varname, varname_len + 1, (void**)&varvalue) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Undefined variable: %s", varname);
RETURN_NULL();
}

*return_value = **varvalue;
zval_copy_ctor(return_value);
}

PHP_FUNCTION(hello_set_local_var)
{
zval *newvar;
char *varname;
int varname_len;
zval *value;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &varname, &varname_len, &value) == FAILURE) {
RETURN_NULL();
}

ALLOC_INIT_ZVAL(newvar);
*newvar = *value;
zval_copy_ctor(newvar);

zend_hash_add(EG(active_symbol_table), varname, varname_len + 1, &newvar, sizeof(zval*), NULL);

RETURN_TRUE;
}

只顾,上面包车型客车内容,大家把PHP扩张中的zval构造产生变量,把PHP代码中的变量成为本土变量。

下一步是怎么?

在本教程-编写扩张体系的第二片段中,你学习了什么选取函数参数,成立并使用了数组,更要紧的是询问了zval的里边运营方式。第3部分/part%203.htmlState of Qatar将关爱财富数据类型并初阶拍卖更复杂的数据构造。

创制本地变量首要分两步,创制变量和装置为本地变量。

  

 

开创变量

变量的品类有各个,在成立变量的不二秘籍也楚河汉界。

对于简易的数据类型,成立变量很简短。只需调用相应的宏方法就能够。

那么些措施在Zend/zend_types.h文件中,宏方法以ZVAL_开头。如:

ZVAL_NULL 设置为null

ZVAL_FALSE 设置为false。

ZVAL_TRUE 设置为true

ZVAL_BOOL 设置bool。

ZVAL_LONG 设置long。

ZVAL_DOUBLE 设置为double。

利用方式,能够参见上面代码中ZVAL_LONG的调用。

对于数组,对象,字符串等复杂数据类型。相比辛劳。能够参照上面包车型客车演示代码。

安装本地变量

设置本地变量Zend引擎为我们提供了五个情势。五个函数的行使,都在上述的代码中做了演示。那五个法子的使用处景有所差距。

zend_set_local_var

假如已经存在项目为zend_string的变量名,则使用那么些艺术创设本地变量

zend_set_local_var_str

假设未有项目为zend_string的变量名,使用此方式创立本地变量

越多PHP相关本领请搜索千锋PHP,做真正的团结,用良心做教育

发表评论

电子邮件地址不会被公开。 必填项已用*标注