使用PHP擴(kuò)展的原因:
①如果應(yīng)用注重效率,使用非常復(fù)雜的算法,推薦使用PHP擴(kuò)展。
②有些系統(tǒng)調(diào)用PHP不能直接訪問(如Linux的fork()函數(shù)創(chuàng)建進(jìn)程),需要編寫成PHP擴(kuò)展。
③應(yīng)用不想暴露關(guān)鍵代碼,可以創(chuàng)建擴(kuò)展使用。
準(zhǔn)備工作
一:了解PHP源碼目錄
網(wǎng)上下載下來PHP 5.4版本源代碼,目錄結(jié)構(gòu)如下:
php-5.4.30
|____build --和編譯有關(guān)的目錄,里面包括wk,awk和sh腳本用于編譯處理,其中m4文件是linux下編譯程序自動(dòng)生成的文件,可以使用buildconf命令操作具體的配置文件。
|____ext --擴(kuò)展庫代碼,例如Mysql,gd,zlib,xml,iconv 等我們熟悉的擴(kuò)展庫,ext_skel是linux下擴(kuò)展生成腳本,windows下使用ext_skel_win32.php。
|____main --主目錄,包含PHP的主要宏定義文件,php.h包含絕大部分PHP宏及PHP API定義。
|____netware --網(wǎng)絡(luò)目錄,只有sendmail_nw.h和start.c,分別定義SOCK通信所需要的頭文件和具體實(shí)現(xiàn)。
|____pear --擴(kuò)展包目錄,PHP Extension and Application Repository。
|____sapi --各種服務(wù)器的接口調(diào)用,如Apache,IIS等。
|____scripts --linux下的腳本目錄。
|____tests --測試腳本目錄,主要是phpt腳本,由--TEST--,--POST--,--FILE--,--EXPECT--組成,需要初始化可添加--INI--部分。
|____TSRM --線程安全資源管理器,Thread Safe Resource Manager保證在單線程和多線程模型下的線程安全和代碼一致性。
|____win32 --Windows下編譯PHP 有關(guān)的腳本。
|____Zend --包含Zend引擎的所有文件,包括PHP的生命周期,內(nèi)存管理,變量定義和賦值以及函數(shù)宏定義等等。
二:自動(dòng)構(gòu)建工具
本篇針對(duì)Linux環(huán)境下創(chuàng)建PHP擴(kuò)展,使用擴(kuò)展自動(dòng)構(gòu)建工具為ext_skel,Windows下使用ext_skel_win32.php,構(gòu)建方式略有不同,其余開發(fā)無差別。
構(gòu)建PHP擴(kuò)展的步驟如下(不唯一):
①cd php_src/ext
②./ext_skel --extname=XXX
此時(shí)當(dāng)前目錄下會(huì)生成一個(gè)名為XXX的文件夾
③cd XXX/
④vim config.m4
會(huì)有這段文字:
dnl If your extension references something external, use with:
dnl PHP_ARG_WITH(say, for say support,
dnl Make sure that the comment is aligned:
dnl [ --with-say Include say support])
dnl Otherwise use enable:
dnl PHP_ARG_ENABLE(say, whether to enable say support,
dnl Make sure that the comment is aligned:
dnl [ --enable-say Enable say support])
其中,dnl 是注釋符號(hào)。上面的代碼說,如果你所編寫的擴(kuò)展如果依賴其它的擴(kuò)展或者lib庫,需要去掉PHP_ARG_WITH相關(guān)代碼的注釋。否則,去掉 PHP_ARG_ENABLE 相關(guān)代碼段的注釋。本篇的擴(kuò)展不依賴其他擴(kuò)展,故修改為:
dnl If your extension references something external, use with:
dnl PHP_ARG_WITH(say, for say support,
dnl Make sure that the comment is aligned:
dnl [ --with-say Include say support])
dnl Otherwise use enable:
PHP_ARG_ENABLE(say, whether to enable say support,
Make sure that the comment is aligned:
[ --enable-XXX Enable say support])
⑤在XXX.c中具體實(shí)現(xiàn)
⑥編譯安裝
phpize
./configure --with-php-config=php_path/bin/php-config
make && make install
⑦修改php.ini文件
增加
[XXX]
extension = XXX.so
三:了解PHP生命周期
任何一個(gè)PHP實(shí)例都會(huì)經(jīng)過Module init、Request init、Request shutdown和Module shutdown四個(gè)過程。
1.Module init
在所有請(qǐng)求到達(dá)前發(fā)生,例如啟動(dòng)Apache服務(wù)器,PHP解釋器隨之啟動(dòng),相關(guān)的各個(gè)模塊(Redis、Mysql等)的MINIT方法被調(diào)用。僅被調(diào)用一次。創(chuàng)建XXX擴(kuò)展后,相應(yīng)的XXX.c文件中將自動(dòng)生成該方法:
PHP_MINIT_FUNCTION(XXX) {
return SUCCESS;
}
2.Request init
每個(gè)請(qǐng)求達(dá)到時(shí)都被觸發(fā)。SAPI層將控制權(quán)交由PHP層,PHP初始化本次請(qǐng)求執(zhí)行腳本所需的環(huán)境變量,函數(shù)列表等,調(diào)用所有模塊的RINIT函數(shù)。XXX.c中對(duì)應(yīng)函數(shù)如下:
PHP_RINIT_FUNCTION(XXX){
return SUCCESS;
}
3.Request shutdown
每個(gè)請(qǐng)求結(jié)束,PHP就會(huì)自動(dòng)清理程序,順序調(diào)用各個(gè)模塊的RSHUTDOWN方法,清除程序運(yùn)行期間的符號(hào)表。典型的RSHUTDOWN方法如:
PHP_RSHUTDOWN_FUNCTION(XXX){
return SUCCESS;
}
4.Module shutdown
所有請(qǐng)求處理完畢后,SAPI也關(guān)閉了(即服務(wù)器關(guān)閉),PHP調(diào)用各個(gè)模塊的MSHUTDOWN方法釋放內(nèi)存。
PHP_MSHUTDOWN_FUNCTION(XXX){
return SUCCESS;
}
PHP的生命周期常見如下幾種
①單進(jìn)程SAPI生命周期
②多進(jìn)程SAPI生命周期
③多線程SAPI聲明周期
這與PHP的運(yùn)行模式有很大關(guān)系,常見的運(yùn)行模式有CLI、CGI、FastCGI和mod_php。
①CLI模式——單進(jìn)程SAPI生命周期
所謂CLI模式,即在終端界面通過php+文件名的方式執(zhí)行PHP文件

輸入命令后,依次調(diào)用MINIT,RINIT,RSHUTDOWN,MSHUTDOWN即完成生命周期,一次只處理一個(gè)請(qǐng)求。
②CGI模式——單進(jìn)程SAPI生命周期
和CLI模式一樣,請(qǐng)求到達(dá)時(shí),為每個(gè)請(qǐng)求fork一個(gè)進(jìn)程,一個(gè)進(jìn)程只對(duì)一個(gè)請(qǐng)求做出響應(yīng),請(qǐng)求結(jié)束后,進(jìn)程也就結(jié)束了。
與CLI模式不同的是,CGI可以看作是規(guī)定了Web Server與PHP的交流規(guī)則,相當(dāng)于執(zhí)行response = exec("php -f xxx.php -url=xxx -cookie=xxx -xxx=xxx")。
③FastCGI模式——多進(jìn)程SAPI生命周期
CGI模式存在明顯缺點(diǎn),每個(gè)進(jìn)程處理一個(gè)請(qǐng)求及結(jié)束,新請(qǐng)求過來需要重新加載php.ini,調(diào)用MINIT等函數(shù)。FastCGI相當(dāng)于可以執(zhí)行多個(gè)請(qǐng)求的CGI,處理完一個(gè)請(qǐng)求后進(jìn)程不結(jié)束,等待下一個(gè)請(qǐng)求到來。
服務(wù)啟動(dòng)時(shí),F(xiàn)astCGI先啟動(dòng)多個(gè)子進(jìn)程等待處理請(qǐng)求,避免了CGI模式請(qǐng)求到來時(shí)fork()進(jìn)程(即fork-and-execute),提高效率。

④mod_php模式——多進(jìn)程SAPI生命周期
該模式將PHP嵌入到Apache中,相當(dāng)于給Apache增加了解析PHP的功能。PHP隨服務(wù)器的啟動(dòng)而啟動(dòng),兩者之間存在從屬關(guān)系。
證明:
CGI模式下,修改php.ini無需重啟服務(wù)器,每個(gè)請(qǐng)求結(jié)束后,進(jìn)程自動(dòng)結(jié)束,新請(qǐng)求到來時(shí)會(huì)重新讀取php.ini文件創(chuàng)建新進(jìn)程。
mod_php下,進(jìn)程是啟動(dòng)即創(chuàng)建,只有結(jié)束現(xiàn)有進(jìn)程,重新啟動(dòng)服務(wù)器讀取PHP配置創(chuàng)建新進(jìn)程,修改才有效。
多線程SAPI模式
多線程模式和多進(jìn)程模式的某個(gè)進(jìn)程類似,在整個(gè)生命周期中會(huì)并行重復(fù)著請(qǐng)求開始,請(qǐng)求結(jié)束的環(huán)節(jié)。
只有一個(gè)服務(wù)器進(jìn)程運(yùn)行,但同時(shí)運(yùn)行多個(gè)線程,優(yōu)點(diǎn)是節(jié)省資源開銷,MINIT和MSHUTDOWN只需在Web Server啟動(dòng)和結(jié)束時(shí)執(zhí)行一次。由于線程的特質(zhì),使得各個(gè)請(qǐng)求之間共享數(shù)據(jù)成為可能。

四:PHP內(nèi)核中的變量
PHP變量的弱類型實(shí)現(xiàn)在之前的文章中講到,可以參讀:http://www.itdecent.cn/p/ef0c91be06a0 PHP的實(shí)現(xiàn)方式即PHP變量在內(nèi)核中的存儲(chǔ)。
PHP提供了一系列內(nèi)核變量的訪問宏。推薦使用它們?cè)O(shè)置和訪問PHP的變量類型和值。
變量類型:
Z_TYPE(zval) 可以獲取和設(shè)置變量類型
Z_TYPE(zval)函數(shù)返回變量的類型,PHP變量類型有:
IS_NULL(空類型),IS_LONG(整型),IS_DOUBLE(浮點(diǎn)型),IS_STRING(字符串),
IS_ARRAY(數(shù)組類型),IS_OBJECT(對(duì)象類型),IS_BOOL(布爾類型),IS_RESOURCE(資源類型)
可以通過
Z_TYPE(zval) = IS_STRING的方式直接設(shè)置變量類型
變量值對(duì)應(yīng)的訪問宏:
整數(shù)類型 Z_LVAL(zval) 對(duì)應(yīng)zval的實(shí)體;Z_VAL_P(&zval) 對(duì)應(yīng)結(jié)構(gòu)體的指針;Z_VAL_PP(&&zval) 對(duì)應(yīng)結(jié)構(gòu)體的二級(jí)指針
浮點(diǎn)數(shù)類型 Z_DVAL(zval) Z_DVAL_P(&zval) Z_DVAL_PP(&&zval)
布爾類型 Z_BVAL(zval) Z_BVAL_P(&zval) Z_BVAL_PP(&&zval)
字符串類型
獲取值:Z_STRVAL(zval) Z_STRVAL_P(&zval) Z_STRVAL_PP(&&zval)
獲取長度:Z_STRLEN(zval) Z_STRLEN_P(&zval) Z_STRLEN_PP(&&zval)
數(shù)組類型 Z_ARRVAL(zval) Z_ARRVAL_P(&zval) Z_ARRVAL_PP(&&zval)
資源類型 Z_RESVAL(zval) Z_RESVAL_P(&zval) Z_RESVAL_PP(&&zval)
五:了解Zend API
1.Zend引擎
Zend引擎就是腳本語言引擎(解釋器+虛擬機(jī)),負(fù)責(zé)解析、翻譯和執(zhí)行PHP腳本。其工作流程大致如下:
①Zend Engine Compiler編譯PHP腳本為Opcode
②Opcode由Zend Engine Executor解析執(zhí)行,期間Zend Engine Executor負(fù)責(zé)調(diào)用使用到的PHP extension
2.Zend內(nèi)存管理
使用C語言開發(fā)PHP擴(kuò)展,需要注意內(nèi)存管理。忘記釋放內(nèi)存將造成內(nèi)存泄漏,釋放多次則產(chǎn)生系統(tǒng)錯(cuò)誤。Zend引擎提供了一些內(nèi)存管理的接口,使用這些接口申請(qǐng)內(nèi)存交由Zend管理。
常見接口:
emalloc(size_t size) 申請(qǐng)size大小的內(nèi)存
efree(void *ptr) 釋放ptr指向的內(nèi)存塊
estrdup(char *str) 申請(qǐng)str大小的內(nèi)存,并將str內(nèi)容復(fù)制進(jìn)去
estrndup(char *str, int slen) 同上,但指定長度復(fù)制
ecalloc(size_t numOfElem, size_t sizeOfElem) 復(fù)制numOfElem個(gè)sizeOfElem大小的內(nèi)存塊
erealloc(void *ptr, size_t nsize) ptr指向內(nèi)存塊的大小擴(kuò)大到nsize
內(nèi)存管理申請(qǐng)的所有內(nèi)存,將在腳本執(zhí)行完畢和處理請(qǐng)求終止時(shí)被釋放。
3.PHP擴(kuò)展架構(gòu)
使用準(zhǔn)備工作(二)中命令生成基本架構(gòu)后,生成的對(duì)應(yīng)目錄中會(huì)有兩個(gè)文件,php_XXX.h和XXX.c,其中php_XXX.h文件用于聲明擴(kuò)展的一些基本信息和實(shí)現(xiàn)的函數(shù),注意,只是聲明。具體的實(shí)現(xiàn)在XXX.c中。
php_XXX.h大致結(jié)構(gòu)如下:
#ifndef PHP_XXX_H
#define PHP_XXX_H
extern zend_module_entry php_list_module_entry;
#define phpext_php_list_ptr &php_list_module_entry
#define PHP_XXX_VERSION "0.1.0"
#ifdef PHP_WIN32
# define PHP_XXX_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
# define PHP_XXX_API __attribute__ ((visibility("default")))
#else
# define PHP_XXX_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
PHP_MINIT_FUNCTION(XXX);
PHP_MSHUTDOWN_FUNCTION(XXX);
PHP_RINIT_FUNCTION(XXX);
PHP_RSHUTDOWN_FUNCTION(XXX);
PHP_MINFO_FUNCTION(XXX);
PHP_FUNCTION(confirm_XXX_compiled);
#ifdef ZTS
#define PHP_LIST_G(v) TSRMG(php_list_globals_id, zend_php_list_globals *, v)
#else
#define PHP_LIST_G(v) (php_list_globals.v)
#endif
#endif
大致信息有版本號(hào),MINIT、RINIT、RSHUTDOWN、MSHUTDOWN函數(shù)等,如果聲明自定義函數(shù),可以在之后以PHP_FUNCTION(XXX);的方式聲明函數(shù),并在XXX.c中具體實(shí)現(xiàn)。
XXX.c內(nèi)容如下:
---------------------------------------頭文件--------------------------------------
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_XXX.h"
static int le_XXX;
---------------------------------------Zend函數(shù)快--------------------------------------
const zend_function_entry XXX_functions[] = {
PHP_FE(confirm_php_list_compiled, NULL) /* For testing, remove later. */
PHP_FE_END /* Must be the last line in php_list_functions[] */
};
---------------------------------------Zend模塊--------------------------------------
zend_module_entry XXX_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"XXX",
XXX_functions,
PHP_MINIT(XXX),
PHP_MSHUTDOWN(XXX),
PHP_RINIT(XXX), /* Replace with NULL if there's nothing to do at request start */
PHP_RSHUTDOWN(XXX), /* Replace with NULL if there's nothing to do at request end */
PHP_MINFO(XXX),
#if ZEND_MODULE_API_NO >= 20010901
PHP_XXX_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};
---------------------------------------實(shí)現(xiàn)get_module函數(shù)--------------------------------------
#ifdef COMPILE_DL_XXX
ZEND_GET_MODULE(XXX)
#endif
---------------------------------------生命周期函數(shù)--------------------------------------
PHP_MINIT_FUNCTION(XXX)
{
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(XXX)
{
return SUCCESS;
}
PHP_RINIT_FUNCTION(XXX)
{
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(XXX)
{
return SUCCESS;
}
---------------------------------------導(dǎo)出函數(shù)--------------------------------------
PHP_FUNCTION(confirm_XXX_compiled)
{
char *arg = NULL;
int arg_len, len;
char *strg;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
return;
}
len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "php_list", arg);
RETURN_STRINGL(strg, len, 0);
}
---------------------------------------負(fù)責(zé)擴(kuò)展info顯示--------------------------------------
PHP_MINFO_FUNCTION(XXX)
{
php_info_print_table_start();
php_info_print_table_header(2, "XXX support", "enabled");
php_info_print_table_end();
}
①擴(kuò)展頭文件
所有擴(kuò)展務(wù)必包含的頭文件有且只有一個(gè)——php.h,以使用PHP定義的各種宏和API。如果在php_XXX.h中聲明了擴(kuò)展相關(guān)的宏和函數(shù),需要將其引入。
②導(dǎo)出函數(shù)
先說導(dǎo)出函數(shù),方便理解Zend函數(shù)塊。PHP能夠調(diào)用擴(kuò)展中的類和方法都是通過導(dǎo)出函數(shù)實(shí)現(xiàn)。導(dǎo)出函數(shù)就是按照PHP內(nèi)核要求編寫的函數(shù),形式如下:
void zif_extfunction(){ //extfunction即為擴(kuò)展中實(shí)現(xiàn)的函數(shù)
int ht; //函數(shù)參數(shù)的個(gè)數(shù)
zval *return_value; //保存函數(shù)的返回值
zval *this_ptr; //指向函數(shù)所在對(duì)象
int return_value_used; //函數(shù)返回值腳本是否使用,0——不使用;1——使用
zend_executor_globals *executor_globals; //指向Zend引擎的全局設(shè)置
}
有如上定義,PHP腳本中即可使用擴(kuò)展函數(shù)
<?php
extfunction();
?>
由于導(dǎo)出函數(shù)格式固定,Zend引擎通過PHP_FUNCTION()宏聲明
PHP_FUNCTION(extfunction);
即可產(chǎn)生之前代碼塊中的導(dǎo)出函數(shù)結(jié)構(gòu)體。
③Zend函數(shù)塊
作用是將編寫的函數(shù)引入Zend引擎,通過zend_function_entry結(jié)構(gòu)體引入。zend_function_entry結(jié)構(gòu)體聲明如下:
typedef struct _zend_function_entry{
char *fname; //指定在PHP腳本里定義的函數(shù)名
void (*handler)(INTERNAL_FUNCTION_PARAMETERS); //指向?qū)С龊瘮?shù)的句柄
unsigned char *func_arg_types; //標(biāo)示一些參數(shù)是否強(qiáng)制按引用方式傳遞,通常設(shè)為NULL
} zend_function_entry;
Zend引擎通過zend_function_entry數(shù)組將導(dǎo)出函數(shù)引入內(nèi)部。方式:
zend_function_entry XXX_functions[] = {
PHP_FE(confirm_php_list_compiled, NULL) /* For testing, remove later. */
PHP_FE_END /* Must be the last line in php_list_functions[] */
};
PHP_FE宏會(huì)把zend_function_entry結(jié)構(gòu)補(bǔ)充完整。
PHP_FE(extfunction); ===> ("extfunction", zif_extfunction, NULL);
PHP_FE_END是告知Zend引擎Zend函數(shù)塊到此為止,有的版本可以使用{NULL, NULL, NULL}的方式。但推薦使用本文方式以兼容。
④Zend模塊聲明
Zend模塊包含所有需要向Zend引擎提供的擴(kuò)展模塊信息,底層由zend_module_entry結(jié)構(gòu)體大體實(shí)現(xiàn)
typedef struct _zend_module_entry{
unsigned short size; ---|
unsigned int zend_api; |
unsigned char zend_debug; |--> 通常用STANDARD_MODULE_HEADER填充
unsigned char zts; ---|
char *name; //模塊名
zend_function_entry *functions; //函數(shù)聲明
int (*module_start_func)(INIT_FUNC_ARGS); //MINIT函數(shù)
int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); //MSHUTDOWN函數(shù)
int (*request_start_func)(INIT_FUNC_ARGS); //RINIT函數(shù)
int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); //RSHUTDOWN函數(shù)
char *version; //版本號(hào)
... //其余信息不做討論
} zend_module_entry;
對(duì)比生成代碼中的模塊聲明和①②③中所講,Zend通過模塊聲明將所有信息讀取并加入到引擎中。
⑤get_module函數(shù)
當(dāng)擴(kuò)展被加載時(shí),調(diào)用get_module函數(shù),該函數(shù)返回一個(gè)指向擴(kuò)展模塊聲明的zend_module_entry指針。是PHP內(nèi)核與擴(kuò)展通信的渠道。
get_module函數(shù)被條件宏包圍,故有些情況下不會(huì)執(zhí)行g(shù)et_module方法,當(dāng)擴(kuò)展被編譯為PHP內(nèi)建模塊時(shí)get_module方法不被實(shí)現(xiàn)。
⑥實(shí)現(xiàn)導(dǎo)出函數(shù)
在php_XXX.h中聲明的函數(shù)在XXX.c中具體實(shí)現(xiàn),實(shí)現(xiàn)方式如自動(dòng)生成的confirm_XXX_compiled導(dǎo)出函數(shù)形式一致
PHP_FUNCTION(extfunction){
...具體實(shí)現(xiàn)...
}
在函數(shù)中獲取參數(shù)和返回結(jié)果,后續(xù)講解。
⑦模塊信息函數(shù)
PHP通過phpinfo查看PHP及其擴(kuò)展信息,PHP_MINFO_FUNCTION負(fù)責(zé)實(shí)現(xiàn)。生成代碼函數(shù)體是最基本的模塊信息,可自行設(shè)置顯示內(nèi)容。
4.導(dǎo)出函數(shù)實(shí)現(xiàn)須知
這部分主要講解函數(shù)具體實(shí)現(xiàn)過程中對(duì)參數(shù)和變量的處理。
(1)獲取參數(shù)個(gè)數(shù)
通過ZEND_NUM_ARGS宏獲取參數(shù)個(gè)數(shù),這個(gè)宏實(shí)際上是獲取zif_extfunction的ht字段,定義在Zend/zned_API.h下
#define ZEND_NUM_ARGS() (ht)
(2)取得參數(shù)實(shí)體
Zend引擎提供獲取參數(shù)實(shí)體的API,聲明如下:
int zend_parse_parameters(int num_args TSRMLS_CC, char *type_spec)
num_args:傳入的參數(shù)個(gè)數(shù)
type_spec:參數(shù)的類型,每種類型對(duì)應(yīng)一個(gè)字符,當(dāng)num_args>1時(shí),接收參數(shù)通過字符串依次指明類型接收。
該函數(shù)成功將返回SUCCESS,失敗返回FAILURE。
可以接受的參數(shù)類型如下:
普通:
l : 長整型
d : 雙精度浮點(diǎn)類型
s : 字符串類型及其長度(需要兩個(gè)變量保存?。。。?b : 布爾類型
r : 資源類型,保存在zva *l中
a : 數(shù)組,保存在zval *中
o : 對(duì)象(任何類型),保存在zval *中
O : 對(duì)象(class entry指定類型),保存在zval *中
z : zval *
特殊:
| : 當(dāng)前及之后的參數(shù)為可選參數(shù),有傳即獲取,否則設(shè)為默認(rèn)值
/ : 當(dāng)前及之后的參數(shù)將以SEPARATE_IF_NOT_REF的方式進(jìn)行拷貝,除非是引用
! : 當(dāng)前及之后的參數(shù)允許為NULL,僅用在r,a,o,O,z類型時(shí)
例:獲取一個(gè)字符串和一個(gè)布爾型參數(shù)
char *str;
int strlen;
zend_bool b;
if(zend_parse_paramsters(ZEND_NUM_ARGS() TSRMLS_CC, "sb", &str, &strlen, &b) == FAILURE){ //字符串需要接收內(nèi)容和長度
return ;
}
例:獲取一個(gè)數(shù)組和一個(gè)可選的長整型參數(shù)
zval *arr;
long l;
if(zend_parse_parameter(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &arr, &l) == FAILURE){
return ;
}
例:獲取一個(gè)對(duì)象或NULL
zval *obj;
if(zend_parse_parameter(ZEND_NUM_ARGS() TSRMLS_CC, "o!", &obj) == FAILURE){
return ;
}
(3)獲取可變參數(shù)
開發(fā)過程中會(huì)遇到方法可接受可變參數(shù)的情況,不指定類型的情況下接收參數(shù)的方式:
int num_arg = ZEND_NUM_ARGS();
zval **parameters[num_args];
if(zend_get_parameters_array_ex(num_arg, parameters) == FAILURE){
return ;
}
(4)參數(shù)類型轉(zhuǎn)換
(3)中PHP可以接受任意類型的參數(shù),可能會(huì)導(dǎo)致在具體實(shí)現(xiàn)過程中出問題,因此Zend提供了一系列參數(shù)類型轉(zhuǎn)換的API。

(5)處理通過引用傳遞的參數(shù)
"z"代表的zval類型的傳參即為引用傳遞。PHP規(guī)定修改非引用傳遞的參數(shù)值不會(huì)影響原來變量的值,但PHP內(nèi)核采用引用傳遞方式傳參。PHP內(nèi)核使用"zval分離"的方式避免這一問題。"zval分離"即寫時(shí)復(fù)制,修改非引用類型的參數(shù)時(shí),先復(fù)制一份新值,然后將引用指向新值,修改參數(shù)時(shí)不會(huì)影響原值。
判斷參數(shù)是否為引用,通過PZVAL_IS_REF(zval *),其定義為:
#define PZVAL_IS_REF(z) ((z)->is_ref)
使用宏SEPARATE_ZVAL(zval **)實(shí)現(xiàn)zval分離。
(6)擴(kuò)展中創(chuàng)建變量
PHP擴(kuò)展中創(chuàng)建變量需要以下三步:
①創(chuàng)建一個(gè)zval容器
②對(duì)zval容器進(jìn)行填充
③引入到Zend引擎內(nèi)部符號(hào)表中
//創(chuàng)建zval容器
zval *new_var;
//初始化和填充
MAKE_STD_ZVAL(new_var);
//引入符號(hào)表
ZEND_SET_SYMBOL(EG(active_symbol_table), "new_var", new_var);
MAKE_STD_ZVAL()宏作用是通過ALLOC_ZVAL()申請(qǐng)一個(gè)zval空間,之后通過INIT_ZVAL()進(jìn)行初始化。
#define MAKE_STD_ZVAL(zv) \
ALLOC_ZVAL(zv); \
INIT_ZVAL(zv);
INIT_ZVAL()宏定義如下:
#DEFINE INIT_ZVAL(z) \
(z) -> refcount = 1; \
(z) -> is_ref = 0;
MAKE_STD_ZVAL()只是為變量分配了內(nèi)存,設(shè)置了refcount和is_ref兩個(gè)屬性。
ZEND_SET_SYMBOL()宏將變量引入到符號(hào)表中,引入時(shí)先檢查變量是否已經(jīng)存在于表中,如果已經(jīng)存在,銷毀原有的zval并替換。
如果創(chuàng)建的是全局變量,前兩步不變,只對(duì)引入操作做調(diào)整。局部變量引入active_symbol_table中,全局變量引入symbol_table中,通過
ZEND_SET_SYMBOL(&EG(symbol_table), "new_var", new_var);
注意:active_symbol_table是個(gè)指針,symbol_table不是指針,需要增加&取地址。
···
擴(kuò)展中:
PHP_FUNCTION(extfunction){
zval *new_var;
ZEND_STD_ZVAL(new_var);
ZVAL_LONG(new_var, 10);
ZEND_SET_SYMBOL(&EG(symbol_table), "new_var", new_var);
}
PHP腳本中:
<?php
extfunction();
var_dump($new_var);
?>
結(jié)果輸出:10
···
(7)變量賦值
①長整型(整型)賦值
PHP中所有整型都是保存在zval的value字段中,整數(shù)保存在value聯(lián)合體的lval字段中,type為IS_LONG,賦值通過宏操作進(jìn)行:
ZVAL_LONG(zval, 10);
②雙精度浮點(diǎn)數(shù)類型賦值
浮點(diǎn)數(shù)保存在value的dval中,type對(duì)應(yīng)IS_DOUBLE,通過宏操作
ZVAL_DOUBLE(zval, 3.14);
③字符串類型
value聯(lián)合體的str結(jié)構(gòu)體保存字符串值,val保存字符串,len保存長度,type為IS_STRING。
char *str = "hello world";
ZVAL_STRING(zval, str, 1); //結(jié)尾參數(shù)表示字符串是否需要被復(fù)制。
④布爾類型
值存放在value.lval中,TRUE——1;FALSE——0,type對(duì)應(yīng)IS_BOOL。
賦值為真:ZVAL_BOOL(zval, 1);
賦值為假:ZVAL_BOOL(zval, 0);
⑤數(shù)組類型變量
PHP數(shù)組基于HashTable實(shí)現(xiàn),變量賦值為數(shù)組類型時(shí)先要?jiǎng)?chuàng)建一個(gè)HashTable,保存在value的ht字段中。Zend提供array_init()實(shí)現(xiàn)賦值。
array_init(zval);
同時(shí)Zend提供了一套完整的關(guān)聯(lián)數(shù)組、索引數(shù)組API用于添加元素,這里不一一列舉。
⑥對(duì)象類型變量
對(duì)象和數(shù)組類似,PHP中對(duì)象可以轉(zhuǎn)換成數(shù)組,但數(shù)組無法轉(zhuǎn)換成對(duì)象,會(huì)丟失方法。Zend通過object_init()函數(shù)初始化一個(gè)對(duì)象。
if(object_init(zval) != SUCCESS){
RETURN_NULL();
}
Zend也提供了對(duì)象設(shè)置屬性所需的API,和數(shù)組設(shè)置元素類似,用到時(shí)候找即可。
⑦資源類型
嚴(yán)格而言,資源不是數(shù)據(jù)類型,而是一個(gè)可以維護(hù)任何數(shù)據(jù)類型的抽象,類似C語言的指針。所有資源都保存在一個(gè)Zend內(nèi)部的資源列表中,每份資源都有一個(gè)指向?qū)嶋H數(shù)據(jù)的指針。
為了及時(shí)回收無用的資源,Zend引擎會(huì)自動(dòng)回收引用數(shù)為0的資源的析構(gòu)函數(shù),析構(gòu)函數(shù)需要在擴(kuò)展中自己定義。
Zend使用統(tǒng)一的zend_register_list_destructors_ex()為資源注冊(cè)析構(gòu)函數(shù),該函數(shù)返回一個(gè)句柄,將資源與析構(gòu)函數(shù)相關(guān)聯(lián)。定義如下:
ZEND_ZPI int zend_register_list_destructors_ex(rsrc_dtor_func_t ld,
rsrc_dtor_func_t pld, char *type_name, int module_number);
參數(shù)描述:
ld : 普通資源的析構(gòu)函數(shù)
pld : 持久化資源的析構(gòu)函數(shù)
type_name : 為資源類型起的名字,如:fopen()創(chuàng)建的資源名稱為stream
module_number : PHP_MINIT_FUNCTION函數(shù)會(huì)定義,可忽略
兩種析構(gòu)函數(shù)至少提供一個(gè),為空可用NULL指定。
資源的析構(gòu)函數(shù)必須如下定義:(resource_destruction_handler)函數(shù)名隨意。
void resource_destruction_handler(zend_rsrc_entry *rsrc TSRMLS_DC){
-----------具體實(shí)現(xiàn)代碼------------
}
其中,rsrc是指向zend_rsrc_entry的指針,結(jié)構(gòu)體結(jié)構(gòu)為:
typedef struct _zend_rsrc_entry{
void *ptr; //資源的實(shí)際地址,析構(gòu)時(shí)釋放
int type;
int refcount;
} zend_rsrc_entry ;
通過zend_register_list_destructors_ex()函數(shù)返回的資源句柄,通過一個(gè)全局變量保存,ext_skel生成的擴(kuò)展架構(gòu)中,自動(dòng)生成了一個(gè)'le_'為前綴的int型變量,zend_register_list_destructors_ex()在MINIT函數(shù)中使用并完成注冊(cè)。如實(shí)現(xiàn)鏈表的析構(gòu):
---------------------------------phplist擴(kuò)展-----------------------------------
static le_phplist; //架構(gòu)自動(dòng)生成,保存資源句柄
//定義鏈表節(jié)點(diǎn)
struct ListNode{
struct ListNode *next;
void *data;
}
//析構(gòu)函數(shù)具體實(shí)現(xiàn)
void phplist_destruction_handler(zend_rsrc_entry *rsrc TSRMLS_DC){
ListNode *pre, *next;
pre = (ListNode *)rsrc->ptr;
while(pre){
next = pre -> next;
free(pre);
pre = next;
}
}
//MINIT中注冊(cè)析構(gòu)函數(shù)
PHP_MINIT_FUNCTION(phplist){
//完成注冊(cè)
le_phplist = zend_register_list_destructors_ex(phplist_destruction_handler,
NULL, "php_list", module_number);
return SUCCESS;
}
注冊(cè)完析構(gòu)函數(shù),需要把資源和句柄關(guān)聯(lián)起來,Zend提供zend_register_resource()函數(shù)或者ZEND_REGISTER_RESOURCE()宏完成這一操作。
int zend_register_resource(zval *rsrc_result, void *rsrc_pointer, int rsrc_type);
參數(shù)解釋:
rsrc_result : 存儲(chǔ)zend_register_resource返回的結(jié)果
rsrc_pointer : 指向保存的資源
rsrc_type : 資源類型
該函數(shù)返回int型結(jié)果,該結(jié)果為資源的id。函數(shù)定義源碼:
int zend_register_resource(zval *rsrc_result, void *rsrc_pointer, int rsrc_type){
int rsrc_id;
rsrc_id = zend_list_insert(rsrc_pointer, rsrc_type); //該函數(shù)將資源加入資源列表,并返回資源在列表中的位置(即id)
if(rsrc_result){
rsrc_result -> value.lval = rsrc_id;
rsrc_result -> type = IS_RESOURCE;
}
return rsrc_id;
}
用戶根據(jù)資源的id沖資源列表中獲取資源,Zend定義了ZEND_FETCH_RESOURCE()宏獲取指定的資源。
ZEND_FETCH_RESOURCE(rsrc, rsrc_type, rsrc_id, default_rsrc_id, resource_type_name, resource_type);
其中
rsrc : 保存返回的資源
rsrc_type : 表明想要的資源類型,如 ListNode *等
rsrc_id : 用戶通過PHP腳本傳來的資源id
default_rsrc_id : 沒有獲取到資源時(shí)的標(biāo)識(shí)符,一般用-1指定
resource_type_name : 請(qǐng)求資源類的名稱,用于找不到時(shí)拋出錯(cuò)誤信息使用
resource_type : 注冊(cè)析構(gòu)函數(shù)時(shí)的句柄,即le_phplist
例如獲取用戶指定的list
zval *lrc;
ListNode *list;
//獲取用戶參數(shù)
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &lrc) == FAILURE){
RETURN_FALSE;
}
//獲取對(duì)應(yīng)資源
ZEND_FETCH_RESOURCE(list, ListNode *, &lrc, -1, "php list", le_phplist);
此時(shí)list即為所要獲取的資源。
資源用完需要析構(gòu),當(dāng)引用數(shù)為0時(shí),Zend對(duì)資源進(jìn)行回收,很多擴(kuò)展對(duì)資源有相應(yīng)的析構(gòu)函數(shù),比如mysql_connect()的mysql_close(),fopen()對(duì)應(yīng)fclose()。PHP的unset()也可以直接釋放一個(gè)資源。
如果想顯示的定義函數(shù)釋放資源,在自定義函數(shù)中調(diào)用zend_list_delete()函數(shù)即可
ZEND_API int zend_list_delete(int id TSRMLS_DC);
該函數(shù)的作用是根據(jù)id將資源的引用數(shù)-1,然后判斷引用數(shù)是否大于0,是則觸發(fā)析構(gòu)函數(shù)清除資源。
(8)錯(cuò)誤輸出API
Zend推薦使用zend_error()函數(shù)輸出錯(cuò)誤信息,該函數(shù)定義如下:
ZEND_API void zend_error(int type, char *format, ...)
參數(shù):
type : PHP的6鐘錯(cuò)誤信息類型
①E_ERROR:拋出一個(gè)錯(cuò)誤,腳本將停止運(yùn)行
②E_WARNING : 拋出警告,腳本繼續(xù)執(zhí)行
③E_NOTICE : 拋出通知,腳本繼續(xù)執(zhí)行,一般情況下php.ini設(shè)置不顯示
④E_CORE_ERROR : 拋出PHP內(nèi)核錯(cuò)誤
⑤E_COMPILE_ERROR : 拋出編譯器內(nèi)部錯(cuò)誤
⑥E_COMPILE_WARNING : 拋出編譯器警告
注意:后三種錯(cuò)誤不應(yīng)由自定義擴(kuò)展模塊拋出?。。?format : 錯(cuò)誤輸出格式
(9)運(yùn)行時(shí)信息函數(shù)
執(zhí)行PHP腳本出錯(cuò)時(shí),經(jīng)常會(huì)有相關(guān)的運(yùn)行信息,指出哪個(gè)文件,哪個(gè)函數(shù),具體哪行有執(zhí)行錯(cuò)誤,Zend引擎有相關(guān)的實(shí)現(xiàn)接口。
查看當(dāng)前執(zhí)行的函數(shù)名
get_active_function_name(TSRMLS_C);
查看當(dāng)前執(zhí)行的文件名
zend_get_executed_filename(TSRMLS_C);
查看所在行
zend_get_executed_lineno(TSRMLS_C);
三個(gè)函數(shù)都需要以TSRMLS_C為參數(shù),作為訪問執(zhí)行器(Executor)全局變量。TSRM_C是TSRM存儲(chǔ)器,與線程安全相關(guān),之后專門寫篇博客講講。
(10)擴(kuò)展調(diào)用腳本中用戶自定義函數(shù)
這種情況比較少,但Zend功能全面,支持這類操作。
在擴(kuò)展中使用用戶自定義函數(shù),通過call_user_function_ex()函數(shù)實(shí)現(xiàn),函數(shù)原型:
int call_user_function_ex(HashTable *function_table, //要訪問的函數(shù)表指針
zval **object_pp, //調(diào)用方法的對(duì)象,沒有設(shè)為NULL
zval *function_name, //函數(shù)名
zval **retval_ptr_ptr, //保存返回值的指針
zend_uint param_count, //參數(shù)個(gè)數(shù)
zval **params[], //參數(shù)
int no_separation, //是否禁止zcal分離操作
HashTable symbol_table //符號(hào)表,一般設(shè)為NULL
TSRMLS_DC
);
其中no_separation為1會(huì)禁止zval分離,節(jié)省內(nèi)存,但任何參數(shù)分離將導(dǎo)致操作失敗,通常設(shè)為0。
腳本中定義用戶函數(shù)
function userfunc(){
return "call user function success";
}
-------------------------調(diào)用擴(kuò)展方法----------------------------
$ret = call_user_function_in_ext();
var_dump($ret);
擴(kuò)展中需要實(shí)現(xiàn)call_user_function_in_ext()函數(shù)
PHP_FUNCTION(call_user_function_in_ext){
zval **funcName;
zval *retval;
if(ZEND_NUM_ARGS() != 1 ||
zend_get_parameters_ex(1, &function_name) == FAILURE){
zend_error(E_ERROR, "function %s call in extension fail", (*function_name)->value->str->val);
}
if((*function_name)->type != IS_STRING){
zend_error(E_ERROR, "function name must be string");
}
if(call_user_function_ex(CG(function_table), NULL, *function_name, &retval, 0, NULL, 0, NULL TSRMLS_DC) != SUCCESS){
zend_error(E_ERROR, "function call fail");
}
zval *ret_val = *retval;
zval_copy_ctor(ret_val);
zval_ptr_dtor(&retval);
}
此外Zend還有提供顯示phpinfo的函數(shù),比較簡單,不做講解。
創(chuàng)建擴(kuò)展
創(chuàng)建一個(gè)鏈表操作的擴(kuò)展,擴(kuò)展名為phplist,生成架構(gòu)先
cd php_src/ext
./ext_skel --extname=phplist
此時(shí)ext目錄下生成phplist/,本例不依賴其他擴(kuò)展或lib庫,按準(zhǔn)備工作(二)修改config.m4文件。
之后實(shí)現(xiàn)擴(kuò)展的函數(shù)。在php_phplist.h中聲明,具體實(shí)現(xiàn)在phplist.c中。
php_phplist.h如下:
#ifndef PHP_PHPLIST_H
#define PHP_PHPLIST_H
extern zend_module_entry phplist_module_entry;
#define phpext_phplist_ptr &phplist_module_entry
#define PHP_PHPLIST_VERSION "0.1.0" /* Replace with version number for your extension */
#ifdef PHP_WIN32
# define PHP_PHPLIST_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
# define PHP_PHPLIST_API __attribute__ ((visibility("default")))
#else
# define PHP_PHPLIST_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
PHP_MINIT_FUNCTION(phplist);
PHP_MSHUTDOWN_FUNCTION(phplist);
PHP_RINIT_FUNCTION(phplist);
PHP_RSHUTDOWN_FUNCTION(phplist);
PHP_MINFO_FUNCTION(phplist);
PHP_FUNCTION(confirm_phplist_compiled); /* For testing, remove later. */
/*聲明擴(kuò)展函數(shù)*/
PHP_FUNCTION(list_create); //創(chuàng)建鏈表
PHP_FUNCTION(list_add_head); //添加到鏈表頭
PHP_FUNCTION(list_add_tail); //添加到鏈表尾
PHP_FUNCTION(list_get_index); //獲取節(jié)點(diǎn)
PHP_FUNCTION(list_get_length); //獲取鏈表長度
PHP_FUNCTION(list_remove_index); //移除節(jié)點(diǎn)
#ifdef ZTS
#define PHPLIST_G(v) TSRMG(phplist_globals_id, zend_phplist_globals *, v)
#else
#define PHPLIST_G(v) (phplist_globals.v)
#endif
#endif /* PHP_PHPLIST_H */
在phplist.c中具體實(shí)現(xiàn)
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_phplist.h"
static int le_phplist;
static int isFree = 0;
const zend_function_entry phplist_functions[] = {
PHP_FE(confirm_phplist_compiled, NULL) /* For testing, remove later. */
PHP_FE(list_create, NULL)
PHP_FE(list_add_head, NULL)
PHP_FE(list_add_tail, NULL)
PHP_FE(list_get_index, NULL)
PHP_FE(list_get_length, NULL)
PHP_FE(list_remove_index, NULL)
PHP_FE(list_destroy, NULL)
PHP_FE(list_get_head, NULL)
PHP_FE_END /* Must be the last line in phplist_functions[] */
};
zend_module_entry phplist_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"phplist",
phplist_functions,
PHP_MINIT(phplist),
PHP_MSHUTDOWN(phplist),
PHP_RINIT(phplist), /* Replace with NULL if there's nothing to do at request start */
PHP_RSHUTDOWN(phplist), /* Replace with NULL if there's nothing to do at request end */
PHP_MINFO(phplist),
#if ZEND_MODULE_API_NO >= 20010901
PHP_PHPLIST_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_PHPLIST
ZEND_GET_MODULE(phplist)
#endif
//定義鏈表節(jié)點(diǎn)和鏈表頭
typedef struct _ListNode{
struct _ListNode *prev;
struct _ListNode *next;
zval *value;
}ListNode;
typedef struct _ListHead{
struct _ListNode *head;
struct _ListNode *tail;
int size;
}ListHead;
//創(chuàng)建鏈表具體實(shí)現(xiàn)
ListHead * list_create(){
ListHead *head;
head = (ListHead *)malloc(sizeof(ListHead));
if (head){
head->size = 0;
head->head = NULL;
head->tail = NULL;
}
return head;
}
//向頭部添加
int list_add_head(ListHead *head, zval *value){
ListNode *node;
node = (ListNode *)malloc(sizeof(*node));
if (!node){
return 0;
}
node->value = value;
node->prev = NULL;
node->next = head->head;
if (head->head){
head->head->prev = node;
}
head->head = node;
if(!head->tail){
head->tail = head->head;
}
head->size++;
return 1;
}
//鏈表尾添加
int list_add_tail(ListHead *list, zval *value){
ListNode *node;
node = (ListNode *)malloc(sizeof(*node));
if(!node){
return 0;
}
node->value = value;
node->next = NULL;
node->prev = list->tail;
if (list->tail){
list->tail->next = node;
}
list->tail = node;
if (!list->head){
list->head = list->tail;
}
list->size++;
return 1;
}
//獲取指定元素
int list_get_index(ListHead *list, int index, zval **retval){
ListNode *node;
if(!list){
return 0;
}
if (index <= 0 || list->size == 0 || index > list->size){
return 0;
}
if (index < list->size / 2){
node = list->head;
while(node && index > 0){
node = node->next;
--index;
}
}else{
node = list->tail;
while(node && index > 0){
node = node->prev;
--index;
}
}
*retval = node->value;
return 1;
}
//獲取鏈表長度
int list_get_length(ListHead *list){
if (list){
return list->size;
}else{
return 0;
}
}
//刪除節(jié)點(diǎn)
int list_remove_index(ListHead *list, int index){
ListNode *node;
if(!list){
return 0;
}
if (index <= 0 || list->size == 0 || index > list->size){
return 0;
}
if (index < list->size / 2){
node = list->head;
while(node && index > 0){
node = node->next;
--index;
}
}else{
node = list->tail;
while(node && index > 0){
node = node->prev;
--index;
}
}
if (!node){
return 0;
}
if (node->prev){
node->prev->next = node->next;
}else{
list->head = node->next;
}
if(node->next){
node->next->prev = node->prev;
}else{
list->tail = node->prev;
}
list->size--;
return 1;
}
//釋放鏈表
void list_destroy(ListHead *list){
ListNode *pre, *next;
pre = list->head;
while(pre){
next = pre->next;
free(pre);
pre = next;
}
free(list);
}
int list_get_head(ListHead *list, zval **retval){
if (!list || !list->head){
return 0;
}
*retval = list->head->value;
return 1;
}
//析構(gòu)函數(shù)實(shí)現(xiàn)
void phplist_destructor_handler(zend_rsrc_list_entry *rsrc TSRMLS_DC){
if (!isFree){
ListHead *list;
list = (ListHead *)rsrc->ptr;
list_destroy(list);
isFree = 1;
}
}
PHP_MINIT_FUNCTION(phplist)
{
le_phplist = zend_register_list_destructors_ex(phplist_destructor_handler, NULL, "phplist", module_number);
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(phplist)
{
return SUCCESS;
}
PHP_RINIT_FUNCTION(phplist)
{
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(phplist)
{
return SUCCESS;
}
PHP_MINFO_FUNCTION(phplist)
{
php_info_print_table_start();
php_info_print_table_header(2, "phplist support", "enabled");
php_info_print_table_end();
}
PHP_FUNCTION(confirm_phplist_compiled)
{
char *arg = NULL;
int arg_len, len;
char *strg;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
return;
}
len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "phplist", arg);
RETURN_STRINGL(strg, len, 0);
}
PHP_FUNCTION(list_create){
ListHead *list;
list = list_create();
if (!list){
RETURN_NULL();
}
ZEND_REGISTER_RESOURCE(return_value, list, le_phplist);
}
PHP_FUNCTION(list_add_head){
zval *value;
zval *lrc;
ListHead *list;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz", &lrc, &value) == FAILURE){
RETURN_FALSE;
}
ZEND_FETCH_RESOURCE(list, ListHead *, &lrc, -1, "php list", le_phplist);
int ret = list_add_head(list, value);
if(ret){
RETURN_TRUE;
}
RETURN_FALSE;
}
PHP_FUNCTION(list_add_tail){
zval *value;
zval *lrc;
ListHead *list;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz", &lrc, &value) == FAILURE){
RETURN_FALSE;
}
ZEND_FETCH_RESOURCE(list, ListHead *, &lrc, -1, "php list", le_phplist);
int ret = list_add_tail(list, value);
if(ret){
RETURN_TRUE;
}
RETURN_FALSE;
}
PHP_FUNCTION(list_get_index){
zval *lrc;
ListHead *list;
long index;
zval *retval;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &lrc, &index) == FAILURE){
RETURN_FALSE;
}
ZEND_FETCH_RESOURCE(list, ListHead *, &lrc, -1, "php list", le_phplist);
int ret = list_get_index(list, index, &retval);
if (ret){
RETURN_ZVAL(retval, 1, 0);
}
RETURN_NULL();
}
PHP_FUNCTION(list_get_length){
zval *lrc;
ListHead *list;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &lrc) == FAILURE){
RETURN_FALSE;
}
ZEND_FETCH_RESOURCE(list, ListHead *, &lrc, -1, "php list", le_phplist);
RETURN_LONG(list_get_length(list));
}
PHP_FUNCTION(list_remove_index){
zval *lrc;
ListHead *list;
long index;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &lrc, &index) == FAILURE){
RETURN_FALSE;
}
ZEND_FETCH_RESOURCE(list, ListHead *, &lrc, -1, "php list", le_phplist);
int ret = list_remove_index(list, index);
if (ret){
RETURN_TRUE;
}
RETURN_FALSE;
}
PHP_FUNCTION(list_destroy){
zval *lrc;
ListHead *list;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &lrc) == FAILURE){
RETURN_FALSE;
}
ZEND_FETCH_RESOURCE(list, ListHead *, &lrc, -1, "php list", le_phplist);
list_destroy(list);
}
PHP_FUNCTION(list_get_head){
zval *lrc;
zval *retval;
ListHead *list;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &lrc) == FAILURE){
RETURN_FALSE;
}
ZEND_FETCH_RESOURCE(list, ListHead *, &lrc, -1, "php list", le_phplist);
int ret = list_get_head(list, &retval);
if (ret){
RETURN_ZVAL(retval, 1, 0);
}
RETURN_NULL();
}
之后按照步驟在php.ini中引入擴(kuò)展即可使用。