PHP擴展開發(fā)教程(總結(jié))
PHP是一種解釋型的語言,對于用戶而言,我們精心的控制內(nèi)存意味著easier prototyping和更少的崩潰!當(dāng)我們深入到內(nèi)核之后,所有的安全防線都已經(jīng)被越過,最終還是要依賴于真正有責(zé)任心的軟件工程師來保證系統(tǒng)的穩(wěn)定運行。
1、線程安全宏定義
在TSRM/TSRM.h文件中有如下定義
#define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL)
#define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = (void ***) ctx
#define TSRMLS_SET_CTX(ctx) ctx = (void ***) tsrm_ls
#define TSRMG(id, type, element) (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
#define TSRMLS_D void ***tsrm_ls
#define TSRMLS_DC , TSRMLS_D
#define TSRMLS_C tsrm_ls
#define TSRMLS_CC , TSRMLS_C
在ext/xsl/php_xsl.h有這么一段話
/* In every utility function you add that needs to use variables.
in php_xsl_globals, call TSRM_FETCH(); after declaring other.
variables used by that function, or better yet, pass in TSRMLS_CC
after the last function argument and declare your utility function
with TSRMLS_DC after the last declared argument. Always refer to
the globals in your function as XSL_G(variable). You are.
encouraged to rename these macros something shorter, see
examples in any other php module directory.
*/
1.在方法定義時加上TSRMLS_D(如果方法沒有參數(shù)用這個)或者TSRMLS_DC(有1個以上的參數(shù))
2.在方法調(diào)用時用TSRMLS_C(如果方法沒有參數(shù)用這個)或者TSRMLS_CC(有1個以上的參數(shù))
應(yīng)該可以這樣理解
第一個后綴字母D表示定義,即D=Define,第一個后綴字母C表示調(diào)用,即C=Call,而第二個后綴字母C是不是表示逗號呢? C=Comma (逗號)
TSRMLS_D就是定義了,所以是 void ***tsrm_ls
TSRMLS_DC是帶逗號的定義,所以是 , void ***tsrm_ls
TSRMLS_C是調(diào)用,即tsrm_ls
TSRMLS_CC是調(diào)用并帶逗號,即 ,tsrm_ls
所以一個是形參、一個是實參
可以這樣使用
int php_myext_action(int action_id, char *message TSRMLS_DC);
php_myext_action(42, "The meaning of life" TSRMLS_CC);
一般推薦使用tsrm_ls指針定義的方式來保證線程安全
TSRMLS_FETCH調(diào)用需要一定的處理時間。這在單次迭代中并不明顯,但是隨著你的線程數(shù)增多,隨著你調(diào)用TSRMLS_FETCH()的點的增多,你的擴展就會顯現(xiàn)出這個瓶頸。因此,請謹(jǐn)慎的使用它。 注意:為了和c++編譯器兼容,請確保將TSRMLS_FETCH()和所有變量定義放在給定塊作用域的頂部(任何其他語句之前)。因為TSRMLS_FETCH()宏自身有多種不同的解析方式,因此最好將它作為變量定義的最后一行
2、PHP的生命周期
PHP的最多的兩種運行模式是WEB模式、CLI模式,無論哪種模式,PHP工作原理都是一樣的,作為一種SAPI運行。
1、當(dāng)我們在終端敲入php這個命令的時候,它使用的是CLI。
它就像一個web服務(wù)器一樣來支持php完成這個請求,請求完成后再重新把控制權(quán)交給終端。
2、當(dāng)使用Apache作為宿主時,當(dāng)一個請求到來時,PHP會來支持完成這個請求
PHP_MINIT_FUNCTION 初始化module時運行
PHP_MSHUTDOWN_FUNCTION 當(dāng)module被卸載時運行
PHP_RINIT_FUNCTION 當(dāng)一個REQUEST請求初始化時運行
PHP_RSHUTDOWN_FUNCTION 當(dāng)一個REQUEST請求結(jié)束時運行
PHP_MINFO_FUNCTION 這個是設(shè)置phpinfo中這個模塊的信息
PHP_GINIT_FUNCTION 初始化全局變量時
PHP_GSHUTDOWN_FUNCTION 釋放全局變量時
比如PHP_GINIT_FUNCTION
PHP_GINIT_FUNCTION(test)
{
/** 初始化全局變量 */
}
//對應(yīng)的C代碼
void zm_globals_ctor_test (zend_test_globals *test_globals TSRMLS_DC)
{
/** 初始化全局變量 */
}
//在線程退出時,需要將之前自己申請的資源釋放時,可以使用 PHP_GSHUTDOWN_FUNCTION來注冊析構(gòu)函數(shù)。
PHP_GSHUTDOWN_FUNCTION(test)
{
/** 清除全局變量 */
}
//對應(yīng)的C代碼
void zm_globals_dtor_test (zend_test_globals *test_globals TSRMLS_DC)
{
/** 清除全局變量 */
}
這里有一段代碼,可以測試一下
int minit_time;
PHP_MINIT_FUNCTION(test)
{
minit_time = time(NULL);
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(test)
{
FILE *fp=fopen("mshutdown.txt","a+");
fprintf(fp,"%ld\n",time(NULL));
fclose(fp);
return SUCCESS;
}
int rinit_time;
PHP_RINIT_FUNCTION(test)
{
rinit_time = time(NULL);
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(test)
{
FILE *fp=fopen("rshutdown.txt","a+");
fprintf(fp,"%ld\n",time(NULL));
fclose(fp);
return SUCCESS;
}
PHP_MINFO_FUNCTION(test)
{
php_info_print_table_start();
php_info_print_table_header(, "module info", "enabled");
php_info_print_table_end();
/* Remove comments if you have entries in php.ini
DISPLAY_INI_ENTRIES();
*/
}
PHP_FUNCTION(test)
{
php_printf("%d",time_of_minit);
php_printf("%d",time_of_rinit);
return;
}
3、段錯誤調(diào)試
Linux下的C程序常常會因為內(nèi)存訪問錯誤等原因造成segment fault(段錯誤)此時如果系統(tǒng)core dump功能是打開的,那么將會有內(nèi)存映像轉(zhuǎn)儲到硬盤上來,之后可以用gdb對core文件進(jìn)行分析,還原系統(tǒng)發(fā)生段錯誤時刻的堆棧情況。這對于我們發(fā)現(xiàn)程序bug很有幫助。
使用ulimit -a可以查看系統(tǒng)core文件的大小限制;使用ulimit -c [kbytes]可以設(shè)置系統(tǒng)允許生成的core文件大小。
ulimit -c 0 不產(chǎn)生core文件
ulimit -c 100 設(shè)置core文件最大為100k
ulimit -c unlimited 不限制core文件大小
步驟:
1、當(dāng)發(fā)生段錯誤時,我們查看ulimit -a (core file size (blocks, -c) 0)并沒有文件,
2、設(shè)置 :ulimit -c unlimited 不限制core文件大小
3、運行程序 ,發(fā)生段錯誤時會自動記錄在core中 (php -f WorkWithArray.php)
4、ls -al core.* 在那個文件下(-rw------- 1 leconte leconte 139264 01-06 22:3 1 core.2065)
5、使用gdb 運行程序和段錯誤記錄的文件。(gdb ./test core.2065)
6、會提哪行有錯。
很多系統(tǒng)默認(rèn)的core文件大小都是0,我們可以通過在shell的啟動腳本/etc/bashrc或者~/.bashrc等地方來加入 ulimit -c 命令來指定core文件大小,從而確保core文件能夠生成。
除此之外,還可以在/proc/sys/kernel/core_pattern里設(shè)置core文件的文件名模板,詳情請看core的官方man手冊。
4、常見的變量操作宏
CG -> Complier Global 編譯時信息,包括函數(shù)表等(zend_globals_macros.h:32)
EG -> Executor Global 執(zhí)行時信息(zend_globals_macros.h:43)
PG -> PHP Core Global 主要存儲php.ini中的信息
SG -> SAPI Global SAPI信息
1、SG 針對SAPI信息 在main/SAPI.h文件中
typedef struct _sapi_globals_struct {
void *server_context;
sapi_request_info request_info;
sapi_headers_struct sapi_headers;
int read_post_bytes;
unsigned char headers_sent;
struct stat global_stat;
char *default_mimetype;
char *default_charset;
HashTable *rfc1867_uploaded_files;
long post_max_size;
int options;
zend_bool sapi_started;
double global_request_time;
HashTable known_post_content_types;
zval *callback_func;
zend_fcall_info_cache fci_cache;
zend_bool callback_run;
} sapi_globals_struct;
看一下SG的定義
BEGIN_EXTERN_C()
#ifdef ZTS
# define SG(v) TSRMG(sapi_globals_id, sapi_globals_struct *, v)
SAPI_API extern int sapi_globals_id;
#else
# define SG(v) (sapi_globals.v)
extern SAPI_API sapi_globals_struct sapi_globals;
#endif
SAPI_API void sapi_startup(sapi_module_struct *sf);
SAPI_API void sapi_shutdown(void);
SAPI_API void sapi_activate(TSRMLS_D);
SAPI_API void sapi_deactivate(TSRMLS_D);
SAPI_API void sapi_initialize_empty_request(TSRMLS_D);
END_EXTERN_C()
成員都在sapi_globals_struct這里了
那么我么可以這樣調(diào)用
SG(default_mimetype)
SG(request_info).request_uri
可以感受一下這么一段代碼
static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
{
char buf[SAPI_CGI_MAX_HEADER_LENGTH];
sapi_header_struct *h;
zend_llist_position pos;
long rfc2616_headers = 0;
if(SG(request_info).no_headers == 1) {
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
if (SG(sapi_headers).http_response_code != 200) {
int len;
len = sprintf(buf, "Status: %d\r\n", SG(sapi_headers).http_response_code);
PHPWRITE_H(buf, len);
}
if (SG(sapi_headers).send_default_content_type) {
char *hd;
hd = sapi_get_default_content_type(TSRMLS_C);
PHPWRITE_H("Content-type:", sizeof("Content-type: ")-1);
PHPWRITE_H(hd, strlen(hd));
PHPWRITE_H("\r\n", 2);
efree(hd);
}
h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
while (h) {
PHPWRITE_H(h->header, h->header_len);
PHPWRITE_H("\r\n", 2);
h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
}
PHPWRITE_H("\r\n", 2);
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
2、EG Executor Globals
EG獲取的是struct _zend_execution_globals結(jié)構(gòu)體中的數(shù)據(jù)
struct _zend_execution_globals {
...
HashTable symbol_table; /* 全局作用域,如果沒有進(jìn)入函數(shù)內(nèi)部,全局=活動 */
HashTable *active_symbol_table; /* 活動作用域,當(dāng)前作用域 */
...
}
通常,使用EG(symbol_table)獲取的是全局作用域中的符號表,使用EG(active_symbol_table)獲取的是當(dāng)前作用域下的符號表
例如 來定義$foo = 'bar'
zval *fooval;
MAKE_STD_ZVAL(fooval);
ZVAL_STRING(fooval, "bar", 1);
ZEND_SET_SYMBOL(EG(active_symbol_table), "foo", fooval);
或者從符號表中查找$foo
zval **fooval;
if(zend_hash_find(&EG(symbol_table), "foo", sizeof("foo"), (void **)&fooval) == SUCCESS) {
RETURN_STRINGL(Z_STRVAL_PP(fooval), Z_STRLEN_PP(fooval));
} else {
RETURN_FALSE;
}
上面的代碼中,EG(active_symbol_table) == &EG(symbol_table)
3、CG() 用來訪問核心全局變量。(zend/zend_globals_macros.h)
4、PG() PHP全局變量。我們知道php.ini會映射一個或者多個PHP全局結(jié)構(gòu)。(main/php_globals.h)
5、FG() 文件全局變量。大多數(shù)文件I/O或相關(guān)的全局變量的數(shù)據(jù)流都塞進(jìn)標(biāo)準(zhǔn)擴展出口結(jié)構(gòu)。(ext/standard/file.h)
5、獲取變量的類型和值
#define Z_TYPE(zval) (zval).type
#define Z_TYPE_P(zval_p) Z_TYPE(*zval_p)
#define Z_TYPE_PP(zval_pp) Z_TYPE(**zval_pp)
比如獲取一個變量的類型
void describe_zval(zval *foo)
{
if ( Z_TYPE_P(foo) == IS_NULL )
{
php_printf("這個變量的數(shù)據(jù)類型是: NULL");
}
else
{
php_printf("這個變量的數(shù)據(jù)類型不是NULL,這種數(shù)據(jù)類型對應(yīng)的數(shù)字是: %d", Z_TYPE_P(foo));
}
}
有這么幾種類型
#define IS_NULL 0
#define IS_LONG 1
#define IS_DOUBLE 2
#define IS_BOOL 3
#define IS_ARRAY 4
#define IS_OBJECT 5
#define IS_STRING 6
#define IS_RESOURCE 7
#define IS_CONSTANT 8
#define IS_CONSTANT_ARRAY 9
#define IS_CALLABLE 10
php_printf()函數(shù)是內(nèi)核對printf()函數(shù)的一層封裝,我們可以像使用printf()函數(shù)那樣使用它,以一個P結(jié)尾的宏的參數(shù)大多是*zval型變量。 此外獲取變量類型的宏還有兩個,分別是Z_TYPE和Z_TYPE_PP,前者的參數(shù)是zval型,而后者的參數(shù)則是**zval
比如gettype函數(shù)的實現(xiàn)
//開始定義php語言中的函數(shù)gettype
PHP_FUNCTION(gettype)
{
//arg間接指向調(diào)用gettype函數(shù)時所傳遞的參數(shù)。是一個zval**結(jié)構(gòu)
//所以我們要對他使用__PP后綴的宏。
zval **arg;
//這個if的操作主要是讓arg指向參數(shù)~
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
return;
}
//調(diào)用Z_TYPE_PP宏來獲取arg指向zval的類型。
//然后是一個switch結(jié)構(gòu),RETVAL_STRING宏代表這gettype函數(shù)返回的字符串類型的值
switch (Z_TYPE_PP(arg)) {
case IS_NULL:
RETVAL_STRING("NULL", 1);
break;
case IS_BOOL:
RETVAL_STRING("boolean", 1);
break;
case IS_LONG:
RETVAL_STRING("integer", 1);
break;
case IS_DOUBLE:
RETVAL_STRING("double", 1);
break;
case IS_STRING:
RETVAL_STRING("string", 1);
break;
case IS_ARRAY:
RETVAL_STRING("array", 1);
break;
case IS_OBJECT:
RETVAL_STRING("object", 1);
break;
case IS_RESOURCE:
{
char *type_name;
type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(arg) TSRMLS_CC);
if (type_name) {
RETVAL_STRING("resource", 1);
break;
}
}
default:
RETVAL_STRING("unknown type", 1);
}
}
獲取變量的值,有這么多宏來獲取
|
Long |
Boolean |
Double |
String value |
String length |
|---|---|---|---|---|
Z_LVAL( ) |
Z_BVAL( ) |
Z_DVAL( ) |
Z_STRVAL( ) |
Z_STRLEN( ) |
Z_LVAL_P( ) |
Z_BVAL_P( ) |
Z_DVAL_P( ) |
Z_STRVAL_P( ) |
Z_STRLEN_P( ) |
Z_LVAL_PP( ) |
Z_BVAL_PP( ) |
Z_DVAL_PP( ) |
Z_STRVAL_PP( ) |
Z_STRLEN_PP( ) |
|
HashTable |
Object |
Object properties |
Object class entry |
Resource value |
Z_ARRVAL( ) |
Z_OBJ( ) |
Z_OBJPROP( ) |
Z_OBJCE( ) |
Z_RESVAL( ) |
Z_ARRVAL_P( ) |
Z_OBJ_P( ) |
Z_OBJPROP_P( ) |
Z_OBJCE_P( ) |
Z_RESVAL_P( ) |
Z_ARRVAL_PP( ) |
Z_OBJ_PP( ) |
Z_OBJPROP_PP( ) |
Z_OBJCE_PP( ) |
Z_RESVAL_PP( ) |
rot13函數(shù)的實現(xiàn)
PHP_FUNCTION(rot13)
{
zval **arg;
char *ch, cap;
int i;
if (ZEND_NUM_ARGS( ) != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
WRONG_PARAM_COUNT;
}
*return_value = **arg;
zval_copy_ctor(return_value);
convert_to_string(return_value);
for(i=0, ch=return_value->value.str.val;
i<return_value->value.str.len; i++, ch++) {
cap = *ch & 32;
*ch &= ~cap;
*ch = ((*ch>='A') && (*ch<='Z') ? ((*ch-'A'+13) % 26 + 'A') : *ch) | cap;
}
}
要獲取變量的值,也應(yīng)該使用Zend定義的宏進(jìn)行訪問。對于簡單的標(biāo)量數(shù)據(jù)類型、Boolean,long,double, 使用Z_BVAL, Z_LVAL, Z_DVAL
void display_values(zval boolzv, zval *longpzv, zval **doubleppzv)
{
if (Z_TYPE(boolzv) == IS_BOOL) {
php_printf("The value of the boolean is : %s\n", Z_BVAL(boolzv) ? "true" : "false");
}
if(Z_TYPE_P(longpzv) == IS_LONG) {
php_printf("The value of the long is: %ld\n", Z_LVAL_P(longpzv));
}
if(Z_TYPE_PP(doubleppzv) == IS_DOUBLE) {
php_printf("The value of the double is : %f\n", Z_DVAL_PP(doubleppzv));
}
}
對于字符串類型,因為它含有兩個字段char * (Z_STRVAL) 和 int (Z_STRLEN),因此需要用兩個宏來進(jìn)行取值,因為需要二進(jìn)制安全的輸出這個字符串
void display_string(zval *zstr)
{
if (Z_TYPE_P(zstr) != IS_STRING) {
php_printf("The wronng datatype was passed!\n");
return ;
}
PHPWRITE(Z_STRVAL_P(zstr), Z_STRLEN_P(zstr));
}
因為數(shù)組在zval中是以HashTable形式存在的,因此使用Z_ARRVAL()進(jìn)行訪問
void display_zval(zval *value)
{
switch (Z_TYPE_P(value)) {
case IS_NULL:
/* 如果是NULL,則不輸出任何東西 */
break;
case IS_BOOL:
/* 如果是bool類型,并且true,則輸出1,否則什么也不干 */
if (Z_BVAL_P(value)) {
php_printf("1");
}
break;
case IS_LONG:
/* 如果是long整型,則輸出數(shù)字形式 */
php_printf("%ld", Z_LVAL_P(value));
break;
case IS_DOUBLE:
/* 如果是double型,則輸出浮點數(shù) */
php_printf("%f", Z_DVAL_P(value));
break;
case IS_STRING:
/* 如果是string型,則二進(jìn)制安全的輸出這個字符串 */
PHPWRITE(Z_STRVAL_P(value), Z_STRLEN_P(value));
break;
case IS_RESOURCE:
/* 如果是資源,則輸出Resource #10 格式的東東 */
php_printf("Resource #%ld", Z_RESVAL_P(value));
break;
case IS_ARRAY:
/* 如果是Array,則輸出Array5個字母! */
php_printf("Array");
break;
case IS_OBJECT:
php_printf("Object");
break;
default:
/* Should never happen in practice,
* but it's dangerous to make assumptions
*/
php_printf("Unknown");
break;
}
}
一些類型轉(zhuǎn)換函數(shù)
ZEND_API void convert_to_long(zval *op);
ZEND_API void convert_to_double(zval *op);
ZEND_API void convert_to_null(zval *op);
ZEND_API void convert_to_boolean(zval *op);
ZEND_API void convert_to_array(zval *op);
ZEND_API void convert_to_object(zval *op);
ZEND_API void _convert_to_string(zval *op ZEND_FILE_LINE_DC);
6、常量的實例化
我們可以這樣實例化
PHP_MINIT_FUNCTION(consts) //模塊初始化時定義常量
{
REGISTER_LONG_CONSTANT("CONSTS_MEANING_OF_LIFE", 42, CONST_CS | CONST_PERSISTENT);
REGISTER_DOUBLE_CONSTANT("CONSTS_PI", 3.1415926, CONST_PERSISTENT);
REGISTER_STRING_CONSTANT("CONSTS_NAME", "leon", CONST_CS|CONST_PERSISTENT);
}
PHP_RINIT_FUNCTION(consts) //每次請求時定義常量
{
char buffer[40];
srand((int)time(NULL));
snprintf(buffer, sizeof(buffer), "%d", rand());
REGISTER_STRING_CONSTANT("CONSTS_RAND", estrdup(buffer), CONST_CS);
return SUCCESS;
}
常見的宏
/*注冊LONG類型常量*/
#define REGISTER_LONG_CONSTANT(name, lval, flags) zend_register_long_constant((name), sizeof(name), (lval), (flags), module_number TSRMLS_CC)
/*注冊double類型常量*/
#define REGISTER_DOUBLE_CONSTANT(name, dval, flags) zend_register_double_constant((name), sizeof(name), (dval), (flags), module_number TSRMLS_CC)
/*注冊STRING類型常量*/
#define REGISTER_STRING_CONSTANT(name, str, flags) zend_register_string_constant((name), sizeof(name), (str), (flags), module_number TSRMLS_CC)
/*注冊STRING類型常量*/
#define REGISTER_STRINGL_CONSTANT(name, str, len, flags) zend_register_stringl_constant((name), sizeof(name), (str), (len), (flags), module_number TSRMLS_CC)
7、全局變量
#php-fpm 生成 POST|GET|COOKIE|SERVER|ENV|REQUEST|FILES全局變量的流程
php_cgi_startup() -> php_module_startup() -> php_startup_auto_globals() -> 保存變量到symbol_table符號表
php_cgi_startup()在 fpm/fpm/fpm_main.c中定義
php_module_startup() 在main/main.c中定義
php_startup_auto_globals() 在main/php_variables.h中定義
zend_hash_update(&EG(symbol_table), "_GET", sizeof("_GET") + 1, &vars, sizeof(zval *), NULL);
/* 讀取$_SERVER變量 */
static PHP_FUNCTION(print_server_vars) {
zval **val;
if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&val) == SUCCESS) {
RETURN_ZVAL(*val, 1, 0);
}else{
RETURN_FALSE;
}
}
/* 讀取$_SERVER[$name] */
ZEND_BEGIN_ARG_INFO(print_server_var_arginfo, 0)
ZEND_ARG_INFO(0, "name")
ZEND_END_ARG_INFO()
static PHP_FUNCTION(print_server_var) {
char *name;
int name_len;
zval **val;
HashTable *ht_vars = NULL;
HashPosition pos;
zval **ret_val;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &name, &name_len) == FAILURE) {
RETURN_NULL();
}
if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&val) == SUCCESS) {
ht_vars = Z_ARRVAL_PP(val);
//此處需傳入大于name長度+1的值,因為字符串值后面需要'\0'
if (zend_hash_find(ht_vars, name, name_len+1, (void **)&ret_val) == SUCCESS) { RETURN_STRING(Z_STRVAL_PP(ret_val), 0);
}else{
RETURN_NULL();
}
}else{
RETURN_NULL();
}
}
8、包裝第三方庫
配置(config.m4)
SEARCH_PATH="/usr/local /usr" #lib搜索的目錄
SEARCH_FOR="/include/curl/curl.h" #lib頭文件的路徑
if test -r $PHP_LIBS/$SEARCH_FOR; then
LIBS_DIR=$PHP_LIBS
else # search default path list
AC_MSG_CHECKING([for libs files in default path])
for i in $SEARCH_PATH ; do
if test -r $i/$SEARCH_FOR; then
LIBS_DIR=$i #搜索到的lib的路徑
AC_MSG_RESULT(found in $i)
fi
done
fi
/*驗證lib是否存在*/
if test -z "$LIBS_DIR"; then
AC_MSG_RESULT([not found])
AC_MSG_ERROR([Please reinstall the libs distribution])
fi
/*編譯的時候添加lib的include目錄, -I/usr/include*/
PHP_ADD_INCLUDE($LIBS_DIR/include)
LIBNAME=curl #lib名稱
LIBSYMBOL=curl_version #lib的一個函數(shù),用來PHP_CHECK_LIBRARY驗證lib
/*驗證lib*/
PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
[
PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $LIBS_DIR/$PHP_LIBDIR, LIBS_SHARED_LIBADD) #編譯的時候鏈接lib, -llibcurl
AC_DEFINE(HAVE_LIBSLIB,1,[ ])
],[
AC_MSG_ERROR([wrong libs lib version or lib not found])
],[
-L$LIBS_DIR/$PHP_LIBDIR -lm
])
PHP_SUBST(LIBS_SHARED_LIBADD)
9、用于返回的宏
//這些宏都定義在Zend/zend_API.h文件里
#define RETVAL_RESOURCE(l) ZVAL_RESOURCE(return_value, l)
#define RETVAL_BOOL(b) ZVAL_BOOL(return_value, b)
#define RETVAL_NULL() ZVAL_NULL(return_value)
#define RETVAL_LONG(l) ZVAL_LONG(return_value, l)
#define RETVAL_DOUBLE(d) ZVAL_DOUBLE(return_value, d)
#define RETVAL_STRING(s, duplicate) ZVAL_STRING(return_value, s, duplicate)
#define RETVAL_STRINGL(s, l, duplicate) ZVAL_STRINGL(return_value, s, l, duplicate)
#define RETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value)
#define RETVAL_ZVAL(zv, copy, dtor) ZVAL_ZVAL(return_value, zv, copy, dtor)
#define RETVAL_FALSE ZVAL_BOOL(return_value, 0)
#define RETVAL_TRUE ZVAL_BOOL(return_value, 1)
#define RETURN_RESOURCE(l) { RETVAL_RESOURCE(l); return; }
#define RETURN_BOOL(b) { RETVAL_BOOL(b); return; }
#define RETURN_NULL() { RETVAL_NULL(); return;}
#define RETURN_LONG(l) { RETVAL_LONG(l); return; }
#define RETURN_DOUBLE(d) { RETVAL_DOUBLE(d); return; }
#define RETURN_STRING(s, duplicate) { RETVAL_STRING(s, duplicate); return; }
#define RETURN_STRINGL(s, l, duplicate) { RETVAL_STRINGL(s, l, duplicate); return; }
#define RETURN_EMPTY_STRING() { RETVAL_EMPTY_STRING(); return; }
#define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; }
#define RETURN_FALSE { RETVAL_FALSE; return; }
#define RETURN_TRUE { RETVAL_TRUE; return; }
其實,除了這些標(biāo)量類型,還有很多php語言中的復(fù)合類型我們需要在函數(shù)中返回,如數(shù)組和對象,我們可以通過RETVAL_ZVAL與RETURN_ZVAL來操作它們
10、hashTable的遍歷函數(shù)
//基于long key的操作函數(shù)
zval *v3;
MAKE_STD_ZVAL(v3);
ZVAL_STRING(v3, "value3", 1);
zend_hash_index_update(names, 0, &v3, sizeof(zval *), NULL);//按數(shù)字索引鍵更新HashTable元素的值
zval **v4;
zend_hash_index_find(names, 1, &v4); //按數(shù)字索引獲取HashTable元素的值
php_printf("v4 : ");
PHPWRITE(Z_STRVAL_PP(v4), Z_STRLEN_PP(v4));
php_printf("\n");
ulong idx;
idx = zend_hash_index_exists(names, 10);//按數(shù)字索引查找HashTable,如果找到返回 1, 反之則返回 0
zend_hash_index_del(names, 2); //按數(shù)字索引刪除HashTable元素
//hashTable的遍歷函數(shù)
zend_hash_internal_pointer_reset(names); //初始化hash指針
zend_hash_internal_pointer_reset_ex(names, &pos);//初始化hash指針,并付值給pos
zend_hash_get_current_data(names, (void**) &val); //獲取當(dāng)前hash存儲值,data should be cast to void**, ie: (void**) &data
zend_hash_get_current_data_ex(names, (void**) &val, &pos) == SUCCESS; //獲取當(dāng)前hash存儲值
zend_hash_get_current_key(names, &key, &klen, &index, 0) == HASH_KEY_IS_LONG
zend_hash_get_current_key_ex(names, &key, &klen, &index, 0, &pos) == HASH_KEY_IS_LONG; //讀取hashtable當(dāng)前的KEY,返回值會有兩種 HASH_KEY_IS_LONG | HASH_KEY_IS_STRING ,分別對應(yīng)array("value"),array("key"=>"value")兩種hashtable
zend_hash_move_forward(names);
zend_hash_move_forward_ex(names, &pos); //hash指針移至下一位
//HashTable長度
php_printf("%*carray(%d) {\n", depth * 2, ' ', zend_hash_num_elements(Z_ARRVAL_P(zv))
一個簡單的函數(shù)
function hello_array_strings($arr) {
if (!is_array($arr)) return NULL;
printf("The array passed contains %d elements ", count($arr));
foreach($arr as $data) {
if (is_string($data)) echo "$data ";
}
}
PHP內(nèi)核實現(xiàn)
PHP_FUNCTION(hello_array_strings)
{
zval *arr, **data;
HashTable *arr_hash;
HashPosition pointer;
int array_count;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
RETURN_NULL();
}
arr_hash = Z_ARRVAL_P(arr);
array_count = zend_hash_num_elements(arr_hash);
php_printf("The array passed contains %d elements ", array_count);
for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) {
if (Z_TYPE_PP(data) == IS_STRING) {
PHPWRITE(Z_STRVAL_PP(data), Z_STRLEN_PP(data));
php_printf(" ");
}
}
RETURN_TRUE;
}
以上所述就是本文給大家介紹的PHP擴展開發(fā)教程,希望大家喜歡。
- PHP擴展開發(fā)入門教程
- PHP內(nèi)核介紹及擴展開發(fā)指南—基礎(chǔ)知識
- 初步介紹PHP擴展開發(fā)經(jīng)驗分享
- PHP7擴展開發(fā)教程之Hello World實現(xiàn)方法示例
- PHP7擴展開發(fā)之基于函數(shù)方式使用lib庫的方法詳解
- PHP7擴展開發(fā)之hello word實現(xiàn)方法詳解
- 快速開發(fā)一個PHP擴展圖文教程
- 詳解Window7 下開發(fā)php擴展
- 一個簡單php擴展介紹與開發(fā)教程
- 關(guān)于嘗試開發(fā)PHP的MYSQL擴展的使用
- windows下開發(fā)并編譯PHP擴展的方法
- php擴展開發(fā)入門demo示例
相關(guān)文章
Laravel 5使用Laravel Excel實現(xiàn)Excel/CSV文件導(dǎo)入導(dǎo)出的功能詳解
這篇文章主要給大家介紹了關(guān)于在Laravel 5中如何使用Laravel Excel實現(xiàn)Excel/CSV文件導(dǎo)入導(dǎo)出功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下。2017-10-10
Smarty foreach控制循環(huán)次數(shù)的一些方法
在win系統(tǒng)安裝配置 Memcached for PHP 5.3 圖文教程
PHP實現(xiàn)采集中國天氣網(wǎng)未來7天天氣

