ホームページ >バックエンド開発 >PHPチュートリアル >PHP 拡張機能と埋め込みリソース データ タイプ 2

PHP 拡張機能と埋め込みリソース データ タイプ 2

WBOY
WBOYオリジナル
2016-06-23 13:58:24906ブラウズ

リソース変数に格納される複雑なデータ型は、通常、初期化中にメモリ割り当て、CPU 時間、またはネットワーク通信を必要とします。ただし、リクエスト間のデータベース接続などのリソースの保持には耐久性が必要です。リソースの耐久性は考慮する必要がある要素です。


まずメモリ割り当ての問題を見てみましょう:

PHP を使用するときは、malloc の再利用バージョンである emalloc を使用することを好みます。ただし、リクエスト間に永続的なリソースが存在する必要があります。ファイル ハンドル リソースの場合、ファイル名を保存する要件を追加する場合は、次のコードをヘッダー ファイルに追加する必要があります:

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 を格納する変数が設定解除されるかスコープ外になると、それらは EG (regulator_list) から削除されます。 EGで使用されるインデックス(persistent_list)はkey-value型であり、リクエスト終了時に要素は自動的に削除されません。 zend_hash_del() が呼び出された場合、またはスレッド/プロセスが完全にシャットダウンされた場合にのみ削除されます。

EG(persistent_list) にも dtor メソッドがありますが、これは zend_register_list_descructors_ex() の 2 番目のパラメータです。一般に、非永続リソースと永続リソースは 2 種類として登録されますが、1 つに結合される場合もあります。次に、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);    }} 

非永続リソースの場合、数値インデックスが指定され、リクエストに依存する in リストに格納されます。

永続リソースの場合、キー値タイプを指定すると、後続のリクエストでハッシュキーを取得できます。次に、リソースを永続リストに追加します。 永続リソースがスコープ外になると、EG (regulator_list) のデストラクターは le_sample_descriptro_persist の registerlist デストラクターをチェックします。 NULL であることが判明した場合、操作は実行されません。これにより、永続的なリソースが解放されなくなります。 リソースが EG (persistent_list) から削除されると、スレッド プロセスが終了するか、意図的に削除されます。この時点で、永続的なデストラクターを探します。



リソースが永続的として適用される理由は、他のリクエストで 再利用するためです:


永続的なリソースを再利用したい場合は、sample_fopen が呼び出されるときに、それらの hash_key を使用する必要があります。関数は、要求されたファイル名とパターンを使用して 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); //这里会去调用之前注册好的析构函数}








   


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。