在資源變量中存儲的復雜的數據類型通常在初始化時需要一些內存分配,CPU時間或網絡通信。但是在請求之間保留類似於數據庫連接這種資源,必須要做到持久。資源是否持久是一個必須要考慮到的因素。
首先看內存分配的問題:
在使用php的時候,偏向使用emalloc因為它是malloc的帶回收的版本。但是持久化的資源必須在請求間都存在。對於一個文件句柄類的資源來說,如果要加入一個存儲文件名的需求,那麼必須在頭文件中加入如下的代碼:
typedef struct _php_sample_descriptor_data { char *filename; FILE *fp; } php_sample_descriptor_data;利用這個結構可以存儲文件名和文件句柄資源,從而能夠在不同的請求之間進行共享。
static void php_sample_descriptor_dtor( //這個是進行資源回收的回調函數,定義在資源的初始化處。 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); }這個靜態函數用來進行資源的回收,需要在初始化資源的時候進行指定回調。
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) {// 獲取文件名和文件長度 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)); //給包含了文件資源和文件名的結構分配空間 fdata->fp = fp; fdata->filename = estrndup(filename, filename_len); ZEND_REGISTER_RESOURCE(return_value, fdata, le_sample_descriptor); // 注冊資源 }
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)); }
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); }
static int le_sample_descriptor_persist; static void php_sample_descriptor_dtor_persistent( zend_rsrc_list_entry *rsrc TSRMLS_DC) {//這是一個持久化的資源析構函數 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);//注冊一個持久化的資源 return SUCCESS; }
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,"ss|b", &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);//這個做了申請內存和賦值兩步操作 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存儲一份副本 */ 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); } }
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,"ss|b", &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; } /* 通過獲得一個hash_key嘗試尋找一個已經打開的文件 */ 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) { /* 成功的找到了這個已經打開的文件句柄資源 */ 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); }
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); //這裡會去調用之前注冊好的析構函數 }