Complex data types stored in resource variables often require some memory allocation, CPU time, or network communication during initialization. But retaining resources like database connections between requests must be durable. Durability of resources is a factor that must be considered.
First look at the issue of memory allocation:
When using PHP, I prefer to use emalloc because it is the recycled version of malloc. But persistent resources must exist between requests. For a file handle resource, if you want to add a requirement to store file names, you must add the following code to the header file:
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; } <strong>fdata = emalloc(sizeof(php_sample_descriptor_data)); //给包含了文件资源和文件名的结构分配空间 fdata->fp = fp; fdata->filename = estrndup(filename, filename_len);</strong> 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(); } <strong>ZEND_FETCH_RESOURCE(fdata, php_sample_descriptor_data*, &file_resource, -1, PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor);</strong> 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(); } <strong>ZEND_FETCH_RESOURCE(fdata, php_sample_descriptor_data*, &file_resource, -1, PHP_SAMPLE_DESCRIPTOR_RES_NAME, le_sample_descriptor);</strong> 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) {<strong>//这是一个持久化的资源析构函数</strong> php_sample_descriptor_data *fdata = (php_sample_descriptor_data*)rsrc->ptr; <strong>fclose(fdata->fp); pefree(fdata->filename, 1); pefree(fdata, 1);</strong> } PHP_MINIT_FUNCTION(sample) { le_sample_descriptor = zend_register_list_destructors_ex( php_sample_descriptor_dtor, NULL, PHP_SAMPLE_DESCRIPTOR_RES_NAME, module_number); <strong>le_sample_descriptor_persist = zend_register_list_destructors_ex( NULL, php_sample_descriptor_dtor_persistent, PHP_SAMPLE_DESCRIPTOR_RES_NAME, module_number);//注册一个持久化的资源</strong> 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; } <strong>/* 通过获得一个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; }</strong> 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); //这里会去调用之前注册好的析构函数 }