国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

Home Backend Development PHP Tutorial [Translation] [php extension development and embedding] Chapter 15 - Implementation of streams in php

[Translation] [php extension development and embedding] Chapter 15 - Implementation of streams in php

Feb 10, 2017 am 10:21 AM
php


Implementing streams

#One of the most powerful features of PHP's streams is that it can access many Data sources: ordinary files, compressed files, network transparent channels, encrypted networks, named pipes and domain sockets, which are unified APIs for user space and internally.

Under the surface of php stream

For a given stream instance, such as a file stream and a network stream, the difference is that the stream creation function you used in the previous chapter returns The ops member in the php_stream structure.

typedef struct _php_stream {
    ...
    php_stream_ops *ops;
    ...
} php_stream;

The php_stream_ops structure defines a collection of function pointers and a description tag.

typedef struct _php_stream_ops {
    size_t (*write)(php_stream *stream, const char *buf,
                            size_t count TSRMLS_DC);
    size_t (*read)(php_stream *stream, char *buf,
                            size_t count TSRMLS_DC);
    int    (*close)(php_stream *stream, int close_handle
                            TSRMLS_DC);
    int    (*flush)(php_stream *stream TSRMLS_DC);

    const char *label;

    int (*seek)(php_stream *stream, off_t offset, int whence,
                            off_t *newoffset TSRMLS_DC);
    int (*cast)(php_stream *stream, int castas, void **ret
                            TSRMLS_DC);
    int (*stat)(php_stream *stream, php_stream_statbuf *ssb
                            TSRMLS_DC);
    int (*set_option)(php_stream *stream, int option,int value,
                            void *ptrparam TSRMLS_DC);
} php_stream_ops;

When the stream access function such as php_stream_read() is called, the stream packaging layer actually parses and calls the corresponding function in stream->ops, so that actually What is called is the read implementation specific to the current stream type. For example, the read function in the stream ops structure of an ordinary file is implemented as follows (the actual implementation is a bit more complicated than the example below):

size_t php_stdio_read(php_stream *stream, char *buf,
                                size_t count TSRMLS_DC)
{
    php_stdio_stream_data *data =
                (php_stdio_stream_data*)stream->abstract;
    return read(data->fd, buf, count);
}

In the ops structure used by the compress.zlib stream, read points to the following function:

size_t php_zlib_read(php_stream *stream, char *buf,
                                size_t count TSRMLS_DC)
{
    struct php_gz_stream_data_t *data =
            (struct php_gz_stream_data_t *) stream->abstract;

    return gzread(data->gz_file, buf, count);
}

The first thing to note here is that the function pointer pointed to by the ops structure is often a thin representation of the actual reading function of the data source. Proxy. In the above two examples, the standard I/O stream uses posix's read() function, while the zlib stream uses libz's gzread() function.

You may also Notice that the stream->abstract element is used here. This is a convenience pointer to the stream implementation, which can be used to obtain various related bundle information. In the above example, the pointer to the custom structure is used To store the file descriptor to be used by the underlying read function.

Another thing you may notice is that each function in the php_stream_ops structure expects an existing stream instance , but how to get the instance? How are the abstract members set up and when the stream directive uses which ops structure? The answer lies in the first open stream function you used in the previous chapter (php_stream_open_wrapper()).

When this function is called, PHP's stream wrapper layer attempts to determine what protocol is being requested based on the scheme:// part of the passed URL. This way it can be used in the registered Find the corresponding php_stream_wrapper item in the php wrapper. Each php_stream_wrapper structure can get its own ops element, which points to a php_stream_wrapper_ops structure:

typedef struct _php_stream_wrapper_ops {
    php_stream *(*stream_opener)(php_stream_wrapper *wrapper,
                        char *filename, char *mode,
                        int options, char **opened_path,
                        php_stream_context *context
                        STREAMS_DC TSRMLS_DC);
    int (*stream_closer)(php_stream_wrapper *wrapper,
                        php_stream *stream TSRMLS_DC);
    int (*stream_stat)(php_stream_wrapper *wrapper,
                        php_stream *stream,
                        php_stream_statbuf *ssb
                        TSRMLS_DC);
    int (*url_stat)(php_stream_wrapper *wrapper,
                        char *url, int flags,
                        php_stream_statbuf *ssb,
                        php_stream_context *context
                        TSRMLS_DC);
    php_stream *(*dir_opener)(php_stream_wrapper *wrapper,
                        char *filename, char *mode,
                        int options, char **opened_path,
                        php_stream_context *context
                        STREAMS_DC TSRMLS_DC);

    const char *label;

    int (*unlink)(php_stream_wrapper *wrapper, char *url,
                        int options,
                        php_stream_context *context
                        TSRMLS_DC);

    int (*rename)(php_stream_wrapper *wrapper,
                        char *url_from, char *url_to,
                        int options,
                        php_stream_context *context
                        TSRMLS_DC);


    int (*stream_mkdir)(php_stream_wrapper *wrapper,
                        char *url, int mode, int options,
                        php_stream_context *context
                        TSRMLS_DC);
    int (*stream_rmdir)(php_stream_wrapper *wrapper, char *url,
                        int options,
                        php_stream_context *context
                        TSRMLS_DC);
} php_stream_wrapper_ops;

Here, the stream packaging layer calls wrapper->ops->stream_opener(), which will perform wrapper-specific operations to create a stream instance, assign the appropriate php_stream_ops structure, and bind related abstract data.

The dir_opener() function provides the same basic services as stream_opener(); however, it is in response to the php_stream_opendir() API call, and usually binds a different php_stream_ops structure to the returned instance. stat() and close() ) functions are repeated at this level to add protocol-specific logic to these operations of the wrapper.

Other functions allow static stream operations to be performed without Actually create the stream instance. Looking back at these stream API calls, they do not actually return php_stream objects, you will see the details of them shortly.

Although url_stat existed internally as a wrapper's ops function when the stream wrapping layer was introduced in PHP 4.3, it was not used until PHP 5.0. Additionally, the last 3 functions, rename(), stream_mkdir() and stream_rmdir() were not introduced until PHP 5.0. Before this version, they were not in the ops structure of the wrapper.

Wrapper operation

Except the url_stat() function, every operation before the const char *label element in the wrapper operation can be used on the activated stream instance. The meaning of each function is as follows:

stream_opener()instantiate a stream instance. When a user-space fopen() function is called, this function

number will be called. The php_stream instance returned by this function is the

## returned by the fopen() function. # Internal representation of file resource handle. Integrated functions such as file(), file_get_contents(),

file_put_contents(), readfile(), etc., This package is used when requesting packaging resources

installer ops.

stream_closer() This function is called when a stream instance ends its life cycle. When stream_opener()

All resources allocated should be called in this function Release.

stream_stat()Similar to the fstat() function in user space, this function should fill the ssb structure (actually

Contains only one struct statbuf sb structure member),

dir_opener() behaves the same as stream_opener(), However, it is called when calling the user space

function of the opendir() family. The underlying stream implementation used by the directory stream follows the same rules as the file stream;

#But directory streaming only needs to return records containing the file names found in the open directory, which

The size of is the size of the structure struct dirent.

Static wrapper operation

Other functions in the wrapper operation function perform atomic operations on the URI path, depending on the wrapper protocol. In the php_stream_wrapper_ops structure of php4.3, there are only url_stat() and unlink(); the other methods are to php 5.0 It is defined later. #ifdef block description should be used when encoding.

url_stat()stat() family function is used to return file metadata, such as Access authorization, size, type; and

#access, modification, creation time. Although this function appeared when PHP 4.3 introduced the flow wrapping layer

在php_stream_wrapper_ops結(jié)構(gòu)體中的, 但直到php 5.0才被用戶空

間的stat()函數(shù)使用.

unlink()和posix文件系統(tǒng)的同名函數(shù)語義相同, 它執(zhí)行文件刪除. 如果對(duì)于當(dāng)

前的包裝器刪除沒有意義, 比如內(nèi)建的http://包裝器, 這個(gè)函數(shù)應(yīng)該被

定義為NULL, 以便內(nèi)核去引發(fā)適當(dāng)?shù)腻e(cuò)誤消息.

rename()當(dāng)用戶空間的rename()函數(shù)的參數(shù)$from和$to參數(shù)指向的是相同的

底層包裝器實(shí)現(xiàn), php則將這個(gè)重命名請(qǐng)求分發(fā)到包裝器的rename函

數(shù).

mkdir() & rmdir()這兩個(gè)函數(shù)直接映射到對(duì)應(yīng)的用戶空間函數(shù).

實(shí)現(xiàn)一個(gè)包裝器

為了演示包裝器和流操作的內(nèi)部工作原理, 我們需要重新實(shí)現(xiàn)php手冊(cè)的stream_wrapper_register()一頁示例中的var://包裝器.

此刻, 首先從下面功能完整的變量流包裝實(shí)現(xiàn)開始. 構(gòu)建他, 并開始檢查每一塊的工作原理.

譯注: 為了方便大家閱讀, 對(duì)代碼的注釋進(jìn)行了適量補(bǔ)充調(diào)整, 此外, 由于phpapi的調(diào)整, 原著中的代碼不能直接在譯者使用的php-5.4.10中運(yùn)行, 進(jìn)行了適當(dāng)?shù)男薷? 因此下面代碼結(jié)構(gòu)可能和原著略有不同, 請(qǐng)參考閱讀.(下面opendir的例子也進(jìn)行了相應(yīng)的修改)

config.m4

PHP_ARG_ENABLE(varstream,whether to enable varstream support,
[  enable-varstream      Enable varstream support])

if test "$PHP_VARSTREAM" = "yes"; then
  AC_DEFINE(HAVE_VARSTREAM,1,[Whether you want varstream])
  PHP_NEW_EXTENSION(varstream, varstream.c, $ext_shared)
fi

php_varstream.h

#ifndef PHP_VARSTREAM_H
#define PHP_VARSTREAM_H

extern zend_module_entry varstream_module_entry;
#define phpext_varstream_ptr &varstream_module_entry

#ifdef PHP_WIN32
#   define PHP_VARSTREAM_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#   define PHP_VARSTREAM_API __attribute__ ((visibility("default")))
#else
#   define PHP_VARSTREAM_API
#endif

#ifdef ZTS
#include "TSRM.h"
#endif

PHP_MINIT_FUNCTION(varstream);
PHP_MSHUTDOWN_FUNCTION(varstream);

#define PHP_VARSTREAM_WRAPPER       "var"
#define PHP_VARSTREAM_STREAMTYPE    "varstream"

/* 變量流的抽象數(shù)據(jù)結(jié)構(gòu) */
typedef struct _php_varstream_data {
    off_t   position;
    char    *varname;
    int     varname_len;
} php_varstream_data;

#ifdef ZTS
#define VARSTREAM_G(v) TSRMG(varstream_globals_id, zend_varstream_globals *, v)
#else
#define VARSTREAM_G(v) (varstream_globals.v)
#endif

#endif

varstream.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/standard/url.h"
#include "php_varstream.h"

static size_t php_varstream_write(php_stream *stream,
                const char *buf, size_t count TSRMLS_DC)
{
    php_varstream_data *data = stream->abstract;
    zval **var;
    size_t newlen;

    /* 查找變量 */
    if (zend_hash_find(&EG(symbol_table), data->varname,
            data->varname_len + 1,(void**)&var) == FAILURE) {
        /* 變量不存在, 直接創(chuàng)建一個(gè)字符串類型的變量, 并保存新傳遞進(jìn)來的內(nèi)容 */
       zval *newval;
       MAKE_STD_ZVAL(newval);
       ZVAL_STRINGL(newval, buf, count, 1); 
       /* 將新的zval *放到變量中 */
       zend_hash_add(&EG(symbol_table), data->varname,
           data->varname_len + 1, (void*)&newval,
           sizeof(zval*), NULL);
       return count;
    }   
    /* 如果需要, 讓變量可寫. 這里實(shí)際上處理的是寫時(shí)復(fù)制 */
    SEPARATE_ZVAL_IF_NOT_REF(var);
    /* 轉(zhuǎn)換為字符串類型 */
    convert_to_string_ex(var);
    /* 重置偏移量(譯注: 相比于正常的文件系統(tǒng), 這里的處理實(shí)際上不支持文件末尾的空洞創(chuàng)建, 讀者如果熟悉*nix文件系統(tǒng), 應(yīng)該了解譯者所說, 否則請(qǐng)略過) */
    if (data->position > Z_STRLEN_PP(var)) {
        data->position = Z_STRLEN_PP(var);
    }   
    /* 計(jì)算新的字符串長(zhǎng)度 */
    newlen = data->position + count;
    if (newlen < Z_STRLEN_PP(var)) {
        /* 總長(zhǎng)度不變 */
        newlen = Z_STRLEN_PP(var);
    } else if (newlen > Z_STRLEN_PP(var)) {
        /* 重新調(diào)整緩沖區(qū)大小以保存新內(nèi)容 */
        Z_STRVAL_PP(var) =erealloc(Z_STRVAL_PP(var),newlen+1);
        /* 更新字符串長(zhǎng)度 */
        Z_STRLEN_PP(var) = newlen;
        /* 確保字符串NULL終止 */
        Z_STRVAL_PP(var)[newlen] = 0;
    }   
    /* 將數(shù)據(jù)寫入到變量中 */
    memcpy(Z_STRVAL_PP(var) + data->position, buf, count);
    data->position += count;

    return count;
}

static size_t php_varstream_read(php_stream *stream,
                char *buf, size_t count TSRMLS_DC)
{
    php_varstream_data *data = stream->abstract;
    zval **var, copyval;
    int got_copied = 0;
    size_t toread = count;

    if (zend_hash_find(&EG(symbol_table), data->varname,
        data->varname_len + 1, (void**)&var) == FAILURE) {
        /* 變量不存在, 讀不到數(shù)據(jù), 返回0字節(jié)長(zhǎng)度 */
        return 0;
    }   
    copyval = **var;
    if (Z_TYPE(copyval) != IS_STRING) {
        /* 對(duì)于非字符串類型變量, 創(chuàng)建一個(gè)副本進(jìn)行讀, 這樣對(duì)于只讀的變量, 就不會(huì)改變其原始類型 */
        zval_copy_ctor(?val);
        INIT_PZVAL(?val);
        got_copied = 1;
    }   
    if (data->position > Z_STRLEN(copyval)) {
        data->position = Z_STRLEN(copyval);
    }   
    if ((Z_STRLEN(copyval) - data->position) < toread) {
        /* 防止讀取到變量可用緩沖區(qū)外的內(nèi)容 */
        toread = Z_STRLEN(copyval) - data->position;
    }   
    /* 設(shè)置緩沖區(qū) */
    memcpy(buf, Z_STRVAL(copyval) + data->position, toread);
    data->position += toread;

    /* 如果創(chuàng)建了副本, 則釋放副本 */
    if (got_copied) {
        zval_dtor(?val);
    }   

    /* 返回設(shè)置到緩沖區(qū)的字節(jié)數(shù) */
    return toread;
}

static int php_varstream_closer(php_stream *stream,
                            int close_handle TSRMLS_DC)
{
    php_varstream_data *data = stream->abstract;

    /* 釋放內(nèi)部結(jié)構(gòu)避免泄露 */
    efree(data->varname);
    efree(data);

    return 0;
}

static int php_varstream_flush(php_stream *stream TSRMLS_DC)
{
    php_varstream_data *data = stream->abstract;
    zval **var;

    /* 根據(jù)不同情況, 重置偏移量 */
    if (zend_hash_find(&EG(symbol_table), data->varname,
                    data->varname_len + 1, (void**)&var)
                    == SUCCESS) {
        if (Z_TYPE_PP(var) == IS_STRING) {
            data->position = Z_STRLEN_PP(var);
        } else {
            zval copyval = **var;
            zval_copy_ctor(?val);
            convert_to_string(?val);
            data->position = Z_STRLEN(copyval);
            zval_dtor(?val);
        }
    } else {
        data->position = 0;
    }

    return 0;
}

static int php_varstream_seek(php_stream *stream, off_t offset,
                    int whence, off_t *newoffset TSRMLS_DC)
{
    php_varstream_data *data = stream->abstract;

    switch (whence) {
        case SEEK_SET:
            data->position = offset;
            break;
        case SEEK_CUR:
            data->position += offset;
            break;
        case SEEK_END:
        {
            zval **var;
           size_t curlen = 0;

           if (zend_hash_find(&EG(symbol_table),
                   data->varname,    data->varname_len + 1,
                   (void**)&var) == SUCCESS) {
              if (Z_TYPE_PP(var) == IS_STRING) {
                  curlen = Z_STRLEN_PP(var);
              } else {
                  zval copyval = **var;
                  zval_copy_ctor(?val);
                  convert_to_string(?val);
                  curlen = Z_STRLEN(copyval);
                  zval_dtor(?val);
              }
           }

           data->position = curlen + offset;
           break;
       }
    }

    /* 防止隨機(jī)訪問指針移動(dòng)到緩沖區(qū)開始位置之前 */
    if (data->position < 0) {
        data->position = 0;
    }

    if (newoffset) {
        *newoffset = data->position;
    }

    return 0;
}

static php_stream_ops php_varstream_ops = {
    php_varstream_write,
    php_varstream_read,
    php_varstream_closer,
    php_varstream_flush,
    PHP_VARSTREAM_STREAMTYPE,
    php_varstream_seek,
    NULL, /* cast */
    NULL, /* stat */
    NULL, /* set_option */
};

/* Define the wrapper operations */
static php_stream *php_varstream_opener(
            php_stream_wrapper *wrapper,
            char *filename, char *mode, int options,
            char **opened_path, php_stream_context *context
            STREAMS_DC TSRMLS_DC)
{
    php_varstream_data *data;
    php_url *url;

    if (options & STREAM_OPEN_PERSISTENT) {
        /* 按照變量流的定義, 是不能持久化的
         * 因?yàn)樽兞吭谡?qǐng)求結(jié)束后將被釋放
         */
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Unable to open %s persistently",
                                        filename);
        return NULL;
    }

    /* 標(biāo)準(zhǔn)URL解析: scheme://user:pass@host:port/path?query#fragment */
    url = php_url_parse(filename);
    if (!url) {
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Unexpected error parsing URL");
        return NULL;
    }
    /* 檢查是否有變量流URL必須的元素host, 以及scheme是否是var */
    if (!url->host || (url->host[0] == 0) ||
        strcasecmp("var", url->scheme) != 0) {
        /* Bad URL or wrong wrapper */
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Invalid URL, must be in the form: "
                     "var://variablename");
        php_url_free(url);
        return NULL;
    }

    /* 創(chuàng)建一個(gè)數(shù)據(jù)結(jié)構(gòu)保存協(xié)議信息(變量流協(xié)議重要是變量名, 變量名長(zhǎng)度, 當(dāng)前偏移量) */
    data = emalloc(sizeof(php_varstream_data));
    data->position = 0;
    data->varname_len = strlen(url->host);
    data->varname = estrndup(url->host, data->varname_len + 1);
    /* 釋放前面解析出來的url占用的內(nèi)存 */
    php_url_free(url);

    /* 實(shí)例化一個(gè)流, 為其賦予恰當(dāng)?shù)牧鱫ps, 綁定抽象數(shù)據(jù) */
    return php_stream_alloc(&php_varstream_ops, data, 0, mode);
}

static php_stream_wrapper_ops php_varstream_wrapper_ops = {
    php_varstream_opener, /* 調(diào)用php_stream_open_wrapper(sprintf("%s://xxx", PHP_VARSTREAM_WRAPPER))時(shí)執(zhí)行 */
    NULL, /* stream_close */
    NULL, /* stream_stat */
    NULL, /* url_stat */
    NULL, /* dir_opener */
    PHP_VARSTREAM_WRAPPER,
    NULL, /* unlink */
#if PHP_MAJOR_VERSION >= 5
    /* PHP >= 5.0 only */
    NULL, /* rename */
    NULL, /* mkdir */
    NULL, /* rmdir */
#endif
};

static php_stream_wrapper php_varstream_wrapper = {
    &php_varstream_wrapper_ops,
    NULL, /* abstract */
    0, /* is_url */
};

PHP_MINIT_FUNCTION(varstream)
{
    /* 注冊(cè)流包裝器:
     * 1. 檢查流包裝器名字是否正確(符合這個(gè)正則: /^[a-zA-Z0-9+.-]+$/)
     * 2. 將傳入的php_varstream_wrapper增加到url_stream_wrappers_hash這個(gè)HashTable中, key為PHP_VARSTREAM_WRAPPER
     */
    if (php_register_url_stream_wrapper(PHP_VARSTREAM_WRAPPER,
            &php_varstream_wrapper TSRMLS_CC)==FAILURE) {
        return FAILURE;
    }
    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(varstream)
{
    /* 卸載流包裝器: 從url_stream_wrappers_hash中刪除 */
    if (php_unregister_url_stream_wrapper(PHP_VARSTREAM_WRAPPER
                                TSRMLS_CC) == FAILURE) {
        return FAILURE;
    }
    return SUCCESS;
}

zend_module_entry varstream_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    "varstream",
    NULL,
    PHP_MINIT(varstream),
    PHP_MSHUTDOWN(varstream),
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    "0.1",
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_VARSTREAM
ZEND_GET_MODULE(varstream)
#endif

在構(gòu)建加載擴(kuò)展后, php就可以處理以var://開始的URL的請(qǐng)求, 它的行為和手冊(cè)中用戶空間實(shí)現(xiàn)的行為一致.

內(nèi)部實(shí)現(xiàn)

首先你注意到的可能是這個(gè)擴(kuò)展完全沒有暴露用戶空間函數(shù). 它所做的只是在MINIT函數(shù)中調(diào)用了一個(gè)核心PHPAPI的鉤子, 將var協(xié)議和我們定義的包裝器關(guān)聯(lián)起來:

static php_stream_wrapper php_varstream_wrapper = {
    &php_varstream_wrapper_ops,
    NULL, /* abstract */
    0, /* is_url */
}

很明顯, 最重要的元素就是ops, 它提供了訪問特定流包裝器的創(chuàng)建以及檢查函數(shù). 你可以安全的忽略abstract屬性, 它僅在運(yùn)行時(shí)使用, 在初始化定義時(shí), 它只是作為一個(gè)占位符. 第三個(gè)元素is_url, 它告訴php在使用這個(gè)包裝器時(shí)是否考慮php.ini中的allow_url_fopen選項(xiàng). 如果這個(gè)值非0, 并且將allow_url_fopen設(shè)置為false, 則這個(gè)包裝器不能被腳本使用.

在本章前面你已經(jīng)知道, 調(diào)用用戶空間函數(shù)比如fopen將通過這個(gè)包裝器的ops元素得到php_varstream_wrapper_ops, 這樣去調(diào)用流的打開函數(shù)php_varstream_opener.

這個(gè)函數(shù)的第一塊代碼檢查是否請(qǐng)求持久化的流:

if (options & STREAM_OPEN_PERSISTENT) {

對(duì)于很多包裝器這樣的請(qǐng)求是合法的. 然而目前的情況這個(gè)行為沒有意義. 一方面用戶空間變量的定義就是臨時(shí)的, 另一方面, varstream的實(shí)例化代價(jià)很低, 這就使得持久化的優(yōu)勢(shì)很小.

像流包裝層報(bào)告錯(cuò)誤很簡(jiǎn)單, 只需要返回一個(gè)NULL值而不是流實(shí)例即可. 流包裝層透出到用戶空間的失敗消息并不會(huì)說明具體的錯(cuò)誤, 只是說明不能打開URL. 要想給開發(fā)者暴露更多的錯(cuò)誤信息, 可以在返回之前使用php_stream_wrapper_log_error()函數(shù).

php_stream_wrapper_log_error(wrapper, options
    TSRMLS_CC, "Unable to open %s persistently",
                                filename);
return NULL;

URL解析

實(shí)例化varstream的下一步需要一個(gè)人類可讀的URL, 將它分塊放入到一個(gè)易管理的結(jié)構(gòu)體中. 幸運(yùn)的是它使用了和用戶空間url_parse()函數(shù)相同的機(jī)制. 如果URL成功解析, 將會(huì)分配一個(gè)php_url結(jié)構(gòu)體并設(shè)置合適的值. 如果在URL中沒有某些值, 在返回的php_url中對(duì)應(yīng)的將被設(shè)置為NULL. 這個(gè)結(jié)構(gòu)體必須在離開php_varstream_opener函數(shù)之前被顯式釋放, 否則它的內(nèi)存將會(huì)泄露:

typedef struct php_url {
    /* scheme://user:pass@host:port/path?query#fragment */
    char *scheme;
    char *user;
    char *pass;
    char *host;
    unsigned short port;
    char *path;
    char *query;
    char *fragment;
} php_url;

最后, varstream包裝器創(chuàng)建了一個(gè)數(shù)據(jù)結(jié)構(gòu), 保存了流指向的變量名, 讀取時(shí)的當(dāng)前位置. 這個(gè)結(jié)構(gòu)體將在流的讀取和寫入函數(shù)中用于獲取變量, 并且將在流結(jié)束使用時(shí)由php_varstream_close函數(shù)釋放.

opendir()

讀寫變量?jī)?nèi)容的實(shí)現(xiàn)可以再次進(jìn)行擴(kuò)展. 這里可以加入一個(gè)新的特性, 允許使用目錄函數(shù)讀取數(shù)組中的key. 在你的php_varstream_wrapper_ops結(jié)構(gòu)體之前增加下面的代碼:

static size_t php_varstream_readdir(php_stream *stream,
                char *buf, size_t count TSRMLS_DC)
{
    php_stream_dirent *ent = (php_stream_dirent*)buf;
    php_varstream_dirdata *data = stream->abstract;
    char *key;
    int type, key_len;
    long idx;

    /* 查找數(shù)組中的key */
    type = zend_hash_get_current_key_ex(Z_ARRVAL_P(data->arr),
                    &key, &key_len, &idx, 0, &(data->pos));

    /* 字符串key */
    if (type == HASH_KEY_IS_STRING) {
        if (key_len >= sizeof(ent->d_name)) {
            /* truncate long keys to maximum length */
            key_len = sizeof(ent->d_name) - 1;
        }
        /* 設(shè)置到目錄結(jié)構(gòu)上 */
        memcpy(ent->d_name, key, key_len);
        ent->d_name[key_len] = 0;
    /* 數(shù)值key */
    } else if (type == HASH_KEY_IS_LONG) {
        /* 設(shè)置到目錄結(jié)構(gòu)上 */
        snprintf(ent->d_name, sizeof(ent->d_name), "%ld",idx);
    } else {
        /* 迭代結(jié)束 */
        return 0;
    }
    /* 移動(dòng)數(shù)組指針(位置記錄到流的抽象結(jié)構(gòu)中) */
    zend_hash_move_forward_ex(Z_ARRVAL_P(data->arr),
                                        &data->pos);
    return sizeof(php_stream_dirent);
}

static int php_varstream_closedir(php_stream *stream,
                            int close_handle TSRMLS_DC)
{
    php_varstream_dirdata *data = stream->abstract;

    zval_ptr_dtor(&(data->arr));
    efree(data);
    return 0;
}

static int php_varstream_dirseek(php_stream *stream,
                    off_t offset, int whence,
                    off_t *newoffset TSRMLS_DC)
{
    php_varstream_dirdata *data = stream->abstract;

    if (whence == SEEK_SET && offset == 0) {
        /* 重置數(shù)組指針 */
        zend_hash_internal_pointer_reset_ex(
                    Z_ARRVAL_P(data->arr), &(data->pos));
        if (newoffset) {
            *newoffset = 0;
        }
        return 0;
    }
    /* 不支持其他類型的隨機(jī)訪問 */
    return -1;
}

static php_stream_ops php_varstream_dirops = {
    NULL, /* write */
    php_varstream_readdir,
    php_varstream_closedir,
    NULL, /* flush */
    PHP_VARSTREAM_DIRSTREAMTYPE,
    php_varstream_dirseek,
    NULL, /* cast */
    NULL, /* stat */
    NULL, /* set_option */
};

static php_stream *php_varstream_opendir(
            php_stream_wrapper *wrapper,
            char *filename, char *mode, int options,
            char **opened_path, php_stream_context *context
            STREAMS_DC TSRMLS_DC)
{
    php_varstream_dirdata *data;
    php_url *url;
    zval **var;

    /* 不支持持久化流 */
    if (options & STREAM_OPEN_PERSISTENT) {
        php_stream_wrapper_log_error(wrapper, options
                TSRMLS_CC, "Unable to open %s persistently",
                filename);
        return NULL;
    }

    /* 解析URL */
    url = php_url_parse(filename);
    if (!url) {
        php_stream_wrapper_log_error(wrapper, options
                TSRMLS_CC, "Unexpected error parsing URL");
        return NULL;
    }
    /* 檢查請(qǐng)求URL的正確性 */
    if (!url->host || (url->host[0] == 0) ||
            strcasecmp("var", url->scheme) != 0) {
        /* Bad URL or wrong wrapper */
        php_stream_wrapper_log_error(wrapper, options
                TSRMLS_CC, "Invalid URL, must be in the form: "
                "var://variablename");
        php_url_free(url);
        return NULL;
    }

    /* 查找變量 */
    if (zend_hash_find(&EG(symbol_table), url->host,
                strlen(url->host) + 1, (void**)&var) == FAILURE) {
        php_stream_wrapper_log_error(wrapper, options
                TSRMLS_CC, "Variable $%s not found", url->host);
        php_url_free(url);
        return NULL;
    }

    /* 檢查變量類型 */
    if (Z_TYPE_PP(var) != IS_ARRAY) {
        php_stream_wrapper_log_error(wrapper, options
                TSRMLS_CC, "$%s is not an array", url->host);
        php_url_free(url);
        return NULL;
    }
    /* 釋放前面分配的URL結(jié)構(gòu) */
    php_url_free(url);

    /* 分配抽象數(shù)據(jù)結(jié)構(gòu) */
    data = emalloc(sizeof(php_varstream_dirdata));
    if ( Z_ISREF_PP(var) && Z_REFCOUNT_PP(var) > 1) {
        /* 全拷貝 */
        MAKE_STD_ZVAL(data->arr);
        *(data->arr) = **var;
        zval_copy_ctor(data->arr);
        INIT_PZVAL(data->arr);
    } else {
        /* 寫時(shí)拷貝 */
        data->arr = *var;
        Z_SET_REFCOUNT_P(data->arr, Z_REFCOUNT_P(data->arr) + 1);
    }
    /* 重置數(shù)組指針 */
    zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(data->arr),
            &data->pos);
    return php_stream_alloc(&php_varstream_dirops,data,0,mode);
}

現(xiàn)在, 將你的php_varstream_wrapper_ops結(jié)構(gòu)體中的dir_opener的NULL替換成你的php_varstream_opendir函數(shù). 最后, 將下面新定義的類型放入到你的php_varstream.h文件的php_varstream_data定義下面:

#define PHP_VARSTREAM_DIRSTREAMTYPE    "varstream directory"
typedef struct _php_varstream_dirdata {
    zval *arr;
    HashPosition pos;
} php_varstream_dirdata;

在你基于fopen()實(shí)現(xiàn)的varstream包裝器中, 你直接使用持久變量名, 每次執(zhí)行讀寫操作時(shí)從符號(hào)表中獲取變量. 而這里, opendir()的實(shí)現(xiàn)中獲取變量時(shí)處理了變量不存在或者類型錯(cuò)誤的異常. 你還有一個(gè)數(shù)組變量的拷貝, 這就說明原數(shù)組的改變并不會(huì)影響后續(xù)的readdir()調(diào)用的結(jié)果. 原來存儲(chǔ)變量名的方式也可以正常工作, 這里只是給出另外一種選擇作為演示示例.

由于目錄訪問是基于成塊的目錄條目, 而不是字符, 因此這里需要一套獨(dú)立的流操作. 這個(gè)版本中, write沒有意義, 因此保持它為NULL. read的實(shí)現(xiàn)使用zend_hash_get_current_key_ex()函數(shù)將數(shù)組映射到目錄名. 而隨機(jī)訪問也只是對(duì)SEEK_SET有效, 用來響應(yīng)rewinddir()跳轉(zhuǎn)到數(shù)組開始位置.

實(shí)際上, 目錄流并沒有使用SEEK_CUR, SEEK_END, 或者除了0之外的偏移量. 在實(shí)現(xiàn)目錄流操作時(shí), 最好還是涉及你的函數(shù)能以某種方式處理這些情況, 以使得在流包裝層變化時(shí)能夠適應(yīng)其目錄隨機(jī)訪問.

操縱

5個(gè)靜態(tài)包裝器操作中的4個(gè)用來處理不是基于I/O的流資源操作. 你已經(jīng)看到過它們并了解它們的原型; 現(xiàn)在我們看看varstream包裝器框架中它們的實(shí)現(xiàn):

unlink

在你的wrapper_ops結(jié)構(gòu)體中增加下面的函數(shù), 它可以讓unlink()通過varstream包裝器, 擁有和unset()一樣的行為:

static int php_varstream_unlink(php_stream_wrapper *wrapper,
                        char *filename, int options,
                        php_stream_context *context
                        TSRMLS_DC)
{               
    php_url *url;   
                        
    url = php_url_parse(filename);
    if (!url) {         
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Unexpected error parsing URL");
        return -1;   
    }       
    if (!url->host || (url->host[0] == 0) ||
        strcasecmp("var", url->scheme) != 0) {
        /* URL不合法 */
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Invalid URL, must be in the form: "
                     "var://variablename");
        php_url_free(url);
        return -1;
    }
    
    /* 從符號(hào)表刪除變量 */
    //zend_hash_del(&EG(symbol_table), url->host, strlen(url->host) + 1);
    zend_delete_global_variable(url->host, strlen(url->host) + 1 TSRMLS_CC);
    
    php_url_free(url);                                      
    return 0;
}

這個(gè)函數(shù)的編碼量和php_varstream_opener差不多. 唯一的不同在于這里你需要傳遞變量名給zend_hash_del()去刪除變量.

譯注: 譯者的php-5.4.10環(huán)境中, 使用unlink()刪除變量后, 在用戶空間再次讀取該變量名的值會(huì)導(dǎo)致core dump. 因此上面代碼中譯者進(jìn)行了修正, 刪除變量時(shí)使用了zend_delete_global_variable(), 請(qǐng)讀者參考閱讀zend_delete_global_variable()函數(shù)源代碼, 考慮為什么直接用zend_hash_del()刪除, 會(huì)導(dǎo)致core dump. 下面是譯者測(cè)試用的用戶空間代碼:

<?php
$fp = fopen(&#39;var://hello&#39;, &#39;r&#39;);
fwrite($fp, &#39;world&#39;);
var_dump($hello);
unlink(&#39;var://hello&#39;);
$a  = $hello;

這個(gè)函數(shù)的代碼量應(yīng)該和php_varstream_opener差不多. 唯一的不同是這里是傳遞變量名給zend_hash_del()去刪除變量.

rename, mkdir, rmdir

為了一致性, 下面給出rename, mkdir, rmdir函數(shù)的實(shí)現(xiàn):

static int php_varstream_rename(php_stream_wrapper *wrapper,
        char *url_from, char *url_to, int options,
        php_stream_context *context TSRMLS_DC)
{
    php_url *from, *to;
    zval **var;

    /* 來源URL解析 */
    from = php_url_parse(url_from);
    if (!from) {
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Unexpected error parsing source");
        return -1;
    }
    /* 查找變量 */
    if (zend_hash_find(&EG(symbol_table), from->host,
                strlen(from->host) + 1,
                (void**)&var) == FAILURE) {
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "$%s does not exist", from->host);
        php_url_free(from);
        return -1;
    }
    /* 目標(biāo)URL解析 */
    to = php_url_parse(url_to);
    if (!to) {
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Unexpected error parsing dest");
        php_url_free(from);
        return -1;
    }
    /* 變量的改名 */
    Z_SET_REFCOUNT_PP(var, Z_REFCOUNT_PP(var) + 1);
    zend_hash_update(&EG(symbol_table), to->host,
                strlen(to->host) + 1, (void*)var,
                sizeof(zval*), NULL);
    zend_hash_del(&EG(symbol_table), from->host,
                strlen(from->host) + 1);
    php_url_free(from);
    php_url_free(to);
    return 0;
}

static int php_varstream_mkdir(php_stream_wrapper *wrapper,
                char *url_from, int mode, int options,
                php_stream_context *context TSRMLS_DC)
{
    php_url *url;

    /* URL解析 */
    url = php_url_parse(url_from);
    if (!url) {
       php_stream_wrapper_log_error(wrapper, options
           TSRMLS_CC, "Unexpected error parsing URL");
       return -1;
    }

    /* 檢查變量是否存在 */
    if (zend_hash_exists(&EG(symbol_table), url->host,
                    strlen(url->host) + 1)) {
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "$%s already exists", url->host);
        php_url_free(url);
        return -1;
    }
    /* EG(uninitialized_zval_ptr)通常是IS_NULL的zval *, 引用計(jì)數(shù)無限大 */
    zend_hash_add(&EG(symbol_table), url->host,
            strlen(url->host) + 1,
            (void*)&EG(uninitialized_zval_ptr),
            sizeof(zval*), NULL);
    php_url_free(url);
    return 0;
}

static int php_varstream_rmdir(php_stream_wrapper *wrapper,
                char *url, int options,
                php_stream_context *context TSRMLS_DC)
{
    /* 行為等價(jià)于unlink() */
    wrapper->wops->unlink(wrapper, url, options,
                                context TSRMLS_CC);
}

檢查

并不是所有的流操作都涉及到資源的操縱. 有時(shí)候也需要查看活動(dòng)的流在某個(gè)時(shí)刻的狀態(tài), 或檢查潛在可打開的資源的狀態(tài).

這一節(jié)流和包裝器的ops函數(shù)都是在相同的數(shù)據(jù)結(jié)構(gòu)php_stream_statbuf上工作的, 它只有一個(gè)元素: posix標(biāo)準(zhǔn)的struct statbuf. 當(dāng)本節(jié)的某個(gè)函數(shù)被調(diào)用時(shí), 將嘗試填充盡可能多的statbuf元素的成員.

stat

如果設(shè)置, 當(dāng)請(qǐng)求激活流實(shí)例的信息時(shí), 將會(huì)調(diào)用wrapper->ops->stream_stat(). 如果沒有設(shè)置, 則對(duì)應(yīng)的stream->ops->stat()將會(huì)被調(diào)用. 無論哪個(gè)函數(shù)被調(diào)用, 都應(yīng)該盡可能多的向返回的statbuf結(jié)構(gòu)體ssb->sb中填充盡可能多流實(shí)例的有用信息. 在普通文件I/O的用法中, 它對(duì)應(yīng)fstat()的標(biāo)準(zhǔn)I/O調(diào)用.

url_stat

在流實(shí)例外部調(diào)用wrapper->ops->url_stat()取到流資源的元數(shù)據(jù). 通常來說, 符號(hào)鏈接和重定向都應(yīng)該被解析, 直到找到一個(gè)真正的資源, 對(duì)其通過stat()系統(tǒng)調(diào)用這樣的機(jī)制讀取統(tǒng)計(jì)信息. url_stat的flags參數(shù)允許是下面PHP_STREAM_URL_STAT_*系列的常量值(省略PHP_STREAM_URL_STAT_前綴):

LINK不解析符號(hào)鏈接和重定向. 而是報(bào)告它碰到的第一個(gè)節(jié)點(diǎn)的信息, 無論是連

接還是真正的資源.

QUIETDoes not report errors. Note that this is the exact opposite of the REPORT_ERRORS logic found in many other stream functions.

Summary

Whether it is exposing remote network I/O or streaming resources of local data sources, it allows you to The extension hooks into the manipulation functions on Core Data, avoiding the need to reimplement the tedious work of descriptor management and I/O buffers. This makes it more useful and powerful in user space environments.

The next chapter will end the study of the flow packaging layer by learning about filters and contexts. Filters and contexts can be used to select default flow behaviors and even modify data in the process.

The above is the content of [Translation] [php extension development and embedded] Chapter 15 - Implementation of PHP mid-stream. For more related content, please pay attention to the PHP Chinese website (www. php.cn)!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undress AI Tool

Undress AI Tool

Undress images for free

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How do I stay up-to-date with the latest PHP developments and best practices? How do I stay up-to-date with the latest PHP developments and best practices? Jun 23, 2025 am 12:56 AM

TostaycurrentwithPHPdevelopmentsandbestpractices,followkeynewssourceslikePHP.netandPHPWeekly,engagewithcommunitiesonforumsandconferences,keeptoolingupdatedandgraduallyadoptnewfeatures,andreadorcontributetoopensourceprojects.First,followreliablesource

What is PHP, and why is it used for web development? What is PHP, and why is it used for web development? Jun 23, 2025 am 12:55 AM

PHPbecamepopularforwebdevelopmentduetoitseaseoflearning,seamlessintegrationwithHTML,widespreadhostingsupport,andalargeecosystemincludingframeworkslikeLaravelandCMSplatformslikeWordPress.Itexcelsinhandlingformsubmissions,managingusersessions,interacti

How to set PHP time zone? How to set PHP time zone? Jun 25, 2025 am 01:00 AM

TosettherighttimezoneinPHP,usedate_default_timezone_set()functionatthestartofyourscriptwithavalididentifiersuchas'America/New_York'.1.Usedate_default_timezone_set()beforeanydate/timefunctions.2.Alternatively,configurethephp.inifilebysettingdate.timez

How do I validate user input in PHP to ensure it meets certain criteria? How do I validate user input in PHP to ensure it meets certain criteria? Jun 22, 2025 am 01:00 AM

TovalidateuserinputinPHP,usebuilt-invalidationfunctionslikefilter_var()andfilter_input(),applyregularexpressionsforcustomformatssuchasusernamesorphonenumbers,checkdatatypesfornumericvalueslikeageorprice,setlengthlimitsandtrimwhitespacetopreventlayout

What is data serialization in PHP (serialize(), unserialize())? What is data serialization in PHP (serialize(), unserialize())? Jun 22, 2025 am 01:03 AM

ThePhpfunctionSerialize () andunserialize () AreusedtoconvertcomplexdaTastructdestoresintostoraSandaBackagain.1.Serialize () c OnvertsdatalikecarraysorobjectsraystringcontainingTypeandstructureinformation.2.unserialize () Reconstruct theoriginalatataprom

How do I embed PHP code in an HTML file? How do I embed PHP code in an HTML file? Jun 22, 2025 am 01:00 AM

You can embed PHP code into HTML files, but make sure that the file has an extension of .php so that the server can parse it correctly. Use standard tags to wrap PHP code, insert dynamic content anywhere in HTML. In addition, you can switch PHP and HTML multiple times in the same file to realize dynamic functions such as conditional rendering. Be sure to pay attention to the server configuration and syntax correctness to avoid problems caused by short labels, quotation mark errors or omitted end labels.

What are the best practices for writing clean and maintainable PHP code? What are the best practices for writing clean and maintainable PHP code? Jun 24, 2025 am 12:53 AM

The key to writing clean and easy-to-maintain PHP code lies in clear naming, following standards, reasonable structure, making good use of comments and testability. 1. Use clear variables, functions and class names, such as $userData and calculateTotalPrice(); 2. Follow the PSR-12 standard unified code style; 3. Split the code structure according to responsibilities, and organize it using MVC or Laravel-style catalogs; 4. Avoid noodles-style code and split the logic into small functions with a single responsibility; 5. Add comments at key points and write interface documents to clarify parameters, return values ??and exceptions; 6. Improve testability, adopt dependency injection, reduce global state and static methods. These practices improve code quality, collaboration efficiency and post-maintenance ease.

How do I execute SQL queries using PHP? How do I execute SQL queries using PHP? Jun 24, 2025 am 12:54 AM

Yes,youcanrunSQLqueriesusingPHP,andtheprocessinvolveschoosingadatabaseextension,connectingtothedatabase,executingqueriessafely,andclosingconnectionswhendone.Todothis,firstchoosebetweenMySQLiorPDO,withPDObeingmoreflexibleduetosupportingmultipledatabas

See all articles