|
您的位置: 首頁(yè) > 網(wǎng)站資訊 > php擴(kuò)展與嵌入--資源數(shù)據(jù)類(lèi)型2 |
php擴(kuò)展與嵌入--資源數(shù)據(jù)類(lèi)型2發(fā)布日期:2017/7/26
在資源變量中存儲(chǔ)的復(fù)雜的數(shù)據(jù)類(lèi)型通常在初始化時(shí)需要一些內(nèi)存分配,CPU時(shí)間或網(wǎng)絡(luò)通信。但是在請(qǐng)求之間保留類(lèi)似于數(shù)據(jù)庫(kù)連接這種資源,必須要做到持久。資源是否持久是一個(gè)必須要考慮到的因素。
首先看內(nèi)存分配的問(wèn)題: 在使用php的時(shí)候,偏向使用emalloc因?yàn)樗莔alloc的帶回收的版本。但是持久化的資源必須在請(qǐng)求間都存在。對(duì)于一個(gè)文件句柄類(lèi)的資源來(lái)說(shuō),假如要加入一個(gè)存儲(chǔ)文件名的需求,那么必須在頭文件中加入如下的代碼: typedef struct _php_sample_descriptor_data { char *filename; FILE *fp; } php_sample_descriptor_data;行使這個(gè)結(jié)構(gòu)可以存儲(chǔ)文件名和文件句柄資源,從而能夠在不同的請(qǐng)求之間進(jìn)行共享。 對(duì)應(yīng)的,要在源文件中進(jìn)行響應(yīng)的更改: static void php_sample_descriptor_dtor( //這個(gè)是進(jìn)行資源回收的回調(diào)函數(shù),定義在資源的初始化處。 zend_rsrc_list_entry *rsrc TSRMLS_DC) { php_sample_descriptor_data *fdata = (php_sample_descriptor_data*)rsrc->ptr; fclose(fdata->fp); efree(fdata->filename); efree(fdata); }這個(gè)靜態(tài)函數(shù)用來(lái)進(jìn)行資源的回收,需要在初始化資源的時(shí)候進(jìn)行指定回調(diào)。 進(jìn)行修改后的文件打開(kāi)函數(shù),需要增添給資源分配空間的操作: PHP_FUNCTION(sample_fopen) //修改后的fopen { php_sample_descriptor_data *fdata; FILE *fp; char *filename, *mode; int filename_len, mode_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &filename, &filename_len, &mode, &mode_len) == FAILURE) {// 獲取文件名和文件長(zhǎng)度 RETURN_NULL(); } if (!filename_len !mode_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filename or mode length"); RETURN_FALSE; } fp = fopen(filename, mode); if (!fp) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s using mode %s", filename, mode); RETURN_FALSE; } fdata = emalloc(sizeof(php_sample_descriptor_data)); //給包含了文件資源和文件名的結(jié)構(gòu)分配空間 fdata->fp = fp; fdata->filename = estrndup(filename, filename_len); ZEND_REGISTER_RESOURCE(return_value, fdata, le_sample_descriptor); // 注冊(cè)資源 } 對(duì)于文件寫(xiě)入函數(shù)fwrite同樣需要修改: PHP_FUNCTION(sample_fwrite) { php_sample_descriptor_data *fdata; zval *file_resource; char *data; int data_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &file_resource, &data, &data_len) == FAILURE ) { RETURN_NULL(); } ZEND_FETCH_RESOURCE(fdata, php_sample_descriptor_data*, &file_resource, -1, PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor); RETURN_LONG(fwrite(data, 1, data_len, fdata->fp)); } 對(duì)于sample_fclose函數(shù)并不需要改變什么,因?yàn)樗鼪](méi)有操作現(xiàn)實(shí)的資源。下面這個(gè)函數(shù)可以從資源中拿到原本的文件名: PHP_FUNCTION(sample_fname) { php_sample_descriptor_data *fdata; zval *file_resource; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &file_resource) == FAILURE ) { RETURN_NULL(); } ZEND_FETCH_RESOURCE(fdata, php_sample_descriptor_data*, &file_resource, -1, PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor); RETURN_STRING(fdata->filename, 1); } 在完成了內(nèi)存分配之后,因?yàn)楸仨毐3殖志没员仨?strong>延遲析構(gòu): 對(duì)于非持久的資源來(lái)說(shuō),一旦存放著資源id的變量被unset或fallen out of scope了,那么它們就被從EG(regular_list)中去除掉了。而EG(persistent_list)中使用的索引是鍵值類(lèi)的,元素在請(qǐng)求的不會(huì)不會(huì)被主動(dòng)的去除掉。只有在zend_hash_del()調(diào)用或線程/進(jìn)程完全關(guān)閉的情況下才會(huì)消弭。 EG(persistent_list)也有dtor方法,但是是zend_register_list_descructors_ex()的第二個(gè)參數(shù)。一般來(lái)說(shuō),非持久和持久的資源會(huì)被注冊(cè)成兩種類(lèi)型,有的時(shí)候也可以合二為一。現(xiàn)在在sample.c中添加一個(gè)持久的資源類(lèi)型。 static int le_sample_descriptor_persist; static void php_sample_descriptor_dtor_persistent( zend_rsrc_list_entry *rsrc TSRMLS_DC) {//這是一個(gè)持久化的資源析構(gòu)函數(shù) php_sample_descriptor_data *fdata = (php_sample_descriptor_data*)rsrc->ptr; fclose(fdata->fp); pefree(fdata->filename, 1); pefree(fdata, 1); } PHP_MINIT_FUNCTION(sample) { le_sample_descriptor = zend_register_list_destructors_ex( php_sample_descriptor_dtor, NULL, PHP_SAMPLE_DESCRIPTOR_RES_NAME, module_number); le_sample_descriptor_persist = zend_register_list_destructors_ex( NULL, php_sample_descriptor_dtor_persistent, PHP_SAMPLE_DESCRIPTOR_RES_NAME, module_number);//注冊(cè)一個(gè)持久化的資源 return SUCCESS; } 下面的這個(gè)fopen函數(shù)就兼容了持久與非持久的兩個(gè)資源類(lèi)型: PHP_FUNCTION(sample_fopen) { php_sample_descriptor_data *fdata; FILE *fp; char *filename, *mode; int filename_len, mode_len; zend_bool persist = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"ssb", &filename, &filename_len, &mode, &mode_len, &persist) == FAILURE) { RETURN_NULL(); } if (!filename_len !mode_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filename or mode length"); RETURN_FALSE; } fp = fopen(filename, mode); if (!fp) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s using mode %s", filename, mode); RETURN_FALSE; } if (!persist) {//非持久化的資源 fdata = emalloc(sizeof(php_sample_descriptor_data)); fdata->filename = estrndup(filename, filename_len);//這個(gè)做了申請(qǐng)內(nèi)存和賦值兩步操作 fdata->fp = fp; ZEND_REGISTER_RESOURCE(return_value, fdata, le_sample_descriptor); } else {//持久化的資源 list_entry le; char *hash_key; int hash_key_len; fdata =pemalloc(sizeof(php_sample_descriptor_data),1); fdata->filename = pemalloc(filename_len + 1, 1); memcpy(fdata->filename, filename, filename_len + 1); fdata->fp = fp; ZEND_REGISTER_RESOURCE(return_value, fdata, le_sample_descriptor_persist); /* Store a copy in the persistent_list 在persistent_list存儲(chǔ)一份副本 */ le.type = le_sample_descriptor_persist; le.ptr = fdata; hash_key_len = spprintf(&hash_key, 0, "sample_descriptor:%s:%s", filename, mode); zend_hash_update(&EG(persistent_list), hash_key, hash_key_len + 1, (void*)&le, sizeof(list_entry), NULL); efree(hash_key); } } 對(duì)于非持久化的資源,給定了一個(gè)數(shù)字的索引,并存放在了跟請(qǐng)求依存的list中。 對(duì)于持久化的資源,給定了一個(gè)鍵值類(lèi)型,這個(gè)hashkey可以在接下來(lái)的請(qǐng)求中被重新得到。然后把資源放進(jìn)了persistentlist中。當(dāng)一個(gè)持久的資源out of scope的時(shí)候,EG(regular_list)的析構(gòu)函數(shù)會(huì)為le_sample_descriptro_persist檢查registerlist析構(gòu)。發(fā)現(xiàn)是NULL的話不會(huì)有任何的操作。從而也就保證了持久的資源不會(huì)被釋放掉。當(dāng)資源被從EG(persistent_list)中去除的時(shí)候,要么是線程進(jìn)程結(jié)束了,要么是有心刪除掉了。這時(shí)候就會(huì)去找持久化的析構(gòu)函數(shù)。 資源被申請(qǐng)為持久化的原因就是為了在其他的請(qǐng)求中可以復(fù)用: 假如想要復(fù)用持久化的資源,那就一定要用到hash_key,當(dāng)sample_fopen被調(diào)用的時(shí)候,函數(shù)會(huì)行使請(qǐng)求的文件名和模式重新創(chuàng)建hash_key,然后嘗試在persistent_list中找到它。 PHP_FUNCTION(sample_fopen) { php_sample_descriptor_data *fdata; FILE *fp; char *filename, *mode, *hash_key; int filename_len, mode_len, hash_key_len; zend_bool persist = 0; //判斷是否持久 list_entry *existing_file; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"ssb", &filename, &filename_len, &mode, &mode_len, &persist) == FAILURE) { RETURN_NULL(); } if (!filename_len !mode_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filename or mode length"); RETURN_FALSE; } /* 通過(guò)獲得一個(gè)hash_key嘗試尋找一個(gè)已經(jīng)打開(kāi)的文件 */ hash_key_len = spprintf(&hash_key, 0, "sample_descriptor:%s:%s", filename, mode); if (zend_hash_find(&EG(persistent_list), hash_key, hash_key_len + 1, (void **)&existing_file) == SUCCESS) { /* 成功的找到了這個(gè)已經(jīng)打開(kāi)的文件句柄資源 */ ZEND_REGISTER_RESOURCE(return_value, existing_file->ptr, le_sample_descriptor_persist); efree(hash_key); return; } fp = fopen(filename, mode); if (!fp) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s using mode %s", filename, mode); RETURN_FALSE; } if (!persist) { fdata = emalloc(sizeof(php_sample_descriptor_data)); fdata->filename = estrndup(filename, filename_len); fdata->fp = fp; ZEND_REGISTER_RESOURCE(return_value, fdata, le_sample_descriptor); } else { list_entry le; fdata =pemalloc(sizeof(php_sample_descriptor_data),1); fdata->filename = pemalloc(filename_len + 1, 1); memcpy(data->filename, filename, filename_len + 1); fdata->fp = fp; ZEND_REGISTER_RESOURCE(return_value, fdata, le_sample_descriptor_persist); /* Store a copy in the persistent_list */ le.type = le_sample_descriptor_persist; le.ptr = fdata; /* hash_key has already been created by now */ zend_hash_update(&EG(persistent_list), hash_key, hash_key_len + 1, (void*)&le, sizeof(list_entry), NULL); } efree(hash_key); } 注重因?yàn)樗械臄U(kuò)展都使用相同的哈希表單去存儲(chǔ)資源,所以命名很主要。一般都是用擴(kuò)展和資源類(lèi)型名作為前綴。 檢查資源可用性: 盡管像文件這種資源可以長(zhǎng)期打開(kāi),但是類(lèi)似遠(yuǎn)程網(wǎng)絡(luò)資源這種假如在請(qǐng)求之間長(zhǎng)期不用的話就有問(wèn)題。所以在使用一個(gè)persistent資源之前,要先確定可用性。 if (zend_hash_find(&EG(persistent_list), hash_key, hash_key_len + 1, (void**)&socket) == SUCCESS) { if (php_sample_socket_is_alive(socket->ptr)) { ZEND_REGISTER_RESOURCE(return_value, socket->ptr, le_sample_socket); return; } zend_hash_del(&EG(persistent_list), hash_key, hash_key_len + 1); //這里會(huì)去調(diào)用之前注冊(cè)好的析構(gòu)函數(shù) } |
其他相關(guān)文章 |
|
|
|
||||||||
Copyright 2012-2025 上海蒙狼網(wǎng)絡(luò)科技有限公司 www.zcrhy.com.cn All Rights Reserved |