日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

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

發(fā)布時間:2025/3/17 php 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php内核介绍及扩展开发指南 pdf vp进,PHP内核介绍及扩展开发指南—Extensions 的编写... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Extensions 的編寫

理解了這些運行機制以后,本章著手介紹Extensions 的編寫,但凡寫程序的人都知道hello world,那好,就從hello world開始。

1.1Hello World

這是摘自《PHP手冊》的示例程序:

/*?include?standard?header?*/

#include?"php.h"

/*?declaration?of?functions?to?be?exported?*/

ZEND_FUNCTION(first_module);

/*?compiled?function?list?so?Zend?knows?what's?in?this?module?*/

zend_function_entry?firstmod_functions[]?=

{

ZEND_FE(first_module,?NULL)

{NULL,?NULL,?NULL}

};

/*?compiled?module?information?*/

zend_module_entryfirstmod_module_entry=

{

STANDARD_MODULE_HEADER,

"First?Module",

firstmod_functions,

NULL,

NULL,

NULL,

NULL,

NULL,

NO_VERSION_YET,

STANDARD_MODULE_PROPERTIES

};

/*?implement?standard?"stub"?routine?to?introduce?ourselves?to?Zend?*/

#if?COMPILE_DL_FIRST_MODULE

ZEND_GET_MODULE(firstmod)

#endif

/*?implement?function?that?is?meant?to?be?made?available?to?PHP?*/

ZEND_FUNCTION(first_module)

{

long?parameter;

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

==?FAILURE)

return;

RETURN_LONG(parameter);

}

這段代碼實現(xiàn)了一個簡單的extension,首先它包含了“php.h”,這是所有extensions都需要包含的頭文件,它定義、聲明了我們可以訪問的所有Zend數(shù)據(jù)結(jié)構(gòu)、常量和API等。下面對剩余的步驟進行解釋。

1.1.1? 聲明導(dǎo)出函數(shù)

ZEND_FUNCTION(first_module);

ZEND_FUNCTION宏用于聲明一個可在PHP代碼中調(diào)用的函數(shù),其參數(shù)即成為PHP函數(shù)名,因此,這一句聲明了一個名為first_module的PHP函數(shù),將其展開如下:

可見,ZEND_FUNCTION就是簡單的聲明了一個名為zif_ first_module的C函數(shù),zif可能是”Zend Internal Function”的縮寫。函數(shù)的原型滿足Zend引擎對PHP函數(shù)的調(diào)用約定,關(guān)于其參數(shù)將在后面章節(jié)進行解釋。

1.1.2? 聲明導(dǎo)出函數(shù)塊

聲明C函數(shù)后,Zend并不知道如何調(diào)用,我們需要使用如下的語句來完成C函數(shù)到PHP函數(shù)的映射:

zend_function_entry?firstmod_functions[]?=

{

ZEND_FE(first_module,?NULL)

{NULL,?NULL,?NULL}

};

這創(chuàng)建了一個zend_function_entry數(shù)組,zend_function_entry存儲了關(guān)于如何調(diào)用該PHP函數(shù)的信息,通過它Zend引擎就能夠理解和調(diào)用我們的函數(shù)。

其定義如下:

typedef?struct?_zend_function_entry?{

char?*fname;

void?(*handler)(INTERNAL_FUNCTION_PARAMETERS);

struct?_zend_arg_info?*arg_info;

zend_uint?num_args;

zend_uint?flags;

}?zend_function_entry;

fname是PHP函數(shù)名,是PHP代碼能夠通過它來調(diào)用我們的函數(shù);handler是指向我們在前面聲明的C函數(shù)的函數(shù)指針。這兩個參數(shù)已經(jīng)足以完成從C函數(shù)到PHP函數(shù)的映射。剩余的參數(shù)用于告訴Zend該PHP函數(shù)對于函數(shù)參數(shù)的要求,arg_info是個數(shù)組,它的每一項都描述了對應(yīng)下標的參數(shù),num_args是參數(shù)的個數(shù),具體將在后面的章節(jié)介紹。

我們可以手動填充一個zend_function_entry,但更好的辦法是使用Zend提供的宏ZEND_FE,因為Zend并不保證這個結(jié)構(gòu)以后不會變。ZEND_FE使用第一個參數(shù)作為PHP函數(shù)名,并且在添加了zif前綴后作為C函數(shù)名;第二個參數(shù)用于填充arg_info,通常使用NULL。上面的代碼將得到這樣一個zend_function_entry結(jié)構(gòu):{” first_module,”, zif_first_module, NULL, 0, 0}。當然,這并不是說PHP函數(shù)名必須和C函數(shù)名有什么關(guān)系,也可以通過宏ZEND_NAMED_FE來手動指定PHP函數(shù)名,不過這并不是個好主意。

我們必須為希望導(dǎo)出的每一個C函數(shù)都創(chuàng)建一個zend_function_entry結(jié)構(gòu),并將其放到一個數(shù)組中以備后用,數(shù)組最后一項的成員必須全部為NULL,這用于標記數(shù)組的結(jié)束。

1.1.3? 填寫模塊信息

下一步需要將我們的模塊介紹給Zend,主要包括我們的模塊名和導(dǎo)出的函數(shù),這通過填充一個zend_module_entry結(jié)構(gòu)來完成。

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宏填充了該結(jié)構(gòu)的首尾部分,具體填充了什么并不是我們需要關(guān)心的,并且為了兼容后續(xù)版本也最好不要手工修改。

第二、三項是模塊名稱和導(dǎo)出函數(shù),名稱可以任意填寫,導(dǎo)出函數(shù)就是我們在前面準備好的zend_function_entry數(shù)組。

接下來的五個參數(shù)是函數(shù)指針,其用法在后面介紹,這里只用NULL填充。

下面的參數(shù)是一個C字符串,用于表示模塊版本,如果沒有則使用NO_VERSION_YET,其實就是NULL。

填寫完畢后,需要把這個結(jié)構(gòu)傳給Zend引擎,這通過下面的語句完成:

#if?COMPILE_DL_FIRST_MODULE

ZEND_GET_MODULE(firstmod)

#endif

宏開關(guān)用于判斷是否是動態(tài)鏈接的,動態(tài)鏈接時才會執(zhí)行下面的語句,本文僅介紹動態(tài)鏈接的模塊,并不關(guān)心靜態(tài)鏈接時如何與Zend交流信息,因此,可以認為條件總為真。

ZEND_GET_MODULE(firstmod)最后展開得到名為get_module的一個函數(shù):

zend_module_entry?*get_module(void)

{

return?&firstmod_module_entry;

}

這個函數(shù)就是簡單的返回我們填充的zend_module_entry結(jié)構(gòu),這里需要注意的是結(jié)構(gòu)的名稱必須是xxx_module_entry,xxx是傳遞給ZEND_GET_MODULE的參數(shù)。當Zend加載我們的模塊時,它首先會解析并調(diào)用名為get_module的函數(shù),這樣就可以得到我們的zend_module_entry,于是,PHP代碼就可以調(diào)用模塊導(dǎo)出的函數(shù)了。

1.1.4 實現(xiàn)導(dǎo)出函數(shù)

代碼最后一部分實現(xiàn)了我們導(dǎo)出的函數(shù):

ZEND_FUNCTION(first_module)

{

long?parameter;

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

?meter)?==?FAILURE)

return;

RETURN_LONG(parameter);

}

這里依然要用ZEND_FUNCTION來聲明函數(shù)原型,函數(shù)體通過Zend API和宏,訪問了函數(shù)參數(shù)并返回一個long值——這些都將在后面的章節(jié)進行詳細介紹。

1.2使用參數(shù)

函數(shù)的一個重要部分就是訪問參數(shù),但由于extension的特殊性,我們無法像通常的函數(shù)那樣來訪問參數(shù)。

先來看導(dǎo)出C函數(shù)的原型:

void?zif_first_module?(

int?ht,

zval?*?return_value,

zval?**return_value_ptr,

zval?*?this_ptr,

int?return_value_used

);

ht是用戶傳入?yún)?shù)的數(shù)目,但一般不應(yīng)直接讀取,而是通過宏ZEND_NUM_ARGS()來獲取,這通常用于判斷用戶是否傳入了規(guī)定數(shù)目的參數(shù)。下面介紹如何在我們的C函數(shù)中訪問這些參數(shù)。

1.2.1? 標準方法

常用的方法是使用下面這個函數(shù),其使用方法類似于scanf,采用格式化字符串和變長參數(shù)列表的方式:

int?zend_parse_parameters(int?num_args?TSRMLS_DC,?char?*type_spec,?...);

num_args指出我希望獲取的參數(shù)數(shù)目,通常使用ZEND_NUM_ARGS(),因為我們一般會先用ZEND_NUM_ARGS()判斷用戶是否傳入了規(guī)定數(shù)目的參數(shù)。TSRMLS_DC宏用于線程安全,define和declare時必須這樣填寫,在調(diào)用時應(yīng)該改用TSRMLS_CC。

type_spec是格式化字符串,其每個字符代表期望的當前參數(shù)的類型,之后應(yīng)傳遞相應(yīng)類型變量的指針來接收值,就像scanf那樣,可用的字符如下:

格式字符

PHP參數(shù)類型

接收變量類型

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是個特例,它需要兩個參數(shù),分別獲取字符串指針和長度,這是因為PHP沒有采用C串,不能根據(jù)0來判斷字符串結(jié)尾。下面是個示例程序:

//?獲取一個long、一個string和一個resource

long?l;

char?*s;????????//?字符串地址

int?s_len;??????//?字符串長度

zval?*res;

//?檢查參數(shù)數(shù)目

if(ZEND_NUM_ARGS()?!=?3)

WRONG_PARAM_COUNT;?//?該宏輸出相應(yīng)錯誤信息并退出當前函數(shù)

if?(zend_parse_parameters(ZEND_NUM_ARGS()?TSRMLS_CC,

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

return;

由于PHP語法不能規(guī)定函數(shù)原型,因此用戶可以傳遞任意類型的參數(shù),對此,zend_parse_parameters自動進行了類型檢查和轉(zhuǎn)換:在內(nèi)置標量類型,即long、double、boolean和string之間,Zend會自動進行類型轉(zhuǎn)換,我們總能成功取得參數(shù);resource和array則不進行轉(zhuǎn)換,用戶傳入的參數(shù)必須具有指定類型,否則返回錯誤;zval作為通用結(jié)構(gòu),可以用于任何參數(shù)類型,Zend只需要簡單的將其寫入本地的接收變量。

除了類型格式符外,該函數(shù)還支持另外3個控制符:

格式字符

意義

|

后面的參數(shù)是可選的,如果用戶沒有傳遞相應(yīng)的參數(shù),則本地接收變量保持不變,這用于支持默認參數(shù);

!

前面的那個參數(shù)可以是NULL,僅用于razoOC,如果用戶傳遞的是NULL,則本地的接收zval*被設(shè)為NULL;

/

如果前面那個參數(shù)不是引用傳遞的,則不直接使用傳入的zval,而是執(zhí)行Copy-On-Write。這一點將在后面解釋。

最后,關(guān)于參數(shù)的數(shù)目也是有要求的。如果沒有采用默認參數(shù),即’|’格式符,則ZEND_NUM_ARGS()、num_args和格式串指出的參數(shù)數(shù)目這三者間必須完全匹配,否則zend_parse_parameters返回錯誤;如果使用了默認參數(shù),則ZEND_NUM_ARGS()應(yīng)和num_args相等,并且應(yīng)該落在格式串指出的參數(shù)數(shù)目區(qū)間內(nèi)。

1.2.2 底層方法

大部分情況下,使用標準方法就可以了,但有些函數(shù)可能需要處理變參,標準方法對此無能為力(*)。此時,只有使用更加原始的方法——直接獲取zval。Zend提供了如下的API:

int?zend_get_parameters_array_ex(

int?param_count,

zval?***argument_array

TSRMLS_DC);

param_count是希望獲取的參數(shù)數(shù)目,這個值不得大于ZEND_NUM_ARGS(),否則函數(shù)出錯。argument_array是一個zval**類型的數(shù)組,用于接收參數(shù)。

這個函數(shù)只是簡單的返回zval,為了使用它們,我們需要自己訪問其成員。首先是獲取參數(shù)類型,這可以通過zval.type值來判斷,可用的type見1.1.1節(jié)。之后是獲取該type對應(yīng)的值,我們可以直接訪問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,它不是簡單的返回值,而是進行了類型轉(zhuǎn)換。另外,這些宏都有相應(yīng)的xxx_P和xxx_PP版本,用于訪問zval*和zval**。

有時,用戶傳入?yún)?shù)的類型并不是我們期望的,這就需要手動進行類型轉(zhuǎn)換了。為此,Zend提供了如下幾個函數(shù):

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()

這些函數(shù)可將目標zval轉(zhuǎn)換成指定類型,它接收zval**作為參數(shù),為什么不用zval*呢?這是因為,這些函數(shù)有一個額外的步驟,它如果發(fā)現(xiàn)傳入的zval不是引用類型的,并且需要執(zhí)行類型轉(zhuǎn)換,則會首先執(zhí)行Copy-On-Write,并對副本施行轉(zhuǎn)換,因此,為了返回副本必須使用zval**作為參數(shù)。如果zval是引用型的,則轉(zhuǎn)換直接作用于目標zval結(jié)構(gòu)。

如果無法轉(zhuǎn)換,這些函數(shù)就會將zval設(shè)置為目標類型的虛值,比如0、FALSE、空串等,因此函數(shù)總會成功返回。

這些函數(shù)的非ex版本不執(zhí)行zval分離,而是直接作用于原zval,因此參數(shù)類型是zval*。

1.2.2? 引用傳遞

函數(shù)參數(shù)的傳遞也是采用的引用計數(shù)方式,函數(shù)棧中存放的只是zval**,它很可能和幾個變量共享一個zval。

顯然,對于引用型的zval,我們可以直接進行寫入操作;而對于非引用型的zval,并且其refcount大于1時,如果要進行寫入操作,就必須執(zhí)行zval分離(參見1.1.3)。refcount等于1的情況是因為Zend引擎已經(jīng)執(zhí)行了zval狀態(tài)切換(參見1.1.4情況II),我們得到的是自己獨占的zval,可以直接寫入。

關(guān)于傳入的zval是否引用,可以通過zval.is_ref來判斷,或者使用宏P(guān)ZVAL_IS_REF(zval*)。對于zval分離,可以使用宏SEPARATE_ZVAL(zval**),它會自動判斷refcount,并且將新zval的地址填充到參數(shù)里。

1.2.4? 編譯檢查(TODO)

上面幾節(jié)介紹了如何在我們的函數(shù)中對參數(shù)進行檢查,也就是運行時檢查,這為函數(shù)的編寫帶來了一些負擔,代碼也不夠簡潔。為此,Zend提供了編譯時檢查機制,允許我們指定函數(shù)原型,如果用戶不按規(guī)定調(diào)用,則會報錯并且跳過該函數(shù),因此,我們的函數(shù)總能得到期望的參數(shù)。

1.3返回值

從C函數(shù)向PHP返回值,并不能使用通常的return語句,導(dǎo)出函數(shù)的原型也說明了這一點:

void?zif_first_module?(

int?ht,

zval?*?return_value,

zval?**return_value_ptr,

zval?*?this_ptr,

int?return_value_used

);

因此,Zend將返回值地址作為參數(shù)傳給我們,return_value是Zend為我們預(yù)先創(chuàng)建的一個標準zval結(jié)構(gòu),相當于一個局部變量,用戶獲得返回值時就相當于對return_value進行賦值操作,我們只需填充它即可;return_value_used表明用戶是否使用了返回值,0表明沒有使用返回值,當函數(shù)結(jié)束后return_value的refcount將被減為0,并被銷毀,因此,這種情況下完全可以不處理返回值;return_value_ptr用于返回引用,它需要和zend_function_entry.arg_info聯(lián)合使用,通常都是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將調(diào)用strlen();duplicate表示是否將傳入的C串復(fù)制一份再賦給zval,如果傳入的C串不是用Zend例程分配的,應(yīng)該指定該值

RETURN_STRINGL(string, length, duplicate)

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

RETURN_EMPTY_STRING()

空字符串

這些宏將在填充完return_value后,執(zhí)行return語句。如果不想return,可以改用相應(yīng)RETURN_xxx宏的RETVAL_xxx版本。

1.3.1? 返回引用

默認情況下,return_value_ptr是NULL,而當指定返回引用后(參見2.2.4),zend將采用*return_value_ptr作為返回值。初始狀態(tài)下,return_value 依然指向一個臨時zval,同時 *return_value_ptr = return_value。

通常應(yīng)該把return_value銷毀,并且將*return_value_ptr設(shè)為將要返回的zval*,注意要加加引用計數(shù),因為這相當于將該zval賦值給一個用作返回值的臨時變量,函數(shù)返回后,Zend會減減引用計數(shù)。

示例程序:

ZEND_FUNCTION(str_reverse)

{

if(ZEND_NUM_ARGS()!=?1)

WRONG_PARAM_COUNT;

zval?**args;

if(zend_get_parameters_array_ex(ZEND_NUM_ARGS(),?&args?TSRMLS_CC)

==?FAILURE)

{

return;

}

convert_to_string(*args);

char?swap;

char?*head=Z_STRVAL_PP(args);

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

for(;?headrefcount;

}

1.4啟動和終止函數(shù)

Zend允許模塊在加載和卸載時收到通知,以進行初始化和清除工作,我們要做的就是把相應(yīng)函數(shù)傳遞給Zend,它會在合適的時機自動調(diào)用。2.1.3節(jié)里留下的五個NULL就是用于這個目的,它們都是函數(shù)指針,最后一個用于配合phpinfo()來顯示模塊信息,在此忽略,只看其他四個。

Zend提供了如下四個宏,分別用于聲明對應(yīng)的函數(shù):

意義

ZEND_MODULE_STARTUP_D(module)

在加載模塊時調(diào)用

ZEND_MODULE_SHUTDOWN_D(module)

在卸載模塊時調(diào)用

ZEND_MODULE_ACTIVATE_D(module)

一個頁面開始運行時調(diào)用

ZEND_MODULE_DEACTIVATE_D(module)

一個頁面運行完畢時調(diào)用

這些宏的用法和ZEND_FUNCTION宏一樣(參見2.1.1),展開后就是聲明了特定原型的函數(shù),其參數(shù)module可以是任意的,但最好使用模塊名稱。這些函數(shù)的參數(shù)中,對我們有用的是int module_number,它是模塊號,全局唯一,后面會提到其用處。

在聲明和實現(xiàn)相應(yīng)函數(shù)時,都應(yīng)該使用這些宏。最后,需要把這些函數(shù)填寫到zend_module_entry里(參見2.1.3),可按順序使用如下的宏,這些宏生成相應(yīng)的函數(shù)名稱:

ZEND_MODULE_STARTUP_N(module)

ZEND_MODULE_SHUTDOWN_N(module)

ZEND_MODULE_ACTIVATE_N(module)

ZEND_MODULE_DEACTIVATE_N(module)

1.5調(diào)用PHP函數(shù)

有時我們需要在模塊中調(diào)用用戶指定的函數(shù),比如我們實現(xiàn)了sort這樣的函數(shù),并且允許用戶指定比較函數(shù)。這可以使用如下的Zend函數(shù):

int?call_user_function_ex(

HashTable?*function_table,

zval?**object_pp,

zval?*function_name,

zval?**retval_ptr_ptr,

zend_uint?param_count,

zval?**params[],

int?no_separation,

HashTable?*symbol_table

TSRMLS_DC)

第一個參數(shù)是HashTable,在1.2.3節(jié)提到Zend使用HashTable來存儲PHP函數(shù),function_table用于指定從哪個HashTable中獲取函數(shù)。通常應(yīng)該用CG(function_table),展開就是compiler_globals.function_table,compiler_globals是一個用來存儲編譯器數(shù)據(jù)的全局數(shù)據(jù)結(jié)構(gòu)(與其對應(yīng)的還有個EG宏,即executor_globals,它用來存儲執(zhí)行器數(shù)據(jù))。compiler_globals.function_table里面存儲了所有我們可以在PHP頁面里面調(diào)用的函數(shù),包括Zend內(nèi)建函數(shù)、PHP標準庫函數(shù)、模塊導(dǎo)出的函數(shù)以及用戶使用PHP代碼定義的函數(shù)。

object_pp是一個對象,當指定該值時,Zend會從對象的函數(shù)表中獲取函數(shù),這里不予討論,總是設(shè)為NULL。

function_name必須是string型的zval,存儲我們希望調(diào)用的函數(shù)的名稱。為什么使用zval而不是直接用char*,是因為Zend考慮到大部分情況下,我們都是從用戶那獲得參數(shù),然后再調(diào)用call_user_function_ex的,這樣就可以不作處理直接把用戶參數(shù)傳給該函數(shù)。當然,我們也可以手動創(chuàng)建一個string型zval傳給它。

retval_ptr_ptr用于獲取函數(shù)的返回值,Zend執(zhí)行完指定的函數(shù)后,它就將返回值的指針填充到這里。

param_count和params用于指定函數(shù)的參數(shù),params是個zval **這點可能讓人感到奇怪,但考慮到該函數(shù)的常見用法(見下面的示例)以及2.2.2節(jié)關(guān)于函數(shù)參數(shù)的介紹,就一點也不奇怪了。

no_separation用于指定是否在必要時執(zhí)行zval分離(參見1.1.3),這在寫入非引用zval時發(fā)生。應(yīng)該總是將其設(shè)為0,表示執(zhí)行zval分離,否則可能破壞數(shù)據(jù)。

symbol_table用于指定目標函數(shù)的active_symbol_table(參見1.2.3),通常應(yīng)該使用NULL,這樣Zend會為目標函數(shù)生成一個空的符號表。

說了這么多,該動動手了,下面的程序片段簡單實現(xiàn)了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;

//?獲取傳入的參數(shù)

if(zend_get_parameters_array_ex(num_args,?args?TSRMLS_CC)

==?FAILURE)

{

efree(args);

return;

}

//?第一個參數(shù)作為函數(shù)名,后面的作為函數(shù)參數(shù)

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

&ret_zval,?num_args?-?1,?args?+?1,?0,?NULL?TSRMLS_CC)

==?FAILURE)

{

efree(args);

zend_error(E_ERROR,?"Function?call?failed");

}

//?將函數(shù)返回值反饋給用戶

*return_value=?*ret_zval;

efree(args);

}

1.6訪問PHP變量

1.6.1 設(shè)置

1.2.3節(jié)提到Zend使用HashTable來存儲全局和局部變量符號,因此訪問PHP變量,其實就是操作HashTable。當然,我們不需要手工去做,Zend提供了一組宏完成這些工作。

PHP變量的創(chuàng)建共有三步,首先需要創(chuàng)建一個zval結(jié)構(gòu),可使用如下的宏:

MAKE_STD_ZVAL(zval*)

這個宏先調(diào)用emalloc分配一塊zval,然后將其refcount設(shè)為1、is_ref設(shè)為0。

之后就是設(shè)置zval的值,同樣,我們不需要直接操作zval的成員,Zend已經(jīng)提供了如下的宏:

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將調(diào)用strlen();duplicate表示是否將傳入的C串復(fù)制一份再賦給zval,如果傳入的C串不是用Zend例程分配的,應(yīng)該指定該值

ZVAL_STRINGL(zval*, string, length, duplicate)

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

ZVAL_EMPTY_STRING(zval*)

空字符串

可能你會發(fā)現(xiàn),這個表格和2.3節(jié)里面的返回值宏表格很相似,不錯,返回值宏就是直接調(diào)用的ZVAL_xxx。

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

ZEND_SET_SYMBOL(symtable,?name,?var)

ZEND_SET_GLOBAL_VAR(name,?var)

symtable用來指定你想插入的符號表,一般使用EG(active_symbol_table),表示訪問當前調(diào)用者的活動符號表。如果想強制訪問全局符號表,可以用&EG(symbol_table),這也正是ZEND_SET_GLOBAL_VAR(name, var)所做的。這兩個宏的最終效果和執(zhí)行PHP賦值語句name = var完全一樣。

如果只是訪問全局變量,可以使用單個宏代替上述三步:

SET_VAR_STRING(name,?value)

SET_VAR_STRINGL(name,?value,?length)

SET_VAR_LONG(name,?value)

SET_VAR_DOUBLE(name,?value)

上述宏分別用于創(chuàng)建全局的string、long和double變量,它們在內(nèi)部執(zhí)行了以上三步,當然,最后調(diào)用的是ZEND_SET_GLOBAL_VAR宏。

1.6.2 獲取

如果想獲取已有的PHP變量,則只能直接訪問HashTable,Zend并沒有提供相應(yīng)的操作:

int?zend_hash_find(

HashTable?*ht,

char?*arKey,?uint?nKeyLength,

void?**pData)

這個函數(shù)從HashTable中查找元素,pData用于獲取結(jié)果值,Bucket.pData將被放到這里(如果找到的話)。函數(shù)成功則返回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("Not?Found\n");

這段代碼從活動符號表中查找名為var的變量,需要注意的是nKeyLength是4,必須包括結(jié)尾的0。

獲得變量后,拿來讀是沒有問題的,但是寫操作就應(yīng)該小心對待了。只有當refcount為1或者is_ref為1,才可以寫入;否則應(yīng)該進行zval分離,具體參見1.2.3節(jié)。

1.6.3 常量

PHP常量的內(nèi)部定義如下:

typedef?struct?_zend_constant?{

zval?value;

int?flags;

char?*name;

uint?name_len;

int?module_number;

}?zend_constant;

常量的值依然使用zval存儲,但這里的zval是私有的,不會和其他變量或常量共享,其refcount和is_ref被忽略。module_number是模塊號,在啟動函數(shù)中可以獲取該值(參見2.4),當模塊被卸載時,Zend會使用模塊號查找和刪除所有該模塊注冊的常量。如果希望在模塊被卸載后,常量依然有效,可以將module_number設(shè)為0。另一個注意點是,name_len需要包含結(jié)尾的0。

flags值可以是如下兩個,可以使用”|”聯(lián)用:

flag

意義

CONST_CS

常量名大小寫敏感

CONST_PERSISTENT

持久常量,在創(chuàng)建常量的頁面執(zhí)行結(jié)束后,常量依然有效(*)

所有常量都被放在EG(zend_constants)這張HashTable里,其key是常量名稱,value是zend_constant,注意不是zend_constant*,因此HashTable會復(fù)制一份zend_constant作為value。

獲取一個常量非常簡單,只要傳遞常量名和接受常量值的zval:

int?zend_get_constant(char?*name,?uint?name_len,?zval?*result

TSRMLS_DC);

設(shè)置常量稍微復(fù)雜一點,需要先填寫一個zend_constant結(jié)構(gòu),要注意的是,常量只能是long、double和string。然后使用如下函數(shù)將其加入常量表:

同時,Zend也為我們提供了如下的宏,可以直接創(chuàng)建常量:

int?zend_register_constant(zend_constant?*c?TSRMLS_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版本用于創(chuàng)建module_number為0的宏,在模塊被卸載后,常量依然有效。而非MAIN版本則假設(shè)存在一個名為module_number的int變量,并拿來給zend_constant.module_number賦值,可見這組宏原本就是為在模塊啟動函數(shù)里調(diào)用而設(shè)計的。另外,當創(chuàng)建string型常量時,Zend也會dup一份字符串,因此可以直接使用C串指定常量值。

最后需要指出的是,上述函數(shù)和宏都無法改變已有的常量,如果發(fā)現(xiàn)已經(jīng)存在同名常量,則函數(shù)失敗。如果想修改的話,只能通過HashTable操作。

1.7輸出信息

Zend提供了兩個函數(shù)用于向瀏覽器輸出信息:

int?zend_printf(const?char?*format,?...);

void?zend_error(int?type,?const?char?*format,?...);

zend_printf用法和C的printf一樣;zend_error用于輸出錯誤信息,type可以指定錯誤的性質(zhì),對于不同的錯誤,Zend將作不同處理:

錯誤碼

處理

E_ERROR

嚴重錯誤,立即終止腳本運行。

E_WARNING

警告, 腳本繼續(xù)執(zhí)行。

E_PARSE

解析錯誤,解析器復(fù)位,腳本繼續(xù)執(zhí)行。

E_NOTICE

通知,腳本繼續(xù)執(zhí)行。該信息默認情況下不予輸出,可以修改php.ini來啟用。

該函數(shù)會同時輸出出錯的文件和行號,類似這樣:

Fatal?error:?no?memory?in?/home/wiki/zdj/ext/test.php?on?line?6

by zhangdongjin

總結(jié)

以上是生活随笔為你收集整理的php内核介绍及扩展开发指南 pdf vp进,PHP内核介绍及扩展开发指南—Extensions 的编写...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 在线观看国产黄色 | 强行无套内谢大学生初次 | 久久男人视频 | 日本69式三人交 | 久久精品偷拍视频 | 美国黄色片网站 | av高清免费 | 日日操天天操 | 亚洲天堂一区二区 | 91午夜在线观看 | 一区二区日本视频 | 激情三区 | 熟女一区二区三区视频 | 极品少妇av | av综合网站 | 成人在线观看免费 | 亚洲一卡二卡在线观看 | 黄色变态网站 | 国产女人18水真多毛片18精品 | 91综合在线 | 青青青青青草 | 久久aⅴ乱码一区二区三区 亚洲成人18 | 人禽l交视频在线播放 视频 | 国产精品人妖 | 懂色a v| 无码av天堂一区二区三区 | 无码av免费精品一区二区三区 | 怡春院国产| 在线视频你懂得 | 午夜影片 | 欧美图片第一页 | 婷婷色婷婷 | 国产日韩欧美在线观看视频 | 伊人网大香 | 国产亚州av | 日韩欧美视频 | 香蕉在线影院 | 国产高清一区在线观看 | 偷拍一区二区三区 | 麻豆网址 | 草草视频在线观看 | 日韩中文电影 | 日韩国产欧美综合 | 另类小说五月天 | 国产片久久 | 2022av视频| 国产在线观看成人 | 久久久精品麻豆 | 日韩在线激情 | 欧美一区三区三区高中清蜜桃 | 美腿丝袜亚洲综合 | 精品日韩一区 | 成年人免费看视频 | 91色在线播放 | 欧美日韩视频网站 | 欧美丰满一区二区免费视频 | 日韩毛片基地 | 免费黄色网址观看 | 国产区精品视频 | 精品熟妇视频一区二区三区 | 亚洲AV无码精品自拍 | 一级欧美一级日韩片 | 国产乱码精品一区二区 | 国内成人在线 | 修女也疯狂3免费观看完整版 | 特级a毛片 | 天堂bt在线| 高清无码视频直接看 | aaaa一级片 | 激情伦成人综合小说 | 国产精品腿扒开做爽爽爽挤奶网站 | 黄色一级大片在线免费看国产 | 国产成人手机在线 | 亚洲一级电影 | 久久全国免费视频 | 成人公开免费视频 | 日韩黄色片免费看 | 久久免费看 | 国产欧美一区二区三区精品酒店 | 色偷偷av男人的天堂 | 18在线观看免费入口 | 精品无码一区二区三区在线 | 日韩在线不卡视频 | 免费观看已满十八岁 | 久久日视频 | 91小视频| 在线看黄色av | 日韩欧美中文字幕在线播放 | 欧美 日韩 成人 | 欧美不卡 | 亚洲精品国产精品国自产网站按摩 | 天天婷婷| 五月婷激情| 国产在线精品播放 | 91成人短视频在线观看 | 草的我好爽 | 少妇久久久久久久 | 欧美日本韩国一区二区 | 毛片网站免费 |