php扩展与嵌入--资源数据类型2
在资源变量中存储的复杂的数据类型通常在初始化时需要一些内存分配,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; } <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); // 注册资源}
对于文件写入函数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(); } <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));}
对于sample_fclose函数并不需要改变什么,因为它没有操作实际的资源。下面这个函数可以从资源中拿到原本的文件名:
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);}
在完成了内存分配之后,由于必须保持持久化,所以必须延迟析构:
对于非持久的资源来说,一旦存放着资源id的变量被unset或fallen out of scope了,那么它们就被从EG(regular_list)中去除掉了。而EG(persistent_list)中使用的索引是键值类的,元素在请求的最后不会不会被自动的去除掉。只有在zend_hash_del()调用或线程/进程完全关闭的情况下才会消除。
EG(persistent_list)也有dtor方法,但是是zend_register_list_descructors_ex()的第二个参数。一般来说,非持久和持久的资源会被注册成两种类型,有的时候也可以合二为一。现在在sample.c中添加一个持久的资源类型。
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;}
下面的这个fopen函数就兼容了持久与非持久的两个资源类型:
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); }}
对于非持久化的资源,给定了一个数字的索引,并存放在了跟请求依存的list中。
对于持久化的资源,给定了一个键值类型,这个hashkey可以在接下来的请求中被重新得到。然后把资源放进了persistentlist中。当一个持久的资源out of scope的时候,EG(regular_list)的析构函数会为le_sample_descriptro_persist检查registerlist析构。发现是NULL的话不会有任何的操作。从而也就保证了持久的资源不会被释放掉。当资源被从EG(persistent_list)中去除的时候,要么是线程进程结束了,要么是故意删除掉了。这时候就会去找持久化的析构函数。
资源被申请为持久化的原因就是为了在其他的请求中可以复用:
如果想要复用持久化的资源,那就一定要用到hash_key,当sample_fopen被调用的时候,函数会利用请求的文件名和模式重新创建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,"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);}
注意由于所有的扩展都使用相同的哈希表单去存储资源,所以命名很重要。一般都是用扩展和资源类型名作为前缀。
检查资源可用性:
尽管像文件这种资源可以长期打开,但是类似远程网络资源这种如果在请求之间长期不用的话就有问题。所以在使用一个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); //这里会去调用之前注册好的析构函数}

절대 세션 시간 초과는 세션 생성시 시작되며, 유휴 세션 시간 초과는 사용자가 작동하지 않아 시작합니다. 절대 세션 타임 아웃은 금융 응용 프로그램과 같은 세션 수명주기의 엄격한 제어가 필요한 시나리오에 적합합니다. 유휴 세션 타임 아웃은 사용자가 소셜 미디어와 같이 오랫동안 세션을 활성화하려는 응용 프로그램에 적합합니다.

서버 세션 고장은 다음 단계를 따라 해결할 수 있습니다. 1. 서버 구성을 확인하여 세션이 올바르게 설정되었는지 확인하십시오. 2. 클라이언트 쿠키를 확인하고 브라우저가 지원하는지 확인하고 올바르게 보내십시오. 3. Redis와 같은 세션 스토리지 서비스가 정상적으로 작동하는지 확인하십시오. 4. 올바른 세션 로직을 보장하기 위해 응용 프로그램 코드를 검토하십시오. 이러한 단계를 통해 대화 문제를 효과적으로 진단하고 수리 할 수 있으며 사용자 경험을 향상시킬 수 있습니다.

session_start () iscrucialinphpformanagingUsersessions.1) itiniteSanewsessionifnoneexists, 2) ResumesAnxistessions, and3) setSasessionCookieForContInuityAcrosrequests, enablingplicationsirecationSerauthenticationAndpersonalizestContent.

XSS 공격을 효과적으로 방지하고 사용자 세션 정보를 보호 할 수 있기 때문에 httponly 플래그를 설정하는 것은 세션 쿠키에 중요합니다. 구체적으로, 1) httponly 플래그는 JavaScript가 쿠키에 액세스하는 것을 방지합니다. 2) PHP 및 Flask에서 SetCookies 및 Make_response를 통해 깃발을 설정할 수 있습니다. 3) 모든 공격으로부터 방지 할 수는 없지만 전체 보안 정책의 일부가되어야합니다.

phpssessionssolvetheproblemofmainingstateacrossmultiplehtttprequestsbystoringdataontheserversociatingititwithauniquessessionid.1) theStoredAserver-side, 일반적으로, 일반적으로 and insessionsecietoretoretrievedata.2) sessionsenhances

phpsessionscanstorestrings, 숫자, 배열 및 객체 1.Strings : TextDatalikeUsernames.2.numbers : integorfloatsforcounters.3.arrays : listslikeshoppingcarts.4.objects : complexStructuresThatareserialized.

세션 재생은 세션 고정 공격의 경우 사용자가 민감한 작업을 수행 할 때 새 세션 ID를 생성하고 이전 ID를 무효화하는 것을 말합니다. 구현 단계에는 다음이 포함됩니다. 1. 민감한 작업 감지, 2. 새 세션 ID 생성, 3. 오래된 세션 ID 파괴, 4. 사용자 측 세션 정보 업데이트.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

맨티스BT
Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

VSCode Windows 64비트 다운로드
Microsoft에서 출시한 강력한 무료 IDE 편집기

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)