ホームページ  >  記事  >  php教程  >  PHP 拡張機能と埋め込み - リソース データ タイプ 2

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

WBOY
WBOYオリジナル
2016-06-21 08:48:19945ブラウズ

リソース変数に格納される複雑なデータ型は、多くの場合、初期化中にメモリ割り当て、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,"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);//这个做了申请内存和赋值两步操作 
        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);
    }
} 

非永続リソースの場合、数値インデックスが指定され、リクエスト依存のリストに保存されます。 永続リソースの場合、キー値タイプを指定すると、後続のリクエストでハッシュキーを取得できます。次に、リソースを永続リストに追加します。永続リソースがスコープ外になると、EG (regulator_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,"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;
    }
    <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); //这里会去调用之前注册好的析构函数
}











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