300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > php内核介绍及扩展开发指南 pdf vp进 PHP内核介绍及扩展开发指南—Extensions 的编写...

php内核介绍及扩展开发指南 pdf vp进 PHP内核介绍及扩展开发指南—Extensions 的编写...

时间:2020-04-11 19:59:44

相关推荐

php内核介绍及扩展开发指南 pdf vp进 PHP内核介绍及扩展开发指南—Extensions 的编写...

Extensions 的编写

理解了这些运行机制以后,本章着手介绍Extensions 的编写,但凡写程序的人都知道hello world,那好,就从hello world开始。

1.1Hello World

这是摘自《PHP手册》的示例程序:

/*includestandardheader*/

#include"php.h"

/*declarationoffunctionstobeexported*/

ZEND_FUNCTION(first_module);

/*compiledfunctionlistsoZendknowswhat'sinthismodule*/

zend_function_entryfirstmod_functions[]=

{

ZEND_FE(first_module,NULL)

{NULL,NULL,NULL}

};

/*compiledmoduleinformation*/

zend_module_entryfirstmod_module_entry=

{

STANDARD_MODULE_HEADER,

"FirstModule",

firstmod_functions,

NULL,

NULL,

NULL,

NULL,

NULL,

NO_VERSION_YET,

STANDARD_MODULE_PROPERTIES

};

/*implementstandard"stub"routinetointroduceourselvestoZend*/

#ifCOMPILE_DL_FIRST_MODULE

ZEND_GET_MODULE(firstmod)

#endif

/*implementfunctionthatismeanttobemadeavailabletoPHP*/

ZEND_FUNCTION(first_module)

{

longparameter;

if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"l",¶meter)

==FAILURE)

return;

RETURN_LONG(parameter);

}

这段代码实现了一个简单的extension,首先它包含了“php.h”,这是所有extensions都需要包含的头文件,它定义、声明了我们可以访问的所有Zend数据结构、常量和API等。下面对剩余的步骤进行解释。

1.1.1 声明导出函数

ZEND_FUNCTION(first_module);

ZEND_FUNCTION宏用于声明一个可在PHP代码中调用的函数,其参数即成为PHP函数名,因此,这一句声明了一个名为first_module的PHP函数,将其展开如下:

可见,ZEND_FUNCTION就是简单的声明了一个名为zif_ first_module的C函数,zif可能是”Zend Internal Function”的缩写。函数的原型满足Zend引擎对PHP函数的调用约定,关于其参数将在后面章节进行解释。

1.1.2 声明导出函数块

声明C函数后,Zend并不知道如何调用,我们需要使用如下的语句来完成C函数到PHP函数的映射:

zend_function_entryfirstmod_functions[]=

{

ZEND_FE(first_module,NULL)

{NULL,NULL,NULL}

};

这创建了一个zend_function_entry数组,zend_function_entry存储了关于如何调用该PHP函数的信息,通过它Zend引擎就能够理解和调用我们的函数。

其定义如下:

typedefstruct_zend_function_entry{

char*fname;

void(*handler)(INTERNAL_FUNCTION_PARAMETERS);

struct_zend_arg_info*arg_info;

zend_uintnum_args;

zend_uintflags;

}zend_function_entry;

fname是PHP函数名,是PHP代码能够通过它来调用我们的函数;handler是指向我们在前面声明的C函数的函数指针。这两个参数已经足以完成从C函数到PHP函数的映射。剩余的参数用于告诉Zend该PHP函数对于函数参数的要求,arg_info是个数组,它的每一项都描述了对应下标的参数,num_args是参数的个数,具体将在后面的章节介绍。

我们可以手动填充一个zend_function_entry,但更好的办法是使用Zend提供的宏ZEND_FE,因为Zend并不保证这个结构以后不会变。ZEND_FE使用第一个参数作为PHP函数名,并且在添加了zif前缀后作为C函数名;第二个参数用于填充arg_info,通常使用NULL。上面的代码将得到这样一个zend_function_entry结构:{” first_module,”, zif_first_module, NULL, 0, 0}。当然,这并不是说PHP函数名必须和C函数名有什么关系,也可以通过宏ZEND_NAMED_FE来手动指定PHP函数名,不过这并不是个好主意。

我们必须为希望导出的每一个C函数都创建一个zend_function_entry结构,并将其放到一个数组中以备后用,数组最后一项的成员必须全部为NULL,这用于标记数组的结束。

1.1.3 填写模块信息

下一步需要将我们的模块介绍给Zend,主要包括我们的模块名和导出的函数,这通过填充一个zend_module_entry结构来完成。

zend_module_entry firstmod_module_entry =

{

STANDARD_MODULE_HEADER,

"First Module",

firstmod_functions,

NULL,

NULL,

NULL,

NULL,

NULL,

NO_VERSION_YET,

STANDARD_MODULE_PROPERTIES

};

STANDARD_MODULE_HEADER和STANDARD_MODULE_

PROPERTIES宏填充了该结构的首尾部分,具体填充了什么并不是我们需要关心的,并且为了兼容后续版本也最好不要手工修改。

第二、三项是模块名称和导出函数,名称可以任意填写,导出函数就是我们在前面准备好的zend_function_entry数组。

接下来的五个参数是函数指针,其用法在后面介绍,这里只用NULL填充。

下面的参数是一个C字符串,用于表示模块版本,如果没有则使用NO_VERSION_YET,其实就是NULL。

填写完毕后,需要把这个结构传给Zend引擎,这通过下面的语句完成:

#ifCOMPILE_DL_FIRST_MODULE

ZEND_GET_MODULE(firstmod)

#endif

宏开关用于判断是否是动态链接的,动态链接时才会执行下面的语句,本文仅介绍动态链接的模块,并不关心静态链接时如何与Zend交流信息,因此,可以认为条件总为真。

ZEND_GET_MODULE(firstmod)最后展开得到名为get_module的一个函数:

zend_module_entry*get_module(void)

{

return&firstmod_module_entry;

}

这个函数就是简单的返回我们填充的zend_module_entry结构,这里需要注意的是结构的名称必须是xxx_module_entry,xxx是传递给ZEND_GET_MODULE的参数。当Zend加载我们的模块时,它首先会解析并调用名为get_module的函数,这样就可以得到我们的zend_module_entry,于是,PHP代码就可以调用模块导出的函数了。

1.1.4 实现导出函数

代码最后一部分实现了我们导出的函数:

ZEND_FUNCTION(first_module)

{

longparameter;

if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"l",

¶meter)==FAILURE)

return;

RETURN_LONG(parameter);

}

这里依然要用ZEND_FUNCTION来声明函数原型,函数体通过Zend API和宏,访问了函数参数并返回一个long值——这些都将在后面的章节进行详细介绍。

1.2使用参数

函数的一个重要部分就是访问参数,但由于extension的特殊性,我们无法像通常的函数那样来访问参数。

先来看导出C函数的原型:

voidzif_first_module(

intht,

zval*return_value,

zval**return_value_ptr,

zval*this_ptr,

intreturn_value_used

);

ht是用户传入参数的数目,但一般不应直接读取,而是通过宏ZEND_NUM_ARGS()来获取,这通常用于判断用户是否传入了规定数目的参数。下面介绍如何在我们的C函数中访问这些参数。

1.2.1 标准方法

常用的方法是使用下面这个函数,其使用方法类似于scanf,采用格式化字符串和变长参数列表的方式:

intzend_parse_parameters(intnum_argsTSRMLS_DC,char*type_spec,...);

num_args指出我希望获取的参数数目,通常使用ZEND_NUM_ARGS(),因为我们一般会先用ZEND_NUM_ARGS()判断用户是否传入了规定数目的参数。TSRMLS_DC宏用于线程安全,define和declare时必须这样填写,在调用时应该改用TSRMLS_CC。

type_spec是格式化字符串,其每个字符代表期望的当前参数的类型,之后应传递相应类型变量的指针来接收值,就像scanf那样,可用的字符如下:

格式字符

PHP参数类型

接收变量类型

l

long

long

d

double

double

s

string

char*和int

b

boolean

zend_bool

r

resource

zval*

a

array

zval*

z

zval

zval*

o/O/C

类,不予讨论

N/A

这里面,string是个特例,它需要两个参数,分别获取字符串指针和长度,这是因为PHP没有采用C串,不能根据0来判断字符串结尾。下面是个示例程序:

//获取一个long、一个string和一个resource

longl;

char*s;//字符串地址

ints_len;//字符串长度

zval*res;

//检查参数数目

if(ZEND_NUM_ARGS()!=3)

WRONG_PARAM_COUNT;//该宏输出相应错误信息并退出当前函数

if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,

"lsr",&l,&s,&s_len,&res)==FAILURE)

return;

由于PHP语法不能规定函数原型,因此用户可以传递任意类型的参数,对此,zend_parse_parameters自动进行了类型检查和转换:在内置标量类型,即long、double、boolean和string之间,Zend会自动进行类型转换,我们总能成功取得参数;resource和array则不进行转换,用户传入的参数必须具有指定类型,否则返回错误;zval作为通用结构,可以用于任何参数类型,Zend只需要简单的将其写入本地的接收变量。

除了类型格式符外,该函数还支持另外3个控制符:

格式字符

意义

|

后面的参数是可选的,如果用户没有传递相应的参数,则本地接收变量保持不变,这用于支持默认参数;

!

前面的那个参数可以是NULL,仅用于razoOC,如果用户传递的是NULL,则本地的接收zval*被设为NULL;

/

如果前面那个参数不是引用传递的,则不直接使用传入的zval,而是执行Copy-On-Write。这一点将在后面解释。

最后,关于参数的数目也是有要求的。如果没有采用默认参数,即’|’格式符,则ZEND_NUM_ARGS()、num_args和格式串指出的参数数目这三者间必须完全匹配,否则zend_parse_parameters返回错误;如果使用了默认参数,则ZEND_NUM_ARGS()应和num_args相等,并且应该落在格式串指出的参数数目区间内。

1.2.2 底层方法

大部分情况下,使用标准方法就可以了,但有些函数可能需要处理变参,标准方法对此无能为力(*)。此时,只有使用更加原始的方法——直接获取zval。Zend提供了如下的API:

intzend_get_parameters_array_ex(

intparam_count,

zval***argument_array

TSRMLS_DC);

param_count是希望获取的参数数目,这个值不得大于ZEND_NUM_ARGS(),否则函数出错。argument_array是一个zval**类型的数组,用于接收参数。

这个函数只是简单的返回zval,为了使用它们,我们需要自己访问其成员。首先是获取参数类型,这可以通过zval.type值来判断,可用的type见1.1.1节。之后是获取该type对应的值,我们可以直接访问zval的成员,比如zval.value.lval就是long值,但更方便的方法是使用Zend提供的宏:

展开

Z_LVAL(zval)

(zval).value.lval

Z_DVAL(zval)

(zval).value.dval

Z_STRVAL(zval)

(zval).value.str.val

Z_STRLEN(zval)

(zval).value.str.len

Z_ARRVAL(zval)

(zval).value.ht

Z_RESVAL(zval)

(zval).value.lval

Z_OBJVAL(zval)

(zval).value.obj

Z_BVAL (zval)

((zend_bool)(zval).value.lval)

Z_TYPE(zval)

(zval).type

一个比较特殊的宏是Z_BVAL,它不是简单的返回值,而是进行了类型转换。另外,这些宏都有相应的xxx_P和xxx_PP版本,用于访问zval*和zval**。

有时,用户传入参数的类型并不是我们期望的,这就需要手动进行类型转换了。为此,Zend提供了如下几个函数:

convert_to_boolean_ex()

convert_to_long_ex()

convert_to_double_ex()

convert_to_string_ex()

convert_to_array_ex()

convert_to_object_ex()

convert_to_null_ex()

这些函数可将目标zval转换成指定类型,它接收zval**作为参数,为什么不用zval*呢?这是因为,这些函数有一个额外的步骤,它如果发现传入的zval不是引用类型的,并且需要执行类型转换,则会首先执行Copy-On-Write,并对副本施行转换,因此,为了返回副本必须使用zval**作为参数。如果zval是引用型的,则转换直接作用于目标zval结构。

如果无法转换,这些函数就会将zval设置为目标类型的虚值,比如0、FALSE、空串等,因此函数总会成功返回。

这些函数的非ex版本不执行zval分离,而是直接作用于原zval,因此参数类型是zval*。

1.2.2 引用传递

函数参数的传递也是采用的引用计数方式,函数栈中存放的只是zval**,它很可能和几个变量共享一个zval。

显然,对于引用型的zval,我们可以直接进行写入操作;而对于非引用型的zval,并且其refcount大于1时,如果要进行写入操作,就必须执行zval分离(参见1.1.3)。refcount等于1的情况是因为Zend引擎已经执行了zval状态切换(参见1.1.4情况II),我们得到的是自己独占的zval,可以直接写入。

关于传入的zval是否引用,可以通过zval.is_ref来判断,或者使用宏PZVAL_IS_REF(zval*)。对于zval分离,可以使用宏SEPARATE_ZVAL(zval**),它会自动判断refcount,并且将新zval的地址填充到参数里。

1.2.4 编译检查(TODO)

上面几节介绍了如何在我们的函数中对参数进行检查,也就是运行时检查,这为函数的编写带来了一些负担,代码也不够简洁。为此,Zend提供了编译时检查机制,允许我们指定函数原型,如果用户不按规定调用,则会报错并且跳过该函数,因此,我们的函数总能得到期望的参数。

1.3返回值

从C函数向PHP返回值,并不能使用通常的return语句,导出函数的原型也说明了这一点:

voidzif_first_module(

intht,

zval*return_value,

zval**return_value_ptr,

zval*this_ptr,

intreturn_value_used

);

因此,Zend将返回值地址作为参数传给我们,return_value是Zend为我们预先创建的一个标准zval结构,相当于一个局部变量,用户获得返回值时就相当于对return_value进行赋值操作,我们只需填充它即可;return_value_used表明用户是否使用了返回值,0表明没有使用返回值,当函数结束后return_value的refcount将被减为0,并被销毁,因此,这种情况下完全可以不处理返回值;return_value_ptr用于返回引用,它需要和zend_function_entry.arg_info联合使用,通常都是NULL。

Zend提供了一组宏用于填充return_value:

Macro

Description

RETURN_RESOURCE(resource)

resource

RETURN_BOOL(bool)

boolean

RETURN_FALSE

false

RETURN_TRUE

true

RETURN_NULL()

NULL

RETURN_LONG(long)

long

RETURN_DOUBLE(double)

double

RETURN_STRING(string, duplicate)

字符串。string必须是C串,因为Zend将调用strlen();duplicate表示是否将传入的C串复制一份再赋给zval,如果传入的C串不是用Zend例程分配的,应该指定该值

RETURN_STRINGL(string, length, duplicate)

指定字符串长度,而不是使用strlen()

RETURN_EMPTY_STRING()

空字符串

这些宏将在填充完return_value后,执行return语句。如果不想return,可以改用相应RETURN_xxx宏的RETVAL_xxx版本。

1.3.1 返回引用

默认情况下,return_value_ptr是NULL,而当指定返回引用后(参见2.2.4),zend将采用*return_value_ptr作为返回值。初始状态下,return_value 依然指向一个临时zval,同时 *return_value_ptr = return_value。

通常应该把return_value销毁,并且将*return_value_ptr设为将要返回的zval*,注意要加加引用计数,因为这相当于将该zval赋值给一个用作返回值的临时变量,函数返回后,Zend会减减引用计数。

示例程序:

ZEND_FUNCTION(str_reverse)

{

if(ZEND_NUM_ARGS()!=1)

WRONG_PARAM_COUNT;

zval**args;

if(zend_get_parameters_array_ex(ZEND_NUM_ARGS(),&argsTSRMLS_CC)

==FAILURE)

{

return;

}

convert_to_string(*args);

charswap;

char*head=Z_STRVAL_PP(args);

char*end=head+Z_STRLEN_PP(args)-1;

for(;headrefcount;

}

1.4启动和终止函数

Zend允许模块在加载和卸载时收到通知,以进行初始化和清除工作,我们要做的就是把相应函数传递给Zend,它会在合适的时机自动调用。2.1.3节里留下的五个NULL就是用于这个目的,它们都是函数指针,最后一个用于配合phpinfo()来显示模块信息,在此忽略,只看其他四个。

Zend提供了如下四个宏,分别用于声明对应的函数:

意义

ZEND_MODULE_STARTUP_D(module)

在加载模块时调用

ZEND_MODULE_SHUTDOWN_D(module)

在卸载模块时调用

ZEND_MODULE_ACTIVATE_D(module)

一个页面开始运行时调用

ZEND_MODULE_DEACTIVATE_D(module)

一个页面运行完毕时调用

这些宏的用法和ZEND_FUNCTION宏一样(参见2.1.1),展开后就是声明了特定原型的函数,其参数module可以是任意的,但最好使用模块名称。这些函数的参数中,对我们有用的是int module_number,它是模块号,全局唯一,后面会提到其用处。

在声明和实现相应函数时,都应该使用这些宏。最后,需要把这些函数填写到zend_module_entry里(参见2.1.3),可按顺序使用如下的宏,这些宏生成相应的函数名称:

ZEND_MODULE_STARTUP_N(module)

ZEND_MODULE_SHUTDOWN_N(module)

ZEND_MODULE_ACTIVATE_N(module)

ZEND_MODULE_DEACTIVATE_N(module)

1.5调用PHP函数

有时我们需要在模块中调用用户指定的函数,比如我们实现了sort这样的函数,并且允许用户指定比较函数。这可以使用如下的Zend函数:

intcall_user_function_ex(

HashTable*function_table,

zval**object_pp,

zval*function_name,

zval**retval_ptr_ptr,

zend_uintparam_count,

zval**params[],

intno_separation,

HashTable*symbol_table

TSRMLS_DC)

第一个参数是HashTable,在1.2.3节提到Zend使用HashTable来存储PHP函数,function_table用于指定从哪个HashTable中获取函数。通常应该用CG(function_table),展开就是compiler_globals.function_table,compiler_globals是一个用来存储编译器数据的全局数据结构(与其对应的还有个EG宏,即executor_globals,它用来存储执行器数据)。compiler_globals.function_table里面存储了所有我们可以在PHP页面里面调用的函数,包括Zend内建函数、PHP标准库函数、模块导出的函数以及用户使用PHP代码定义的函数。

object_pp是一个对象,当指定该值时,Zend会从对象的函数表中获取函数,这里不予讨论,总是设为NULL。

function_name必须是string型的zval,存储我们希望调用的函数的名称。为什么使用zval而不是直接用char*,是因为Zend考虑到大部分情况下,我们都是从用户那获得参数,然后再调用call_user_function_ex的,这样就可以不作处理直接把用户参数传给该函数。当然,我们也可以手动创建一个string型zval传给它。

retval_ptr_ptr用于获取函数的返回值,Zend执行完指定的函数后,它就将返回值的指针填充到这里。

param_count和params用于指定函数的参数,params是个zval **这点可能让人感到奇怪,但考虑到该函数的常见用法(见下面的示例)以及2.2.2节关于函数参数的介绍,就一点也不奇怪了。

no_separation用于指定是否在必要时执行zval分离(参见1.1.3),这在写入非引用zval时发生。应该总是将其设为0,表示执行zval分离,否则可能破坏数据。

symbol_table用于指定目标函数的active_symbol_table(参见1.2.3),通常应该使用NULL,这样Zend会为目标函数生成一个空的符号表。

说了这么多,该动动手了,下面的程序片段简单实现了PHP API call_user_func的功能:

ZEND_FUNCTION(call)

{

intnum_args=ZEND_NUM_ARGS();

if(num_args<1)

WRONG_PARAM_COUNT;

zval***args=(zval***)emalloc(sizeof(zval**)*num_args);

zval*ret_zval;

//获取传入的参数

if(zend_get_parameters_array_ex(num_args,argsTSRMLS_CC)

==FAILURE)

{

efree(args);

return;

}

//第一个参数作为函数名,后面的作为函数参数

if(call_user_function_ex(CG(function_table),NULL,**args,

&ret_zval,num_args-1,args+1,0,NULLTSRMLS_CC)

==FAILURE)

{

efree(args);

zend_error(E_ERROR,"Functioncallfailed");

}

//将函数返回值反馈给用户

*return_value=*ret_zval;

efree(args);

}

1.6访问PHP变量

1.6.1 设置

1.2.3节提到Zend使用HashTable来存储全局和局部变量符号,因此访问PHP变量,其实就是操作HashTable。当然,我们不需要手工去做,Zend提供了一组宏完成这些工作。

PHP变量的创建共有三步,首先需要创建一个zval结构,可使用如下的宏:

MAKE_STD_ZVAL(zval*)

这个宏先调用emalloc分配一块zval,然后将其refcount设为1、is_ref设为0。

之后就是设置zval的值,同样,我们不需要直接操作zval的成员,Zend已经提供了如下的宏:

Macro

Description

ZVAL_RESOURCE(zval*, resource)

resource

ZVAL_BOOL(zval*, bool)

boolean

ZVAL_FALSE(zval*)

false

ZVAL_TRUE(zval*)

true

ZVAL_NULL(zval*)

NULL

ZVAL_LONG(zval*, long)

long

ZVAL_DOUBLE(zval*, double)

double

ZVAL_STRING(zval*, string, duplicate)

string必须是C串,因为Zend将调用strlen();duplicate表示是否将传入的C串复制一份再赋给zval,如果传入的C串不是用Zend例程分配的,应该指定该值

ZVAL_STRINGL(zval*, string, length, duplicate)

指定字符串长度,而不是使用strlen()

ZVAL_EMPTY_STRING(zval*)

空字符串

可能你会发现,这个表格和2.3节里面的返回值宏表格很相似,不错,返回值宏就是直接调用的ZVAL_xxx。

既然有了zval,下面把它添加到变量符号表里就可以了,可以使用如下的一组宏:

ZEND_SET_SYMBOL(symtable,name,var)

ZEND_SET_GLOBAL_VAR(name,var)

symtable用来指定你想插入的符号表,一般使用EG(active_symbol_table),表示访问当前调用者的活动符号表。如果想强制访问全局符号表,可以用&EG(symbol_table),这也正是ZEND_SET_GLOBAL_VAR(name, var)所做的。这两个宏的最终效果和执行PHP赋值语句name = var完全一样。

如果只是访问全局变量,可以使用单个宏代替上述三步:

SET_VAR_STRING(name,value)

SET_VAR_STRINGL(name,value,length)

SET_VAR_LONG(name,value)

SET_VAR_DOUBLE(name,value)

上述宏分别用于创建全局的string、long和double变量,它们在内部执行了以上三步,当然,最后调用的是ZEND_SET_GLOBAL_VAR宏。

1.6.2 获取

如果想获取已有的PHP变量,则只能直接访问HashTable,Zend并没有提供相应的操作:

intzend_hash_find(

HashTable*ht,

char*arKey,uintnKeyLength,

void**pData)

这个函数从HashTable中查找元素,pData用于获取结果值,Bucket.pData将被放到这里(如果找到的话)。函数成功则返回SUCCESS,否则返回FAILURE。

下面是个示例:

zval**ppzval;//Bucket.pData里存放的是zval**

if(zend_hash_find(EG(active_symbol_table),"var",4,

(void**)&ppzval)==SUCCESS)

printf("var.refcount=%d\n",(*p)->refcount);

else

printf("NotFound\n");

这段代码从活动符号表中查找名为var的变量,需要注意的是nKeyLength是4,必须包括结尾的0。

获得变量后,拿来读是没有问题的,但是写操作就应该小心对待了。只有当refcount为1或者is_ref为1,才可以写入;否则应该进行zval分离,具体参见1.2.3节。

1.6.3 常量

PHP常量的内部定义如下:

typedefstruct_zend_constant{

zvalvalue;

intflags;

char*name;

uintname_len;

intmodule_number;

}zend_constant;

常量的值依然使用zval存储,但这里的zval是私有的,不会和其他变量或常量共享,其refcount和is_ref被忽略。module_number是模块号,在启动函数中可以获取该值(参见2.4),当模块被卸载时,Zend会使用模块号查找和删除所有该模块注册的常量。如果希望在模块被卸载后,常量依然有效,可以将module_number设为0。另一个注意点是,name_len需要包含结尾的0。

flags值可以是如下两个,可以使用”|”联用:

flag

意义

CONST_CS

常量名大小写敏感

CONST_PERSISTENT

持久常量,在创建常量的页面执行结束后,常量依然有效(*)

所有常量都被放在EG(zend_constants)这张HashTable里,其key是常量名称,value是zend_constant,注意不是zend_constant*,因此HashTable会复制一份zend_constant作为value。

获取一个常量非常简单,只要传递常量名和接受常量值的zval:

intzend_get_constant(char*name,uintname_len,zval*result

TSRMLS_DC);

设置常量稍微复杂一点,需要先填写一个zend_constant结构,要注意的是,常量只能是long、double和string。然后使用如下函数将其加入常量表:

同时,Zend也为我们提供了如下的宏,可以直接创建常量:

intzend_register_constant(zend_constant*cTSRMLS_DC);

REGISTER_LONG_CONSTANT(name, value, flags)REGISTER_MAIN_LONG_CONSTANT(name, value, flags)

REGISTER_DOUBLE_CONSTANT(name, value, flags)REGISTER_MAIN_DOUBLE_CONSTANT(name, value, flags)

REGISTER_STRING_CONSTANT(name, value, flags)REGISTER_MAIN_STRING_CONSTANT(name, value, flags)

REGISTER_STRINGL_CONSTANT(name, value, length, flags)REGISTER_MAIN_STRINGL_CONSTANT(name, value, length, flags)

上述宏的MAIN版本用于创建module_number为0的宏,在模块被卸载后,常量依然有效。而非MAIN版本则假设存在一个名为module_number的int变量,并拿来给zend_constant.module_number赋值,可见这组宏原本就是为在模块启动函数里调用而设计的。另外,当创建string型常量时,Zend也会dup一份字符串,因此可以直接使用C串指定常量值。

最后需要指出的是,上述函数和宏都无法改变已有的常量,如果发现已经存在同名常量,则函数失败。如果想修改的话,只能通过HashTable操作。

1.7输出信息

Zend提供了两个函数用于向浏览器输出信息:

intzend_printf(constchar*format,...);

voidzend_error(inttype,constchar*format,...);

zend_printf用法和C的printf一样;zend_error用于输出错误信息,type可以指定错误的性质,对于不同的错误,Zend将作不同处理:

错误码

处理

E_ERROR

严重错误,立即终止脚本运行。

E_WARNING

警告, 脚本继续执行。

E_PARSE

解析错误,解析器复位,脚本继续执行。

E_NOTICE

通知,脚本继续执行。该信息默认情况下不予输出,可以修改php.ini来启用。

该函数会同时输出出错的文件和行号,类似这样:

Fatalerror:nomemoryin/home/wiki/zdj/ext/test.phponline6

by zhangdongjin

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。