国产精品成人VA在线观看,亚洲日韩在线中文字幕综合,亚洲AV电影天堂男人的天堂,久久人人爽人人爽人人av东京热

News新聞

業(yè)界新聞動(dòng)態(tài)、技術(shù)前沿
Who are we?

您的位置:首頁      樂道系統(tǒng)FAQ      如何編寫PHP擴(kuò)展

如何編寫PHP擴(kuò)展

標(biāo)簽: 發(fā)布日期:2014-02-21 00:00:00 944

翻譯:taft@wjl.cn

 

Ver 0.1

 

最后修改日期 2006/1/19

 

WJL Studio @ wjl.cn 2006

 

目 錄

簡 介

快速開始

內(nèi)存管理

從PHP函數(shù)中返回值

完成self_concat()

實(shí)例小結(jié)

包裹第三方的擴(kuò)展

編寫利用資源的第一個(gè)PHP函數(shù)

全局變量

添加自定義INI指令

線程安全資源管理宏

總 結(jié)

詞匯表

 

 

 

PHP取得成功的一個(gè)主要原因之一是她擁有大量的可用擴(kuò)展。web開發(fā)者無論有何種需求,這種需求最有可能在PHP發(fā)行包里找到。PHP發(fā)行包包括支持各種數(shù)據(jù)庫,圖形文件格式,壓縮,XML技術(shù)擴(kuò)展在內(nèi)的許多擴(kuò)展。

擴(kuò)展API的引入使PHP3取得了巨大的進(jìn)展,擴(kuò)展API機(jī)制使PHP開發(fā)社區(qū)很容易的開發(fā)出幾十種擴(kuò)展?,F(xiàn)在,兩個(gè)版本過去了,API仍然和PHP3時(shí)的非常相似。擴(kuò)展主要的思想是:盡可能的從擴(kuò)展編寫者那里隱藏PHP的內(nèi)部機(jī)制和腳本引擎本身,僅僅需要開發(fā)者熟悉API。

有兩個(gè)理由需要自己編寫PHP擴(kuò)展。第一個(gè)理由是:PHP需要支持一項(xiàng)她還未支持的技術(shù)。這通常包括包裹一些現(xiàn)成的C函數(shù)庫,以便提供PHP接口。例如,如果一個(gè)叫FooBase的數(shù)據(jù)庫已推出市場,你需要建立一個(gè)PHP擴(kuò)展幫助你從PHP里調(diào)用FooBase的C函數(shù)庫。這個(gè)工作可能僅由一個(gè)人完成,然后被整個(gè)PHP社區(qū)共享(如果你愿意的話)。第二個(gè)不是很普遍的理由是:你需要從性能或功能的原因考慮來編寫一些商業(yè)邏輯。

如果以上的兩個(gè)理由都和你沒什么關(guān)系,同時(shí)你感覺自己沒有冒險(xiǎn)精神,那么你可以跳過本章。

本章教你如何編寫相對簡單的PHP擴(kuò)展,使用一部分?jǐn)U展API函數(shù)。對于大多數(shù)打算開發(fā)自定義PHP擴(kuò)展開發(fā)者而言,它含概了足夠的資料。學(xué)習(xí)一門編程課程的最好方法之一就是動(dòng)手做一些極其簡單的例子,這些例子正是本章的線索。一旦你明白了基礎(chǔ)的東西,你就可以在互聯(lián)網(wǎng)上通過閱讀文擋、原代碼或參加郵件列表新聞組討論來豐富自己。因此,本章集中在讓你如何開始的話題。在UNIX下一個(gè)叫ext_skel的腳本被用于建立擴(kuò)展的骨架,骨架信息從一個(gè)描述擴(kuò)展接口的定義文件中取得。因此你需要利用UNIX來建立一個(gè)骨架。Windows開發(fā)者可以使用Windows ext_skel_win32.php代替ext_skel。

然而,本章關(guān)于用你開發(fā)的擴(kuò)展編譯PHP的指導(dǎo)僅涉及UNIX編譯系統(tǒng)。本章中所有的對API的解釋與UNIX和Windows下開發(fā)的擴(kuò)展都有聯(lián)系。

當(dāng)你閱讀完這章,你能學(xué)會(huì)如何

? 建立一個(gè)簡單的商業(yè)邏輯擴(kuò)展。

? .建議個(gè)C函數(shù)庫的包裹擴(kuò)展,尤其是有些標(biāo)準(zhǔn)C文件操作函數(shù)比如fopen()

 

快速開始

 

本節(jié)沒有介紹關(guān)于腳本引擎基本構(gòu)造的一些知識(shí),而是直接進(jìn)入擴(kuò)展的編碼講解中,因此不要擔(dān)心你無法立刻獲得對擴(kuò)展整體把握的感覺。假設(shè)你正在開發(fā)一個(gè)網(wǎng)站,需要一個(gè)把字符串重復(fù)n次的函數(shù)。下面是用PHP寫的例子:

 

function self_concat($string, $n)

{

$result = "";

for ($i = 0; $i < $n; $i++) {

$result .= $string;

}

return $result;

}

 

self_concat("One", 3) returns "OneOneOne".

self_concat("One", 1) returns "One".

 

假設(shè)由于一些奇怪的原因,你需要時(shí)常調(diào)用這個(gè)函數(shù),而且還要傳給函數(shù)很長的字符串和大值n。這意味著在腳本里有相當(dāng)巨大的字符串連接量和內(nèi)存重新分配過程,以至顯著地降低腳本執(zhí)行速度。如果有一個(gè)函數(shù)能夠更快地分配大量且足夠的內(nèi)存來存放結(jié)果字符串,然后把$string重復(fù)n次,就不需要在每次循環(huán)迭代中分配內(nèi)存。

為擴(kuò)展建立函數(shù)的第一步是寫一個(gè)函數(shù)定義文件,該函數(shù)定義文件定義了擴(kuò)展對外提供的函數(shù)原形。該例中,定義函數(shù)只有一行函數(shù)原形self_concat() :

 

string self_concat(string str, int n)

 

函數(shù)定義文件的一般格式是一個(gè)函數(shù)一行。你可以定義可選參數(shù)和使用大量的PHP類型,包括: bool, float, int, array等。

保存為myfunctions.def文件至PHP原代碼目錄樹下。

該是通過擴(kuò)展骨架(skeleton)構(gòu)造器運(yùn)行函數(shù)定義文件的時(shí)機(jī)了。該構(gòu)造器腳本叫ext_skel,放在PHP原代碼目錄樹的ext/目錄下(PHP原碼主目錄下的README.EXT_SKEL提供了更多的信息)。假設(shè)你把函數(shù)定義保存在一個(gè)叫做myfunctions.def的文件里,而且你希望把擴(kuò)展取名為myfunctions,運(yùn)行下面的命令來建立擴(kuò)展骨架

 

./ext_skel --extname=myfunctions --proto=myfunctions.def

 

       這個(gè)命令在ext/目錄下建立了一個(gè)myfunctions/目錄。你要做的第一件事情也許就是編譯該骨架,以便編寫和測試實(shí)際的C代碼。編譯擴(kuò)展有兩種方法:

 

?  作為一個(gè)可裝載模塊或者DSO(動(dòng)態(tài)共享對象)

?  靜態(tài)編譯到PHP

 

因?yàn)榈诙N方法比較容易上手,所以本章采用靜態(tài)編譯。如果你對編譯可裝載擴(kuò)展模塊感興趣,可以閱讀PHP原代碼根目錄下的README.SELF-CONTAINED_EXTENSIONS文件。為了使擴(kuò)展能夠被編譯,需要修改擴(kuò)展目錄ext/myfunctions/下的config.m4文件。擴(kuò)展沒有包裹任何外部的C庫,你需要添加支持--enable-myfunctions配置開關(guān)到PHP編譯系統(tǒng)里(–with-extension 開關(guān)用于那些需要用戶指定相關(guān)C庫路徑的擴(kuò)展)??梢匀サ糇詣?dòng)生成的下面兩行的注釋來開啟這個(gè)配置。

 

PHP_ARG_ENABLE(myfunctions, whether to enable myfunctions support,

[ --enable-myfunctions                Include myfunctions support])

 

現(xiàn)在剩下的事情就是在PHP原代碼樹根目錄下運(yùn)行./buildconf,該命令會(huì)生成一個(gè)新的配置腳本。通過查看./configure --help輸出信息,可以檢查新的配置選項(xiàng)是否被包含到配置文件中?,F(xiàn)在,打開你喜好的配置選項(xiàng)開關(guān)和--enable-myfunctions重新配置一下PHP。最后的但不是最次要的是,用make來重新編譯PHP。

       ext_skel應(yīng)該把兩個(gè)PHP函數(shù)添加到你的擴(kuò)展骨架了:打算實(shí)現(xiàn)的self_concat()函數(shù)和用于檢測myfunctions 是否編譯到PHP的confirm_myfunctions_compiled()函數(shù)。完成PHP的擴(kuò)展開發(fā)后,可以把后者去掉。

 

<?php

print confirm_myfunctions_compiled("myextension");

?>

 

運(yùn)行這個(gè)腳本會(huì)出現(xiàn)類似下面的輸出:

"Congratulations! You have successfully modified ext/myfunctions

config.m4. Module myfunctions is now compiled into PHP."

另外,ext_skel腳本生成一個(gè)叫myfunctions.php的腳本,你也可以利用它來驗(yàn)證擴(kuò)展是否被成功地編譯到PHP。它會(huì)列出該擴(kuò)展所支持的所有函數(shù)。

       現(xiàn)在你學(xué)會(huì)如何編譯擴(kuò)展了,該是真正地研究self_concat()函數(shù)的時(shí)候了。

              下面就是ext_skel腳本生成的骨架結(jié)構(gòu):

 

/* {{{ proto string self_concat(string str, int n)

*/

PHP_FUNCTION(self_concat)

}

char *str = NULL;

int argc = ZEND_NUM_ARGS();

int str_len;

long n;

if (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE)

return;

php_error(E_WARNING, "self_concat: not yet implemented");

}

/* }}} */

 

自動(dòng)生成的PHP函數(shù)周圍包含了一些注釋,這些注釋用于自動(dòng)生成代碼文檔和vi、Emacs等編輯器的代碼折疊。函數(shù)自身的定義使用了宏P(guān)HP_FUNCTION(),該宏可以生成一個(gè)適合于Zend引擎的函數(shù)原型。邏輯本身分成語義各部分,取得調(diào)用函數(shù)的參數(shù)和邏輯本身。

       為了獲得函數(shù)傳遞的參數(shù),可以使用zend_parse_parameters()API函數(shù)。下面是該函數(shù)的原型:

zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, …);

 

第一個(gè)參數(shù)是傳遞給函數(shù)的參數(shù)個(gè)數(shù)。通常的做法是傳給它ZEND_NUM_ARGS()。這是一個(gè)表示傳遞給函數(shù)參數(shù)總個(gè)數(shù)的宏。第二個(gè)參數(shù)是為了線程安全,總是傳遞TSRMLS_CC宏,后面會(huì)講到。第三個(gè)參數(shù)是一個(gè)字符串,指定了函數(shù)期望的參數(shù)類型,后面緊跟著需要隨參數(shù)值更新的變量列表。因?yàn)镻HP采用松散的變量定義和動(dòng)態(tài)的類型判斷,這樣做就使得把不同類型的參數(shù)轉(zhuǎn)化為期望的類型成為可能。例如,如果用戶傳遞一個(gè)整數(shù)變量,可函數(shù)需要一個(gè)浮點(diǎn)數(shù),那么zend_parse_parameters()就會(huì)自動(dòng)地把整數(shù)轉(zhuǎn)換為相應(yīng)的浮點(diǎn)數(shù)。如果實(shí)際值無法轉(zhuǎn)換成期望類型(比如整形到數(shù)組形),會(huì)觸發(fā)一個(gè)警告。

下表列出了可能指定的類型。我們從完整性考慮也列出了一些沒有討論到的類型。

 

類型指定符

對應(yīng)的C類型

描述

l

long

符號(hào)整數(shù)

d

double

浮點(diǎn)數(shù)

s

char *, int

二進(jìn)制字符串,長度

b

zend_bool

邏輯型(1或0)

r

zval *

資源(文件指針,數(shù)據(jù)庫連接等)

a

zval *

聯(lián)合數(shù)組

o

zval *

任何類型的對象

O

zval *

指定類型的對象。需要提供目標(biāo)對象的類類型

z

zval *

無任何操作的zval

 

為了容易地理解最后幾個(gè)選項(xiàng)的含義,你需要知道zval是Zend引擎的值容器[1]。無論這個(gè)變量是布爾型,字符串型或者其他任何類型,其信息總會(huì)包含在一個(gè)zval聯(lián)合體中。本章中我們不直接存取zval,而是通過一些附加的宏來操作。下面的是或多或少在C中的zval, 以便我們能更好地理解接下來的代碼。

 

typedef union _zval {

long lval;

double dval;

struct {

char *val;

int len;

} str;

HashTable *ht;

zend_object_value obj;

} zval;

 

在我們的例子中,我們用基本類型調(diào)用zend_parse_parameters(),以本地C類型的方式取得函數(shù)參數(shù)的值,而不是用zval容器。

為了讓zend_parse_parameters()能夠改變傳遞給它的參數(shù)的值,并返回這個(gè)改變值,需要傳遞一個(gè)引用。仔細(xì)查看一下self_concat():

 

if (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE)

return;

 

       注意到自動(dòng)生成的代碼會(huì)檢測函數(shù)的返回值FAILUER(成功即SUCCESS)來判斷是否成功。如果沒有成功則立即返回,并且由zend_parse_parameters()負(fù)責(zé)觸發(fā)警告信息。因?yàn)楹瘮?shù)打算接收一個(gè)字符串l和一個(gè)整數(shù)n,所以指定 ”sl” 作為其類型指示符。s需要兩個(gè)參數(shù),所以我們傳遞參考char * 和int (str 和 str_len)給zend_parse_parameters()函數(shù)。無論什么時(shí)候,記得總是在代碼中使用字符串長度str_len來確保函數(shù)工作在二進(jìn)制安全的環(huán)境中。不要使用strlen()和strcpy(),除非你不介意函數(shù)在二進(jìn)制字符串下不能工作。二進(jìn)制字符串是包含有nulls的字符串。二進(jìn)制格式包括圖象文件,壓縮文件,可執(zhí)行文件和更多的其他文件。”l” 只需要一個(gè)參數(shù),所以我們傳遞給它n的引用。盡管為了清晰起見,骨架腳本生成的C變量名與在函數(shù)原型定義文件中的參數(shù)名一樣;這樣做不是必須的,盡管在實(shí)踐中鼓勵(lì)這樣做。

回到轉(zhuǎn)換規(guī)則中來。下面三個(gè)對self_concat()函數(shù)的調(diào)用使str, str_len和n得到同樣的值:

 

self_concat("321", 5);

self_concat(321, "5");

self_concat("321", "5");

str points to the string "321", str_len equals 3, and n equals 5.

str 指向字符串"321",str_len等于3,n等于5。

 

在我們編寫代碼來實(shí)現(xiàn)連接字符串返回給PHP的函數(shù)前,還得談?wù)剝蓚€(gè)重要的話題:內(nèi)存管理、從PHP內(nèi)部返回函數(shù)值所使用的API。

 

 

內(nèi)存管理

 

用于從堆中分配內(nèi)存的PHP API幾乎和標(biāo)準(zhǔn)C API一樣。在編寫擴(kuò)展的時(shí)候,使用下面與C對應(yīng)(因此不必再解釋)的API函數(shù):

 

emalloc(size_t size);

efree(void *ptr);

ecalloc(size_t nmemb, size_t size);

erealloc(void *ptr, size_t size);

estrdup(const char *s);

estrndup(const char *s, unsigned int length);

 

在這一點(diǎn)上,任何一位有經(jīng)驗(yàn)的C程序員應(yīng)該象這樣思考一下:“什么?標(biāo)準(zhǔn)C沒有strndup()?”是的,這是正確的,因?yàn)镚NU擴(kuò)展通常在Linux下可用。estrndup()只是PHP下的一個(gè)特殊函數(shù)。它的行為與estrdup()相似,但是可以指定字符串重復(fù)的次數(shù)(不需要結(jié)束空字符),同時(shí)是二進(jìn)制安全的。這是推薦使用estrndup()而不是estrdup()的原因。

在幾乎所有的情況下,你應(yīng)該使用這些內(nèi)存分配函數(shù)。有一些情況,即擴(kuò)展需要分配在請求中永久存在的內(nèi)存,從而不得不使用malloc(),但是除非你知道你在做什么,你應(yīng)該始終使用以上的函數(shù)。如果沒有使用這些內(nèi)存函數(shù),而相反使用標(biāo)準(zhǔn)C函數(shù)分配的內(nèi)存返回給腳本引擎,那么PHP會(huì)崩潰。

這些函數(shù)的優(yōu)點(diǎn)是:任何分配的內(nèi)存在偶然情況下如果沒有被釋放,則會(huì)在頁面請求的最后被釋放。因此,真正的內(nèi)存泄漏不會(huì)產(chǎn)生。然而,不要依賴這一機(jī)制,從調(diào)試和性能兩個(gè)原因來考慮,應(yīng)當(dāng)確保釋放應(yīng)該釋放的內(nèi)存。剩下的優(yōu)點(diǎn)是在多線程環(huán)境下性能的提高,調(diào)試模式下檢測內(nèi)存錯(cuò)誤等。

       還有一個(gè)重要的原因,你不需要檢查這些內(nèi)存分配函數(shù)的返回值是否為null。當(dāng)內(nèi)存分配失敗,它們會(huì)發(fā)出E_ERROR錯(cuò)誤,從而決不會(huì)返回到擴(kuò)展。

 

PHP函數(shù)中返回值

 

擴(kuò)展API包含豐富的用于從函數(shù)中返回值的宏。這些宏有兩種主要風(fēng)格:第一種是RETVAL_type()形式,它設(shè)置了返回值但C代碼繼續(xù)執(zhí)行。這通常使用在把控制交給腳本引擎前還希望做的一些清理工作的時(shí)候使用,然后再使用C的返回聲明 ”return” 返回到PHP;后一個(gè)宏更加普遍,其形式是RETURN_type(),他設(shè)置了返回類型,同時(shí)返回控制到PHP。下表解釋了大多數(shù)存在的宏。

 

設(shè)置返回值并且結(jié)束函數(shù)

設(shè)置返回值

宏返回類型和參數(shù)

RETURN_LONG(l)

RETVAL_LONG(l)

整數(shù)

RETURN_BOOL(b)

RETVAL_BOOL(b)

布爾數(shù)(1或0)

RETURN_NULL()

RETVAL_NULL()

NULL

RETURN_DOUBLE(d)

RETVAL_DOUBLE(d)

浮點(diǎn)數(shù)

RETURN_STRING(s, dup)

RETVAL_STRING(s, dup)

字符串。如果dup為1,引擎會(huì)調(diào)用estrdup()重復(fù)s,使用拷貝。如果dup為0,就使用s

RETURN_STRINGL(s, l, dup)

RETVAL_STRINGL(s, l, dup)

長度為l的字符串值。與上一個(gè)宏一樣,但因?yàn)閟的長度被指定,所以速度更快。

RETURN_TRUE

RETVAL_TRUE

返回布爾值true。注意到這個(gè)宏沒有括號(hào)。

RETURN_FALSE

RETVAL_FALSE

返回布爾值false。注意到這個(gè)宏沒有括號(hào)。

RETURN_RESOURCE(r)

RETVAL_RESOURCE(r)

資源句柄。

 

完成self_concat()

 

現(xiàn)在你已經(jīng)學(xué)會(huì)了如何分配內(nèi)存和從PHP擴(kuò)展函數(shù)里返回函數(shù)值,那么我們就能夠完成self_concat()的編碼:

 

/* {{{ proto string self_concat(string str, int n)

*/

PHP_FUNCTION(self_concat)

}

char *str = NULL;

int argc = ZEND_NUM_ARGS();

int str_len;

long n;

char *result; /* Points to resulting string */

char *ptr; /* Points at the next location we want to copy to */

int result_length; /* Length of resulting string */

if (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE)

return;

/* Calculate length of result */

result_length = (str_len * n);

/* Allocate memory for result */

result = (char *) emalloc(result_length + 1);

/* Point at the beginning of the result */

ptr = result;

while (n--) {

/* Copy str to the result */

memcpy(ptr, str, str_len);

/* Increment ptr to point at the next position we want to write to */

ptr += str_len;

}

/* Null terminate the result. Always null-terminate your strings

even if they are binary strings */

*ptr = '/0';

/* Return result to the scripting engine without duplicating it*/

RETURN_STRINGL(result, result_length, 0);

}

/* }}} */

 

現(xiàn)在要做的就是重新編譯一下PHP,這樣就完成了第一個(gè)PHP函數(shù)。

讓我門檢查函數(shù)是否真的工作。在最新編譯過的PHP樹下執(zhí)行[2]下面的腳本:

<?php

for ($i = 1; $i <= 3; $i++) {

print self_concat("ThisIsUseless", $i);

print "/n";

}

?>

 

你應(yīng)該得到下面的結(jié)果:

 

ThisIsUseless

ThisIsUselessThisIsUseless

ThisIsUselessThisIsUselessThisIsUseless

 

實(shí)例小結(jié)

 

你已經(jīng)學(xué)會(huì)如何編寫一個(gè)簡單的PHP函數(shù)?;氐奖菊碌拈_頭,我們提到用C編寫PHP功能函數(shù)的兩個(gè)主要的動(dòng)機(jī)。第一個(gè)動(dòng)機(jī)是用C實(shí)現(xiàn)一些算法來提高性能和擴(kuò)展功能。前一個(gè)例子應(yīng)該能夠指導(dǎo)你快速上手這種類型擴(kuò)展的開發(fā)。第二個(gè)動(dòng)機(jī)是包裹三方函數(shù)庫。我們將在下一步討論。

 

包裹第三方的擴(kuò)展

 

本節(jié)中你將學(xué)到如何編寫更有用和更完善的擴(kuò)展。該節(jié)的擴(kuò)展包裹了一個(gè)C庫,展示了如何編寫一個(gè)含有多個(gè)互相依賴的PHP函數(shù)擴(kuò)展。

 

動(dòng)機(jī) 也許最常見的PHP擴(kuò)展是那些包裹第三方C庫的擴(kuò)展。這些擴(kuò)展包括MySQL或Oracle的數(shù)據(jù)庫服務(wù)庫,libxml2的 XML技術(shù)庫,ImageMagick 或GD的圖形操縱庫。

在本節(jié)中,我們編寫一個(gè)擴(kuò)展,同樣使用腳本來生成骨架擴(kuò)展,因?yàn)檫@能節(jié)省許多工作量。這個(gè)擴(kuò)展包裹了標(biāo)準(zhǔn)C函數(shù)fopen(), fclose(), fread(), fwrite()和 feof().

擴(kuò)展使用一個(gè)被叫做資源的抽象數(shù)據(jù)類型,用于代表已打開的文件FILE*。你會(huì)注意到大多數(shù)處理比如數(shù)據(jù)庫連接、文件句柄等的PHP擴(kuò)展使用了資源類型,這是因?yàn)橐孀约簾o法直接“理解”它們。我們計(jì)劃在PHP擴(kuò)展中實(shí)現(xiàn)的C API列表如下:

 

FILE *fopen(const char *path, const char *mode);

int fclose(FILE *stream);

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

int feof(FILE *stream);

 

我們實(shí)現(xiàn)這些函數(shù),使它們在命名習(xí)慣和簡單性上符合PHP腳本。如果你曾經(jīng)向PHP社區(qū)貢獻(xiàn)過代碼,你被期望遵循一些公共習(xí)俗,而不是跟隨C庫里的API。并不是所有的習(xí)俗都寫在PHP代碼樹的CODING_STANDARDS文件里。這即是說,此功能已經(jīng)從PHP發(fā)展的很早階段即被包含在PHP中,并且與C庫API類似。PHP安裝已經(jīng)支持fopen(), fclose()和更多的PHP函數(shù)。

以下是PHP風(fēng)格的API:

 

resource file_open(string filename, string mode)

file_open()接收兩個(gè)字符串(文件名和模式),返回一個(gè)文件的資源句柄。

bool file_close(resource filehandle)

file_close()接收一個(gè)資源句柄,返回真/假指示是否操作成功。

string file_read(resource filehandle, int size)

file_read()接收一個(gè)資源句柄和讀入的總字節(jié)數(shù),返回讀入的字符串。

bool file_write(resource filehandle, string buffer)

file_write接收一個(gè)資源句柄和被寫入的字符串,返回真/假指示是否操作成功。

bool file_eof(resource filehandle)

file_eof()接收一個(gè)資源句柄,返回真/假指示是否到達(dá)文件的尾部。

 

因此,我們的函數(shù)定義文件——保存為ext/目錄下的myfile.def——內(nèi)容如下:

 

resource file_open(string filename, string mode)

bool file_close(resource filehandle)

string file_read(resource filehandle, int size)

bool file_write(resource filehandle, string buffer)

bool file_eof(resource filehandle)

 

下一步,利用ext_skel腳本在ext./ 原代碼目錄執(zhí)行下面的命令:

 

./ext_skel --extname=myfile --proto=myfile.def

 

然后,按照前一個(gè)例子的關(guān)于編譯新建立腳本的步驟操作。你會(huì)得到一些包含F(xiàn)ETCH_RESOURCE()宏行的編譯錯(cuò)誤,這樣骨架腳本就無法順利完成編譯。為了讓骨架擴(kuò)展順利通過編譯,把那些出錯(cuò)行[3]注釋掉即可。

 

資源 資源是一個(gè)能容納任何信息的抽象數(shù)據(jù)結(jié)構(gòu)。正如前面提到的,這個(gè)信息通常包括例如文件句柄、數(shù)據(jù)庫連接結(jié)構(gòu)和其他一些復(fù)雜類型的數(shù)據(jù)。

使用資源的主要原因是因?yàn)椋嘿Y源被一個(gè)集中的隊(duì)列所管理,該隊(duì)列可以在PHP開發(fā)人員沒有在腳本里面顯式地釋放時(shí)可以自動(dòng)地被釋放。

       舉個(gè)例子,考慮到編寫一個(gè)腳本,在腳本里調(diào)用mysql_connect()打開一個(gè)MySQL連接,可是當(dāng)該數(shù)據(jù)庫連接資源不再使用時(shí)卻沒有調(diào)用mysql_close()。在PHP里,資源機(jī)制能夠檢測什么時(shí)候這個(gè)資源應(yīng)當(dāng)被釋放,然后在當(dāng)前請求的結(jié)尾或通常情況下更早地釋放資源。這就為減少內(nèi)存泄漏賦予了一個(gè)“防彈”機(jī)制。如果沒有這樣一個(gè)機(jī)制,經(jīng)過幾次web請求后,web服務(wù)器也許會(huì)潛在地泄漏許多內(nèi)存資源,從而導(dǎo)致服務(wù)器當(dāng)機(jī)或出錯(cuò)。

 

注冊資源類型 如何使用資源?Zend引擎讓使用資源變地非常容易。你要做的第一件事就是把資源注冊到引擎中去。使用這個(gè)API函數(shù):

 

int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, char *type_name, int module_number)

 

這個(gè)函數(shù)返回一個(gè)資源類型id,該id應(yīng)當(dāng)被作為全局變量保存在擴(kuò)展里,以便在必要的時(shí)候傳遞給其他資源API。ld:該資源釋放時(shí)調(diào)用的函數(shù)。pld用于在不同請求中始終存在的永久資源,本章不會(huì)涉及。type_name是一個(gè)具有描述性類型名稱的字符串,module_number為引擎內(nèi)部使用,當(dāng)我們調(diào)用這個(gè)函數(shù)時(shí),我們只需要傳遞一個(gè)已經(jīng)定義好的module_number變量。

回到我們的例子中來:我們會(huì)添加下面的代碼到myfile.c原文件中。該文件包括了資源釋放函數(shù)的定義,此資源函數(shù)被傳遞給zend_register_list_destructors_ex()注冊函數(shù)(資源釋放函數(shù)應(yīng)該提早添加到文件中,以便在調(diào)用zend_register_list_destructors_ex()時(shí)該函數(shù)已被定義):

 

static void myfile_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)

{

FILE *fp = (FILE *) rsrc->ptr;

fclose(fp);

}

 

把注冊行添加到PHP_MINIT_FUNCTION()后,看起來應(yīng)該如下面的代碼:

 

PHP_MINIT_FUNCTION(myfile)

{

/* If you have INI entries, uncomment these lines

ZEND_INIT_MODULE_GLOBALS(myfile, php_myfile_init_globals,NULL);

REGISTER_INI_ENTRIES();

*/

le_myfile = zend_register_list_destructors_ex(myfile_dtor,NULL,"standard-c-file", module_number);

return SUCCESS;

}

 

l       注意到le_myfile是一個(gè)已經(jīng)被ext_skel腳本定義好的全局變量。

PHP_MINIT_FUNCTION()是一個(gè)先于模塊(擴(kuò)展)的啟動(dòng)函數(shù),是暴露給擴(kuò)展的一部分API。下表提供可用函數(shù)簡要的說明。

函數(shù)聲明宏

函數(shù)聲明宏

語義

PHP_MINIT_FUNCTION()

當(dāng)PHP被裝載時(shí),模塊啟動(dòng)函數(shù)即被引擎調(diào)用。這使得引擎做一些例如資源類型,注冊INI變量等的一次初始化。

PHP_MSHUTDOWN_FUNCTION()

當(dāng)PHP完全關(guān)閉時(shí),模塊關(guān)閉函數(shù)即被引擎調(diào)用。通常用于注銷INI條目

PHP_RINIT_FUNCTION()

在每次PHP請求開始,請求前啟動(dòng)函數(shù)被調(diào)用。通常用于管理請求前邏輯。

PHP_RSHUTDOWN_FUNCTION()

在每次PHP請求結(jié)束后,請求前關(guān)閉函數(shù)被調(diào)用。經(jīng)常應(yīng)用在清理請求前啟動(dòng)函數(shù)的邏輯。

PHP_MINFO_FUNCTION()

調(diào)用phpinfo()時(shí)模塊信息函數(shù)被呼叫,從而打印出模塊信息。

 

新建和注冊新資源 我們準(zhǔn)備實(shí)現(xiàn)file_open()函數(shù)。當(dāng)我們打開文件得到一個(gè)FILE *,我們需要利用資源機(jī)制注冊它。下面的主要宏實(shí)現(xiàn)注冊功能:

 

ZEND_REGISTER_RESOURCE(rsrc_result, rsrc_pointer, rsrc_type);

 

參考表格對宏參數(shù)的解釋

 

ZEND_REGISTER_RESOURCE 宏參數(shù)

宏參數(shù)

參數(shù)類型

rsrc_result

zval *, which should be set with the registered resource information.

zval * 設(shè)置為已注冊資源信息

rsrc_pointer

Pointer to our resource data.

資源數(shù)據(jù)指針

rsrc_type

The resource id obtained when registering the resource type.

注冊資源類型時(shí)獲得的資源id

 

文件函數(shù) 現(xiàn)在你知道了如何使用ZEND_REGISTER_RESOURCE()宏,并且準(zhǔn)備好了開始編寫file_open()函數(shù)。還有一個(gè)主題我們需要講述。

       當(dāng)PHP運(yùn)行在多線程服務(wù)器上,不能使用標(biāo)準(zhǔn)的C文件存取函數(shù)。這是因?yàn)樵谝粋€(gè)線程里正在運(yùn)行的PHP腳本會(huì)改變當(dāng)前工作目錄,因此另外一個(gè)線程里的腳本使用相對路徑則無法打開目標(biāo)文件。為了阻止這種錯(cuò)誤發(fā)生,PHP框架提供了稱作VCWD (virtual current working directory 虛擬當(dāng)前工作目錄)宏,用來代替任何依賴當(dāng)前工作目錄的存取函數(shù)。這些宏與被替代的函數(shù)具備同樣的功能,同時(shí)是被透明地處理。在某些沒有標(biāo)準(zhǔn)C函數(shù)庫平臺(tái)的情況下,VCWD框架則不會(huì)得到支持。例如,Win32下不存在chown(),就不會(huì)有相應(yīng)的VCWD_CHOWN()宏被定義。

VCWD列表

標(biāo)準(zhǔn)C庫

VCWD宏

說明

getcwd()

VCWD_GETCWD()

 

fopen()

VCWD_FOPEN

 

open()

VCWD_OPEN()

用于兩個(gè)參數(shù)的版本

open()

VCWD_OPEN_MODE()

用于三個(gè)參數(shù)的open()版本

creat()

VCWD_CREAT()

 

chdir()

VCWD_CHDIR()

 

getwd()

VCWD_GETWD()

 

realpath()

VCWD_REALPATH()

 

rename()

VCWD_RENAME()

 

stat()

VCWD_STAT()

 

lstat()

VCWD_LSTAT()

 

unlink()

VCWD_UNLINK()

 

mkdir()

VCWD_MKDIR()

 

rmdir()

VCWD_RMDIR()

 

opendir()

VCWD_OPENDIR()

 

popen()

VCWD_POPEN()

 

access()

VCWD_ACCESS()

 

utime()

VCWD_UTIME()

 

chmod()

VCWD_CHMOD()

 

chown()

VCWD_CHOWN()

 

 

編寫利用資源的第一個(gè)PHP函數(shù)

實(shí)現(xiàn)file_open()應(yīng)該非常簡單,看起來像下面的樣子:

 

PHP_FUNCTION(file_open)

{

char *filename = NULL;

char *mode = NULL;

int argc = ZEND_NUM_ARGS();

int filename_len;

int mode_len;

FILE *fp;

if (zend_parse_parameters(argc TSRMLS_CC, "ss", &filename,&filename_len, &mode, &mode_len) == FAILURE) {

return;

}

fp = VCWD_FOPEN(filename, mode);

if (fp == NULL) {

RETURN_FALSE;

}

ZEND_REGISTER_RESOURCE(return_value, fp, le_myfile);

}

 

你可能會(huì)注意到資源注冊宏的第一個(gè)參數(shù)return_value,可此地找不到它的定義。這個(gè)變量自動(dòng)的被擴(kuò)展框架定義為zval * 類型的函數(shù)返回值。先前討論的、能夠影響返回值的RETURN_LONG() 和RETVAL_BOOL()宏確實(shí)改變了return_value的值。因此很容易猜到程序注冊了我們?nèi)〉玫奈募羔榝p,同時(shí)設(shè)置return_value為該注冊資源。

 

訪問資源 需要使用下面的宏訪問資源(參看表對宏參數(shù)的解釋)

 

ZEND_FETCH_RESOURCE(rsrc, rsrc_type, passed_id, default_id,

resource_type_name, resource_type);

 

ZEND_FETCH_RESOURCE 宏參數(shù)

參數(shù)

含義

rsrc

資源值保存到的變量名。它應(yīng)該和資源有相同類型。

rsrc_type

rsrc的類型,用于在內(nèi)部把資源轉(zhuǎn)換成正確的類型

passed_id

尋找的資源值(例如zval **)

default_id

如果該值不為-1,就使用這個(gè)id。用于實(shí)現(xiàn)資源的默認(rèn)值。

resource_type_name

資源的一個(gè)簡短名稱,用于錯(cuò)誤信息。

resource_type

注冊資源的資源類型id

使用這個(gè)宏,我們現(xiàn)在能夠?qū)崿F(xiàn)file_eof():

 

PHP_FUNCTION(file_eof)

      

int argc = ZEND_NUM_ARGS();

zval *filehandle = NULL;

FILE *fp;

if (zend_parse_parameters(argc TSRMLS_CC, "r", &filehandle) ==FAILURE) {

return;

       }

ZEND_FETCH_RESOURCE(fp, FILE *, &filehandle, -1, "standard-c-file",le_myfile);

if (fp == NULL) {

RETURN_FALSE;

       }

if (feof(fp) <= 0) {

/* Return eof also if there was an error */

RETURN_TRUE;

       }

RETURN_FALSE;

      

 

刪除一個(gè)資源 通常使用下面這個(gè)宏刪除一個(gè)資源:

 

int zend_list_delete(int id)

 

傳遞給宏一個(gè)資源id,返回SUCCESS或者FAILURE。如果資源存在,優(yōu)先從Zend資源列隊(duì)中刪除,該過程中會(huì)調(diào)用該資源類型的已注冊資源清理函數(shù)。因此,在我們的例子中,不必取得文件指針,調(diào)用fclose()關(guān)閉文件,然后再刪除資源。直接把資源刪除掉即可。

使用這個(gè)宏,我們能夠?qū)崿F(xiàn)file_close():

 

PHP_FUNCTION(file_close)

{

int argc = ZEND_NUM_ARGS();

zval *filehandle = NULL;

if (zend_parse_parameters(argc TSRMLS_CC, "r", &filehandle) == FAILURE) {

return;

       }

if (zend_list_delete(Z_RESVAL_P(filehandle)) == FAILURE) {

RETURN_FALSE;

       }

RETURN_TRUE;

       }

 

你肯定會(huì)問自己Z_RESVAL_P()是做什么的。當(dāng)我們使用zend_parse_parameters()從參數(shù)列表中取得資源的時(shí)候,得到的是zval的形式。為了獲得資源id,我們使用Z_RESVAL_P()宏得到id,然后把id傳遞給zend_list_delete()。

       有一系列宏用于訪問存儲(chǔ)于zval值(參考表的宏列表)。盡管在大多數(shù)情況下zend_parse_parameters()返回與c類型相應(yīng)的值,我們?nèi)韵M苯犹幚韟val,包括資源這一情況。

 

Zval訪問宏

 

訪問對象

C 類型

Z_LVAL, Z_LVAL_P,

Z_LVAL_PP

 

整型值

long

Z_BVAL, Z_BVAL_P,

Z_BVAL_PP

 

布爾值

zend_bool

Z_DVAL, Z_DVAL_P,

Z_DVAL_PP

 

浮點(diǎn)值

double

Z_STRVAL, Z_STRVAL_P,

Z_STRVAL_PP

 

字符串值

char *

Z_STRLEN, Z_STRLEN_P, Z_STRLEN_PP

 

字符串長度值

int

Z_RESVAL, Z_RESVAL_P,Z_RESVAL_PP

資源值

long

Z_ARRVAL, Z_ARRVAL_P,

Z_ARRVAL_PP

 

聯(lián)合數(shù)組

HashTable *

Z_TYPE, Z_TYPE_P,

Z_TYPE_PP

 

Zval類型

Enumeration (IS_NULL, IS_LONG, IS_DOUBLE, IS_STRING, IS_ARRAY, IS_OBJECT, IS_BOOL, IS_RESOURCE)

 

Z_OBJPROP,

Z_OBJPROP_P,

Z_OBJPROP_PP

 

對象屬性hash(本章不會(huì)談到)

HashTable *

Z_OBJCE, Z_OBJCE_P,

Z_OBJCE_PP

 

對象的類信息(本章不會(huì)談到)

zend_class_entry

 

用于訪問zval值的宏        所有的宏都有三種形式:一個(gè)是接受zval s,另外一個(gè)接受zval *s,最后一個(gè)接受zval **s。它們的區(qū)別是在命名上,第一個(gè)沒有后綴,zval *有后綴_P(代表一個(gè)指針),最后一個(gè) zval **有后綴_PP(代表兩個(gè)指針)。

       現(xiàn)在,你有足夠的信息來獨(dú)立完成 file_read()和 file_write()函數(shù)。這里是一個(gè)可能的實(shí)現(xiàn):

 

PHP_FUNCTION(file_read)

{

int argc = ZEND_NUM_ARGS();

long size;

zval *filehandle = NULL;

FILE *fp;

char *result;

size_t bytes_read;

if (zend_parse_parameters(argc TSRMLS_CC, "rl", &filehandle,&size) == FAILURE) {

return;

}

ZEND_FETCH_RESOURCE(fp, FILE *, &filehandle, -1, "standard-cfile", le_myfile);

result = (char *) emalloc(size+1);

bytes_read = fread(result, 1, size, fp);

result[bytes_read] = '/0';

RETURN_STRING(result, 0);

}

 

PHP_FUNCTION(file_write)

{

char *buffer = NULL;

int argc = ZEND_NUM_ARGS();

int buffer_len;

zval *filehandle = NULL;

FILE *fp;

if (zend_parse_parameters(argc TSRMLS_CC, "rs", &filehandle,&buffer, &buffer_len) == FAILURE) {

return;

}

ZEND_FETCH_RESOURCE(fp, FILE *, &filehandle, -1, "standard-cfile", le_myfile);

if (fwrite(buffer, 1, buffer_len, fp) != buffer_len) {

RETURN_FALSE;

}

RETURN_TRUE;

}

測試擴(kuò)展 你現(xiàn)在可以編寫一個(gè)測試腳本來檢測擴(kuò)展是否工作正常。下面是一個(gè)示例腳本,該腳本打開文件test.txt,輸出文件類容到標(biāo)準(zhǔn)輸出,建立一個(gè)拷貝test.txt.new。

 

<?php

$fp_in = file_open("test.txt", "r") or die("Unable to open input file/n");

$fp_out = file_open("test.txt.new", "w") or die("Unable to open output file/n");

while (!file_eof($fp_in)) {

$str = file_read($fp_in, 1024);

print($str);

file_write($fp_out, $str);

}

file_close($fp_in);

file_close($fp_out);

?>

 

 

全局變量

你可能希望在擴(kuò)展里使用全局C變量,無論是獨(dú)自在內(nèi)部使用或訪問php.ini文件中的INI擴(kuò)展注冊標(biāo)記(INI在下一節(jié)中討論)。因?yàn)镻HP是為多線程環(huán)境而設(shè)計(jì),所以不必定義全局變量。PHP提供了一個(gè)創(chuàng)建全局變量的機(jī)制,可以同時(shí)應(yīng)用在線程和非線程環(huán)境中。我們應(yīng)當(dāng)始終利用這個(gè)機(jī)制,而不要自主地定義全局變量。用一個(gè)宏訪問這些全局變量,使用起來就像普通全局變量一樣。

       用于生成myfile工程骨架文件的ext_skel腳本創(chuàng)建了必要的代碼來支持全局變量。通過檢查php_myfile.h文件,你應(yīng)當(dāng)發(fā)現(xiàn)類似下面的被注釋掉的一節(jié),

 

ZEND_BEGIN_MODULE_GLOBALS(myfile)

int global_value;

char *global_string;

ZEND_END_MODULE_GLOBALS(myfile)

 

你可以把這一節(jié)的注釋去掉,同時(shí)添加任何其他全局變量于這兩個(gè)宏之間。文件后部的幾行,骨架腳本自動(dòng)地定義一個(gè)MYFILE_G(v)宏。這個(gè)宏應(yīng)當(dāng)被用于所有的代碼,以便訪問這些全局變量。這就確保在多線程環(huán)境中,訪問的全局變量僅是一個(gè)線程的拷貝,而不需要互斥的操作。

       為了使全局變量有效,最后需要做的是把myfile.c:

 

ZEND_DECLARE_MODULE_GLOBALS(myfile)

 

注釋去掉。

你也許希望在每次PHP請求的開始初始化全局變量。另外,做為一個(gè)例子,全局變量已指向了一個(gè)已分配的內(nèi)存,在每次PHP請求結(jié)束時(shí)需要釋放內(nèi)存。為了達(dá)到這些目的,全局變量機(jī)制提供了一個(gè)特殊的宏,用于注冊全局變量的構(gòu)造和析構(gòu)函數(shù)(參考表對宏參數(shù)的說明):

 

ZEND_INIT_MODULE_GLOBALS(module_name, globals_ctor, globals_dtor)

 

 ZEND_INIT_MODULE_GLOBALS 宏參數(shù)

參數(shù)

含義

module_name

 

與傳遞給ZEND_BEGIN_MODULE_GLOBALS()宏相同的擴(kuò)展名稱。

globals_ctor

 

構(gòu)造函數(shù)指針。在myfile擴(kuò)展里,函數(shù)原形與void php_myfile_init_globals(zend_myfile_globals *myfile_globals)類似

globals_dtor

 

析構(gòu)函數(shù)指針。例如,php_myfile_init_globals(zend_myfile_globals *myfile_globals)

 

你可以在myfile.c里看到如何使用構(gòu)造函數(shù)和ZEND_INIT_MODULE_GLOBALS()宏的示例。

 

添加自定義INI指令

 

       INI文件(php.ini)的實(shí)現(xiàn)使得PHP擴(kuò)展注冊和監(jiān)聽各自的INI條目。如果這些INI條目由php.ini、Apache的htaccess或其他配置方法來賦值,注冊的INI變量總是更新到正確的值。整個(gè)INI框架有許多不同的選項(xiàng)以實(shí)現(xiàn)其靈活性。我們涉及一些基本的(也是個(gè)好的開端),借助本章的其他材料,我們就能夠應(yīng)付日常開發(fā)工作的需要。

通過在PHP_INI_BEGIN()/PHP_INI_END()宏之間的STD_PHP_INI_ENTRY()宏注冊PHP INI指令。例如在我們的例子里,myfile.c中的注冊過程應(yīng)當(dāng)如下:

 

PHP_INI_BEGIN()

STD_PHP_INI_ENTRY("myfile.global_value", "42", PHP_INI_ALL, OnUpdateInt, global_value, zend_myfile_globals, myfile_globals)

STD_PHP_INI_ENTRY("myfile.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_myfile_globals, myfile_globals)

PHP_INI_END()

 

除了STD_PHP_INI_ENTRY()其他宏也能夠使用,但這個(gè)宏是最常用的,可以滿足大多數(shù)需要(參看表對宏參數(shù)的說明):

 

STD_PHP_INI_ENTRY(name, default_value, modifiable, on_modify, property_name, struct_type, struct_ptr)

 

STD_PHP_INI_ENTRY 宏參數(shù)表

 

參數(shù)

含義

name

INI條目名

default_value

如果沒有在INI文件中指定,條目的默認(rèn)值。默認(rèn)值始終是一個(gè)字符串。

modifiable

設(shè)定在何種環(huán)境下INI條目可以被更改的位域。可以的值是:

• PHP_INI_SYSTEM. 能夠在php.ini或http.conf等系統(tǒng)文件更改

• PHP_INI_PERDIR. 能夠在 .htaccess中更改

• PHP_INI_USER. 能夠被用戶腳本更改

• PHP_INI_ALL.  能夠在所有地方更改

on_modify

處理INI條目更改的回調(diào)函數(shù)。你不需自己編寫處理程序,使用下面提供的函數(shù)。包括:

• OnUpdateInt

• OnUpdateString

• OnUpdateBool

• OnUpdateStringUnempty

• OnUpdateReal

property_name

應(yīng)當(dāng)被更新的變量名

struct_type

變量駐留的結(jié)構(gòu)類型。因?yàn)橥ǔJ褂萌肿兞繖C(jī)制,所以這個(gè)類型自動(dòng)被定義,類似于zend_myfile_globals。

struct_ptr

全局結(jié)構(gòu)名。如果使用全局變量機(jī)制,該名為myfile_globals。

 

最后,為了使自定義INI條目機(jī)制正常工作,你需要分別去掉PHP_MINIT_FUNCTION(myfile)中的REGISTER_INI_ENTRIES()調(diào)用和PHP_MSHUTDOWN_FUNCTION(myfile)中的UNREGISTER_INI_ENTRIES()的注釋。

       訪問兩個(gè)示例全局變量中的一個(gè)與在擴(kuò)展里編寫MYFILE_G(global_value) 和MYFILE_G(global_string)一樣簡單。

       如果你把下面的兩行放在php.ini中,MYFILE_G(global_value)的值會(huì)變?yōu)?9。

 

; php.ini – The following line sets the INI entry myfile.global_value to 99.

myfile.global_value = 99

 

線程安全資源管理宏

 

現(xiàn)在,你肯定注意到以TSRM(線程安全資源管理器)開頭的宏隨處使用。這些宏提供給擴(kuò)展擁有獨(dú)自的全局變量的可能,正如前面提到的。

當(dāng)編寫PHP擴(kuò)展時(shí),無論是在多進(jìn)程或多線程環(huán)境中,都是依靠這一機(jī)制訪問擴(kuò)展自己的全局變量。如果使用全局變量訪問宏(例如MYFILE_G()宏),需要確保TSRM上下文信息出現(xiàn)在當(dāng)前函數(shù)中。基于性能的原因,Zend引擎試圖把這個(gè)上下文信息作為參數(shù)傳遞到更多的地方,包括PHP_FUNCTION()的定義。正因?yàn)檫@樣,在PHP_FUNCTION()內(nèi)當(dāng)編寫的代碼使用訪問宏(例如MYFILE_G()宏)時(shí),不需要做任何特殊的聲明。然而,如果PHP函數(shù)調(diào)用其他需要訪問全局變量的C函數(shù),要么把上下文作為一個(gè)額外的參數(shù)傳遞給C函數(shù),要么提取上下文(要慢點(diǎn))。

       在需要訪問全局變量的代碼塊開頭使用TSRMLS_FETCH()來提取上下文。例如:

 

void myfunc()

{

TSRMLS_FETCH();

MYFILE_G(myglobal) = 2;

}

 

如果希望讓代碼更加優(yōu)化,更好的辦法是直接傳遞上下文給函數(shù)(正如前面敘述的,PHP_FUNCTION()范圍內(nèi)自動(dòng)可用)。可以使用TSRMLS_C(C表示調(diào)用Call)和TSRMLS_CC(CC邊式調(diào)用Call和逗號(hào)Comma)宏。前者應(yīng)當(dāng)用于僅當(dāng)上下文作為一個(gè)單獨(dú)的參數(shù),后者應(yīng)用于接受多個(gè)參數(shù)的函數(shù)。在后一種情況中,因?yàn)楦鶕?jù)取名,逗號(hào)在上下文的前面,所以TSRMLS_CC不能是第一個(gè)函數(shù)參。

在函數(shù)原形中,可以分別使用TSRMLS_D和TSRMLS_DC宏聲名正在接收上下文。

       下面是前一例子的重寫,利用了參數(shù)傳遞上下文。

 

void myfunc(TSRMLS_D)

{

MYFILE_G(myglobal) = 2;

}

PHP_FUNCTION(my_php_function)

{

myfunc(TSRMLS_C);

}

 

 結(jié)

 

現(xiàn)在,你已經(jīng)學(xué)到了足夠的東西來創(chuàng)建自己的擴(kuò)展。本章講述了一些重要的基礎(chǔ)來編寫和理解PHP擴(kuò)展。Zend引擎提供的擴(kuò)展API相當(dāng)豐富,使你能夠開發(fā)面向?qū)ο蟮臄U(kuò)展。幾乎沒有文檔談幾許多高級(jí)特性。當(dāng)然,依靠本章所學(xué)的基礎(chǔ)知識(shí),你可以通過瀏覽現(xiàn)有的原碼學(xué)到很多。

       更多關(guān)于信息可以在PHP手冊的擴(kuò)展PHP章節(jié)http://www.php.net/manual/en/zend.php中找到。另外,你也可以考慮加入PHP開發(fā)者郵件列表internals@ lists.php.net,該郵件列表圍繞開發(fā)PHP 本身。你還可以查看一下新的擴(kuò)展生成工具——PECL_Gen(http://pear.php.net/package/PECL_Gen),這個(gè)工具正在開發(fā)之中,比起本章使用的ext_skel有更多的特性。

 

詞匯表

 

binary safe 二進(jìn)制安全

context 上下文

extensions 擴(kuò)展

entry 條目

skeleton 骨架

Thread-Safe Resource Manager TSRM 線程安全資源管理器

 

Contact info:

Email: taft # wjl.cn



[1] 可參考譯者寫的

[2] 譯者:可以使用phpcli程序在控制臺(tái)里執(zhí)行php文件。

[3] 譯者:可以查看到生成的FETCH_RESOURCE()宏參數(shù)是一些’???’。