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

首頁 後端開發(fā) php教程 [翻譯][php擴展開發(fā)與嵌入式]第14章-php中流的訪問

[翻譯][php擴展開發(fā)與嵌入式]第14章-php中流的訪問

Feb 10, 2017 am 10:18 AM
php


訪問流

PHP用戶空間中所有的檔案I/O處理都是透過php 4.3引入的php流包裝層處理的. 在內(nèi)部擴充程式碼可以選擇使用檔處理和本地文件系統(tǒng)或伯克利域套接字進行通信, 或者也可以調(diào)用和用戶空間流I/O相同的API.

流的概覽

, 直接的文件描述通常相描述比調(diào)用流包裝層消耗更少的CPU和內(nèi)存; 然而, 這樣會將實現(xiàn)某個特定協(xié)議的所有工作都堆積到作為擴展開發(fā)者的你身上. 通過掛鉤到流包裝層, 你的擴展代碼可以透明的使用各種內(nèi)建的流包裝, 比如HTTP, FTP, 以及它們對應的SSL版本, 另外還有g(shù)zip和bzip2壓縮包裝. 通過include特定的PEAR或PECL模組, 你的代碼還可以訪問其他協(xié)議, 比如SSH2, WebDav, 甚至是Gopher!

本章將介紹內(nèi)部基於流工作的基礎API. 後面到第16章"有趣的流"中, 我們將看到諸如應用過濾器, 使用上下文選項和參數(shù)等高級概念.

打開流

儘管是一個統(tǒng)一的API, 但實際上依賴於所需的流的類型, 有四種不同的路徑去打開一個流. 從用戶空間角度來看, 這四種不同的類別如下(函數(shù)列表只代表示例, 不是完整列表):

<?php
    /* fopen包裝
     * 操作文件/URI方式指定遠程文件類資源 */
    $fp = fopen($url, $mode);
    $data = file_get_contents($url);
    file_put_contents($url, $data);
    $lines = file($url);

    /* 傳輸
     * 基于套接字的順序I/O */
    $fp = fsockopen($host, $port);
    $fp = stream_socket_client($uri);
    $fp = stream_socket_server($uri, $options);

    /* 目錄流 */
    $dir = opendir($url);
    $files = scandir($url);
    $obj = dir($url);

    /* "特殊"的流 */
    $fp = tmpfile();
    $fp = popen($cmd);
    proc_open($cmd, $pipes);
?>

無論你打開的是什麼類型的流, 它們都存儲在一個公共的結(jié)構(gòu)體php_stream中.

fopen包裝

我們首先從實現(xiàn)fopen()函數(shù)開始. 現(xiàn)在你應該已經(jīng)對創(chuàng)建擴展骨架很熟悉了, 如果還不熟悉, 請回到第5章"你的第一個擴展"複習一下, 以下是我們實作的fopen()函數(shù):

PHP_FUNCTION(sample5_fopen)
{
    php_stream *stream;
    char *path, *mode;
    int path_len, mode_len;
    int options = ENFORCE_SAFE_MODE | REPORT_ERRORS;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
            &path, &path_len, &mode, &mode_len) == FAILURE) {
        return;
    }
    stream = php_stream_open_wrapper(path, mode, options, NULL);
    if (!stream) {
        RETURN_FALSE;
    }
    php_stream_to_zval(stream, return_value);
}

php_stream_open_wrapper()的目的應該是完全繞過底層. path指定要讀寫檔名或URL, 讀寫行為依賴於mode的值.

options是位域的標記值集合, 這裡是設定為下面介紹的一組固定值:

USE_PATH將php.ini檔案中的include_path上. 內(nèi)建函數(shù)fopen()在指定第三個參數(shù)為TRUE

時將會設定這個選項.

STREAM_USE_URL

STREAM_USE_URL

STREAM_USE_URL

php://, file://, zlib://, bzip2://這些URL包裝器並不會

認為它們是遠端URL.P.CE

儘管這個常數(shù)這樣命名, 但實際上設定這個選項

後僅僅是啟用了安全模式(php.ini檔案中的

safe_mode指令)的強制檢查. 如果沒有設定這個

不論INI設定

中safe_mode如何設定)

REPORT_ERRORS

在指定的資源開啟過程中碰到錯誤時將設定設定為這個選項

.

STREAM_MUST_SEEK對於某些流, 例如套接字, 是不可以seek的(隨機

); seek. 如果調(diào)用作用域指定這個選項, 並且包裝

器檢測到它不能保證可以seek, 將會拒絕打開這

_Wk呼叫作用域要求流可以被轉(zhuǎn)換到stdio或

posix檔案描述子, 則應該給open_wrapper函數(shù)

傳遞這個選項, 以保證在失敗/O完成操作之前就保證在失敗/O

STREAM_ONLY_GET_HEADERS

標識只需要從流中請求元資料. 實際上這是用於

http包裝器, 獲取http_response_http包裝器, 獲取http_response_

http包裝器, 獲取http_response_量的全域變體內(nèi)容.

STREAM_DISABLE_OPEN_BASEDIR類似safe_mode檢查, 不設定這個選項會檢查

的檢查

STREAM_OPEN_PERSISTENT

告知流包裝層, 所有內(nèi)部分配的空間都採用持久

化分配, 並將關聯(lián)的資源註冊到持久化列表中。則搜尋預設的包含路徑.多數(shù)URL

包裝器都忽略這個選項.

IGNORE_URL提供這個選項時, 流包裝層只打開本地文件. 所

有的is_url包裝器都將被忽略.

最后的NULL參數(shù)是char **類型, 它最初是用來設置匹配路徑, 如果path指向普通文件URL, 則去掉file://部分, 保留直接的文件路徑用于傳統(tǒng)的文件名操作. 這個參數(shù)僅僅是以前引擎內(nèi)部處理使用的.

此外, 還有php_stream_open_wrapper()的一個擴展版本:

php_stream *php_stream_open_wrapper_ex(char *path,
            char *mode, int options, char **opened_path,
            php_stream_context *context);

最后一個參數(shù)context允許附加的控制, 并可以得到包裝器內(nèi)的通知. 你將在第16章看到這個參數(shù)的細節(jié).

傳輸層包裝

盡管傳輸流和fopen包裝流是相同的組件組成的, 但它的注冊策略和其他的流不同. 從某種程度上來說, 這是因為用戶空間對它們的訪問方式的不同造成的, 它們需要實現(xiàn)基于套接字的其他因子.

從擴展開發(fā)者角度來看, 打開傳輸流的過程是相同的. 下面是對fsockopen()的實現(xiàn):

PHP_FUNCTION(sample5_fsockopen)
{
    php_stream *stream;
    char *host, *transport, *errstr = NULL;
    int host_len, transport_len, implicit_tcp = 1, errcode = 0;
    long port = 0;
    int options = ENFORCE_SAFE_MODE;
int flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l",
                        &host, &host_len, &port) == FAILURE) {
        return;
    }
    if (port) {
        int implicit_tcp = 1;
        if (strstr(host, "://")) {
                /* A protocol was specified,
                 * no need to fall back on tcp:// */
            implicit_tcp = 0;
        }
        transport_len = spprintf(&transport, 0, "%s%s:%d",
                implicit_tcp ? "tcp://" : "", host, port);
    } else {
        /* When port isn&#39;t specified
         * we can safely assume that a protocol was
         * (e.g. unix:// or udg://) */
        transport = host;
        transport_len = host_len;
    }
    stream = php_stream_xport_create(transport, transport_len,
                              options, flags,
                              NULL, NULL, NULL, &errstr, &errcode);
    if (transport != host) {
        efree(transport);
    }
    if (errstr) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %s",
                                errcode, errstr);
        efree(errstr);
    }
    if (!stream) {
        RETURN_FALSE;
    }
    php_stream_to_zval(stream, return_value);
}

這個函數(shù)的基礎構(gòu)造和前面的fopen示例是一樣的. 不同在于host和端口號使用不同的參數(shù)指定, 接著為了給出一個傳輸流URL就必須將它們合并到一起. 在產(chǎn)生了一個有意義的"路徑:后, 將它傳遞給php_stream_xport_create()函數(shù), 方式和fopen()使用的php_stream_open_wrapper()API一樣. php_stream_xport_create()的原型如下:

php_stream *php_stream_xport_create(char *xport, int xport_len,
                    int options, int flags,
                    const char *persistent_id,
                                     struct timeval *timeout,
                                     php_stream_context *context,
                                     char **errstr, int *errcode);

每個參數(shù)的含義如下:

xport基于URI的傳輸描述符. 對于基于inet的套接字流, 它可以是

tcp://127.0.0.1:80, udp://10.0.0.1:53, ssl://169.254.13.24:445等. 此

外, UNIX域傳輸協(xié)議unix:///path/to/socket,

udg:///path/to/dgramsocket等都是合法的. xport_len指定了xport的長

度, 因此xport是二進制安全的.

options這個值是由前面php_stream_open_wrapper()中介紹的選項通過按位

或組成的值.

flags由STREAM_XPORT_CLIENT或STREAM_XPORT_SERVER之一

與下面另外一張表中將列出的STREAM_XPORT_*常量通過按位或

組合得到的值.

persistent_id如果請求的傳輸流需要在請求間持久化, 呼叫作用域可以提供一個

key名字描述連接. 指定這個值為NULL創(chuàng)建非持久化連接;key名字描述連接.指定這個值為NULL創(chuàng)建非持久化連接;

一的字串值將嘗試先從持久化池中尋找已有的傳輸流, 或沒有

找到時就創(chuàng)建一個新的持久化流

找到時就創(chuàng)建一個新的持久化流.在逾時回傳失敗之前連線的嘗試時間. 如果這個值傳遞為NULL則使

用php.ini中指定的預設逾時值. 這個參數(shù)對服務端傳輸流沒有意義.errstr

如果在選定的套接字上建立, 連接, 綁定或監(jiān)聽時發(fā)生錯誤, 這裡傳遞

的char *引用值將被設定為一個描述發(fā)生錯誤原因的字串. errstr初

始應該指向的是NULL; 如果在返回時它被設定了值, 則調(diào)用作用域有

透過errstr傳回的錯誤訊息對應的數(shù)值錯誤碼.

php_stream_xport_create()的flags參數(shù)中使用了STREAM_XPORT_*一族常數(shù)定義如下:建立連接. 這個

標記通常和STREAM_XPORT_CONNECT或

STREAM_XPORT_CON STREAM_XPORT_SERVER

本地端將透過傳輸層accept連接. 這個標記通常

和STREAM_XPORT_BIND以及

STREAM_XPORT_LISTEN一起使用.

分. 在建立客戶端傳輸流時省略這個標記是合法

的, 但是這樣做就要求手動的調(diào)用

php_stream_xport_connect().

STREAM_XPORT_CONNECT_ASYNC嘗試連接到遠程資源, 但不阻塞.

STREAM_XPORT_BIND將傳輸流綁定到本地資源. 用在服務端傳輸流時,

這將使得accept連接的傳輸流準備端口, 路徑或

特定的端點標識符等信息.

STREAM_XPORT_LISTEN在已綁定的傳輸流端點上監(jiān)聽到來的連接. 這通

常用于基于流的傳輸協(xié)議, 比如: tcp://, ssl://,

unix://.

目錄訪問

fopen包裝器支持目錄訪問, 比如file://和ftp://, 還有第三種流打開函數(shù)也可以用于目錄訪問, 下面是對opendir()的實現(xiàn):

PHP_FUNCTION(sample5_opendir)
{
    php_stream *stream;
    char *path;
    int path_len, options = ENFORCE_SAFE_MODE | REPORT_ERRORS;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
                                   &path, &path_len) == FAILURE) {
        return;
    }
    stream = php_stream_opendir(path, options, NULL);
    if (!stream) {
        RETURN_FALSE;
    }
    php_stream_to_zval(stream, return_value);
}

同樣的, 也可以為某個特定目錄打開一個流, 比如本地文件系統(tǒng)的目錄名或支持目錄訪問的URL格式資源. 這里我們又看到了options參數(shù), 它和原來的含義一樣, 第三個參數(shù)NULL原型是php_stream_context類型.

在目錄流打開后, 和文件以及傳輸流一樣, 返回給用戶空間.

特殊流

還有一些特殊類型的流不能歸類到fopen/transport/directory中. 它們中每一個都有自己獨有的API:

php_stream *php_stream_fopen_tmpfile(void);
php_stream *php_stream_fopen_temporary_file(const char *dir,
                            const char *pfx, char **opened_path);

創(chuàng)建一個可seek的緩沖區(qū)流用于讀寫. 在關閉時, 這個流使用的所有臨時資源, 包括所有的緩沖區(qū)(無論是在內(nèi)存還是磁盤), 都將被釋放. 使用這一組API中的后一個函數(shù), 允許臨時文件被以特定的格式命名放到指定路徑. 這些內(nèi)部API調(diào)用被用戶空間的tmpfile()函數(shù)隱藏.

php_stream *php_stream_fopen_from_fd(int fd,
                const char *mode, const char *persistent_id);
php_stream *php_stream_fopen_from_file(FILE *file,
                const char *mode);
php_stream *php_stream_fopen_from_pipe(FILE *file,
            const char *mode);

這3個API方法接受已經(jīng)打開的FILE *資源或文件描述符ID, 使用流API的某種操作包裝. fd格式的接口不會搜索匹配你前面看到過的fopen函數(shù)打開的資源, 但是它會注冊持久化的資源, 后續(xù)的fopen可以使用到這個持久化資源.

訪問流

在你打開一個流之后, 就可以在它上面執(zhí)行I/O操作了. 使用那種協(xié)議包裝API創(chuàng)建了流并不重要, 它們都使用相同的訪問API.

流的讀寫可以使用下面的API函數(shù)組合完成, 它們多數(shù)都是遵循POSIX I/O中對應的API規(guī)范的:

int php_stream_getc(php_stream *stream);

從數(shù)據(jù)流中接收一個字符. 如果流上再沒有數(shù)據(jù), 則返回EOF.

size_t php_stream_read(php_stream *stream, char *buf, size_t count);

從指定流中讀取指定字節(jié)的數(shù)據(jù). buf必須預分配至少count字節(jié)的內(nèi)存空間. 這個函數(shù)將返回從數(shù)據(jù)流實際讀到緩沖區(qū)中的數(shù)據(jù)字節(jié)數(shù).

php_stream_read()不同于其他的流讀取函數(shù). 如果使用的流不是普通文件流, 哪怕數(shù)據(jù)流中有超過請求字節(jié)數(shù)的數(shù)據(jù), 并且當前也可以返回, 它也只會調(diào)用過一次底層流實現(xiàn)的read函數(shù). 這是為了兼容基于包(比如UDP)的協(xié)議的這種做法.

char *php_stream_get_line(php_stream *stream, char *buf,
                             size_t maxlen, size_t *returned_len);
char *php_stream_gets(php_stream *stream, char *buf,
                             size_t maxlen);

這兩個函數(shù)從stream中讀取最多maxlen個字符, 直到碰到換行符或流結(jié)束. buf可以是一個指向預分配的至少maxlen字節(jié)的內(nèi)存空間的指針, 也可以是NULL, 當它是NULL時, 則會自動的創(chuàng)建一個動態(tài)大小的緩沖區(qū), 用從流中實際讀出的數(shù)據(jù)填充, 成功后函數(shù)返回指向緩沖區(qū)的指針, 失敗則返回NULL. 如果returned_len傳遞了非NULL值, 則在返回時它將被設置為實際從流中讀取的字節(jié)數(shù).

char *php_stream_get_record(php_stream *stream,
        size_t maxlen, size_t *returned_len,
        char *delim, size_t delim_len
        TSRMLS_DC);

和php_stream_get_line()類似, 這個函數(shù)將讀取最多maxlen, 或到達EOF/行結(jié)束第一次出現(xiàn)的位置. 但是它也有和php_stream_get_line()的不同指出, 這個函數(shù)允許指定任意的停止讀取標記.

讀取目錄項

從php流中讀取目錄項和上面從普通文件中讀取普通數(shù)據(jù)相同. 這些數(shù)據(jù)放到了固定大小的dirents塊中. 內(nèi)部的php_stream_dirent結(jié)構(gòu)體如下, 它與POSIX定義的dirent結(jié)構(gòu)體一致:

typedef struct _php_stream_dirent {
    char d_name[MAXPATHLEN];
} php_stream_dirent;

實際上你可以直接使用php_stream_read()函數(shù)讀取數(shù)據(jù)到這個結(jié)構(gòu)體中:

{
   struct dirent entry;
   if (php_stream_read(stream, (char*)&entry, sizeof(entry))
                               == sizeof(entry)) {
       /* 成功從目錄流中讀取到一項 */
       php_printf("File: %s\n", entry.d_name);
   }
}

由于從目錄流中讀取是很常見的操作, php流包裝層暴露了一個API, 它將記錄大小的檢查和類型轉(zhuǎn)換處理封裝到了一次調(diào)用中:

php_stream_dirent *php_stream_readdir(php_stream *dirstream,
                            php_stream_dirent *entry);

如果成功讀取到目錄項, 則傳入的entry指針將被返回, 否則返回NULL標識錯誤. 使用這個為目錄流特殊構(gòu)建的函數(shù)而不是直接從目錄流讀取非常重要, 這樣做未來流API改變時就不至于和你的代碼沖突.

和讀類似, 向流中寫數(shù)據(jù)只需要傳遞一個緩沖區(qū)和緩沖區(qū)長度給流.

size_t php_stream_write(php_stream *stream, char *buf,
                                                         size_t count);
size_t php_stream_write_string(php_stream *stream, char *stf);

write_string的版本實際上是一個提供便利的宏, 它允許寫一個NULL終止的字符串,而不用顯式的提供長度. 返回的是實際寫到流中的字節(jié)數(shù). 要特別小心的是嘗試寫大數(shù)據(jù)的時候可能導致流阻塞, 比如套接字流, 而如果流被標記為非阻塞, 則實際寫入的數(shù)據(jù)量可能會小于傳遞給函數(shù)的期望大小.

int php_stream_putc(php_stream *stream, int c);
int php_stream_puts(php_string *stream, char *buf);

還有一種選擇是, 使用php_stream_putc()和php_stream_puts()寫入一個字符或一個字符串到流中. 要注意, php_stream_puts()不同于php_stream_write_string(), 雖然它們的原型看起來是一樣的, 但是php_stream_puts()會在寫出buf中的數(shù)據(jù)后自動的追加一個換行符.

size_t php_stream_printf(php_stream *stream TSRMLS_DC,
                                        const char *format, ...);

功能和格式上都類似于fprintf(), 這個API調(diào)用允許在寫的同時構(gòu)造字符串而不用去創(chuàng)建臨時緩沖區(qū)構(gòu)造數(shù)據(jù). 這里我們能夠看到的一個明顯的不同是它需要TSRMLS_CC宏來保證線程安全.

隨機訪問, 查看文件偏移量以及緩存的flush

基于文件的流, 以及另外幾種流是可以隨機訪問的. 也就是說, 在流的一個位置讀取了一些數(shù)據(jù)之后, 文件指針可以向前或向后移動, 以非線性順序讀取其他部分.

如果你的流應用代碼預測到底層的流支持隨機訪問, 在打開的時候就應該傳遞STREAM_MUST_SEEK選項. 對于那些原本就可隨機訪問的流來說, 這通常不會有什么影響, 因為流本身就是可隨機訪問的. 而對于那些原本不可隨機訪問的流, 比如網(wǎng)絡I/O或線性訪問文件比如FIFO管道, 這個暗示可以讓調(diào)用程序有機會在流的數(shù)據(jù)被消耗掉之前, 優(yōu)雅的失敗.

在可隨機訪問的流資源上工作時, 下面的函數(shù)可用來將文件指針移動到任意位置:

int php_stream_seek(php_stream *stream, off_t offset, int whence);
int php_stream_rewind(php_stream *stream);

offset是相對于whence表示的流位置的偏移字節(jié)數(shù), whence的可選值及含義如下:

SEEK_SEToffset相對于文件開始位置. php_stream_rewind()API調(diào)用實際上是一個宏,

展開后是php_stream_seek(stream, 0, SEEK_SET), 表示移動到文件開始

位置偏移0字節(jié)處. 當使用SEEK_SET時, 如果offset傳遞負值被認為是錯誤

的, 將會導致未定義行為. 指定的位置超過流的末尾也是未定義的, 不過結(jié)果

通常是一個錯誤或文件被擴大以滿足指定的偏移量.

SEEK_CURoffset相對于文件的當前偏移量. 調(diào)用php_stream_seek(steram, offset,

SEEK_CUR)一般來說等價于php_stream_seek(stream, php_stream_tell()

+ offset, SEEK_SET);

SEEK_ENDoffset是相對于當前的EOF位置的. 負值的offset表示在EOF之前的位置, 正

值和SEEK_SET中描述的是相同的語義, 可能在某些流實現(xiàn)上可以工作.

int php_stream_rewinddir(php_stream *dirstream);

在目錄流上隨機訪問時, 只有php_stream_rewinddir()函數(shù)可用. 使用php_stream_seek()函數(shù)將導致未定義行為. 所有的隨機訪問一族函數(shù)返回0標識成功或者-1標識失敗.

off_t php_stream_tell(php_stream *stream);

如你之前所見, php_stream_tell()將返回當前的文件偏移量.

int php_stream_flush(php_stream *stream);

調(diào)用flush()函數(shù)將強制將流過濾器此類內(nèi)部緩沖區(qū)中的數(shù)據(jù)輸出到最終的資源中. 在流被關閉時, flush()函數(shù)將自動調(diào)用, 并且大多數(shù)無過濾流資源雖然不進行任何內(nèi)部緩沖, 但也需要flush. 顯式的調(diào)用這個函數(shù)很少見, 并且通常也是不需要的.

int php_stream_stat(php_stream *stream, php_stream_statbuf *ssb);

調(diào)用php_stream_stat()可以獲取到流實例的其他信息, 它的行為類似于fstat()函數(shù). 實際上, php_stream_statbuf結(jié)構(gòu)體現(xiàn)在僅包含一個元素: struct statbuf sb; 因此, php_stream_stat()調(diào)用可以如下面例子一樣, 直接用傳統(tǒng)的fstat()操作替代, 它只是將posix的stat操作翻譯成流兼容的:

int php_sample4_fd_is_fifo(int fd)
{
    struct statbuf sb;
    fstat(fd, &sb);
    return S_ISFIFO(sb.st_mode);
}
int php_sample4_stream_is_fifo(php_stream *stream)
{
    php_stream_statbuf ssb;
    php_stream_stat(stream, &ssb);
    return S_ISFIFO(ssb.sb.st_mode);
}

關閉

所有流的關閉都是通過php_stream_free()函數(shù)處理的, 它的原型如下:

int php_stream_free(php_stream *stream, int options);

這個函數(shù)中的options參數(shù)允許的值是PHP_STREAM_FREE_xxx一族常量的按位或的結(jié)果, 這一族常量定義如下(下面省略PHP_STREAM_FREE_前綴):

CALL_DTOR流實現(xiàn)的析構(gòu)器應該被調(diào)用. 這里提供了一個時機對特定的流

進行顯式釋放.

RELEASE_STREAM釋放為php_stream結(jié)構(gòu)體分配的內(nèi)存

PRESERVE_HANDLE指示流的析構(gòu)器不要關閉它的底層描述符句柄

RSRC_DTOR流包裝層內(nèi)部管理資源列表的垃圾回收

PERSISTENT作用在持久化流上時, 它的行為將是永久的而不局限于當前請

求.

CLOSECALL_DTOR和RELEASE_STREAM的聯(lián)合. 這是關閉

非持久化流的一般選項.

CLOSE_CASTEDCLOSE和PRESERVE_HANDLE的聯(lián)合.

CLOSE_PERSISTENTCLOSE和PERSISTENT的聯(lián)合. 這是永久關閉持久化流的一

般選項.

實際上, 你并不需要直接調(diào)用php_stream_free()函數(shù). 而是在關閉流時使用下面兩個宏的某個替代:

#define php_stream_close(stream) \
    php_stream_free((stream), PHP_STREAM_FREE_CLOSE)
#define php_stream_pclose(stream) \
    php_stream_free((stream), PHP_STREAM_FREE_CLOSE_PERSISTENT)

通過zval交換流

因為流通常映射到zval上, 反之亦然, 因此提供了一組宏用來簡化操作, 并統(tǒng)一編碼(格式):

#define php_stream_to_zval(stream, pzval) \
    ZVAL_RESOURCE((pzval), (stream)->rsrc_id);

要注意, 這里并沒有調(diào)用ZEND_REGISTER_RESOURCE(). 這是因為當流打開的時候, 已經(jīng)自動的注冊為資源了, 這樣就可以利用到引擎內(nèi)建的垃圾回收和shutdown系統(tǒng)的優(yōu)點. 使用這個宏而不是嘗試手動的將流注冊為新的資源ID是非常重要的; 這樣做的最終結(jié)果是導致流被關閉兩次以及引擎崩潰.

#define php_stream_from_zval(stream, ppzval) \
    ZEND_FETCH_RESOURCE2((stream), php_stream*, (ppzval), \
    -1, "stream", php_file_le_stream(), php_file_le_pstream())
#define php_stream_from_zval_no_verify(stream, ppzval) \
    (stream) = (php_stream*)zend_fetch_resource((ppzval) \
    TSRMLS_CC, -1, "stream", NULL, 2, \
    php_file_le_stream(), php_file_le_pstream())

從傳入的zval *中取回php_stream *有一個類似的宏. 可以看出, 這個宏只是對資源獲取函數(shù)(第9章"資源數(shù)據(jù)類型")的一個簡單封裝. 請回顧ZEND_FETCH_RESOURCE2()宏, 第一個宏php_stream_from_zval()就是對它的包裝, 如果資源類型不匹配, 它將拋出一個警告并嘗試從函數(shù)實現(xiàn)中返回. 如果你只是想從傳入的zval *中獲取一個php_stream *, 而不希望有自動的錯誤處理, 就需要使用php_stream_from_zval_no_verify()并且需要手動的檢查結(jié)果值.

靜態(tài)資源操作

一個基于流的原子操作并不需要實際的實例. 下面這些API僅僅使用URL執(zhí)行這樣的操作:

int php_stream_stat_path(char *path, php_stream_statbuf *ssb);

和前面的php_stream_stat()類似, 這個函數(shù)提供了一個對POSIX的stat()函數(shù)協(xié)議依賴的包裝. 要注意, 并不是所有的協(xié)議都支持URL記法, 并且即便支持也可能不能報告出statbuf結(jié)構(gòu)體中的所有成員值. 一定要檢查php_stream_stat_path()失敗時的返回值, 0標識成功, 要知道, 不支持的元素返回時其值將是默認的0.

int php_stream_stat_path_ex(char *path, int flags,
        php_stream_statbuf *ssb, php_stream_context *context);

這個php_stream_url_stat()的擴展版本允許傳遞另外兩個參數(shù). 第一個是flags, 它的值可以是下面的PHP_STERAM_URL_STAT_*(下面省略PHP_STREAM_URL_STAT_前綴)一族常量的按位或的結(jié)果. 還有一個是context參數(shù), 它在其他的一些流函數(shù)中也有出現(xiàn), 我們將在第16章去詳細學習.

LINK原始的php_stream_stat_path()對于符號鏈接或目錄將會進行解析直到碰到

協(xié)議定義的結(jié)束資源. 傳遞PHP_STREAM_URL_STAT_LINK標記將導致

php_stream_stat_path()返回請求資源的信息而不會進行符號鏈接的解析.

(譯注: 我們可以這樣理解, 沒有這個標記, 底層使用stat(), 如果有這個標記,

底層使用lstat(), 關于stat()和lstat()的區(qū)別, 請查看*nix手冊)

QUIET默認情況下, 如果在執(zhí)行URL的stat操作過程中碰到錯誤, 包括文件未找到錯

誤, 都將通過php的錯誤處理機制觸發(fā). 傳遞QUIET標記可以使得

php_stream_stat_path()返回而不報告錯誤.

int php_stream_mkdir(char *path, int mode, int options,
                            php_stream_context *context);
int php_stream_rmdir(char *path, int options,
                            php_stream_context *context);

創(chuàng)建和刪除目錄也會如你期望的工作. 這里的options參數(shù)和前面的php_stream_open_wrapper()函數(shù)的同名參數(shù)含義一致. 對于php_stream_mkdir(), 還有一個參數(shù)mode用于指定一個八進制的值表明讀寫執(zhí)行權(quán)限.

小結(jié)

本章中你接觸了一些基于流的I/O的內(nèi)部表象. 下一章將演示做呢樣實現(xiàn)自己的協(xié)議包裝, 甚至是定義自己的流類型.

以上就是[翻譯][php擴展開發(fā)和嵌入式]第14章-php中流的訪問的內(nèi)容,更多相關內(nèi)容請關注PHP中文網(wǎng)(miracleart.cn)!


本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願投稿,版權(quán)歸原作者所有。本站不承擔相應的法律責任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請聯(lián)絡admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動的應用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

我如何了解最新的PHP開發(fā)和最佳實踐? 我如何了解最新的PHP開發(fā)和最佳實踐? Jun 23, 2025 am 12:56 AM

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

什麼是PHP,為什麼它用於Web開發(fā)? 什麼是PHP,為什麼它用於Web開發(fā)? Jun 23, 2025 am 12:55 AM

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

如何設置PHP時區(qū)? 如何設置PHP時區(qū)? Jun 25, 2025 am 01:00 AM

tosetTherightTimeZoneInphp,restate_default_timezone_set()functionAtthestArtofyourscriptWithavalIdidentIdentifiersuchas'america/new_york'.1.usedate_default_default_timezone_set_set()

我如何驗證PHP中的用戶輸入以確保其符合某些標準? 我如何驗證PHP中的用戶輸入以確保其符合某些標準? Jun 22, 2025 am 01:00 AM

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

什麼是php(serialize(),Unserialize())中的數(shù)據(jù)序列化? 什麼是php(serialize(),Unserialize())中的數(shù)據(jù)序列化? Jun 22, 2025 am 01:03 AM

thephpfunctionserize()andunSerialize()redustoconvertComplexdatStructDestoresToroStoroStoroSandaBackagagain.1.Serialize()

如何將PHP代碼嵌入HTML文件中? 如何將PHP代碼嵌入HTML文件中? Jun 22, 2025 am 01:00 AM

可以將PHP代碼嵌入HTML文件中,但需確保文件以.php為擴展名,以便服務器能正確解析。使用標準的標籤包裹PHP代碼,可在HTML中任意位置插入動態(tài)內(nèi)容。此外,可在同一文件中多次切換PHP與HTML,實現(xiàn)條件渲染等動態(tài)功能。務必注意服務器配置及語法正確性,避免因短標籤、引號錯誤或遺漏結(jié)束標籤導致問題。

編寫清潔和可維護的PHP代碼的最佳實踐是什麼? 編寫清潔和可維護的PHP代碼的最佳實踐是什麼? Jun 24, 2025 am 12:53 AM

寫乾淨、易維護的PHP代碼關鍵在於清晰命名、遵循標準、合理結(jié)構(gòu)、善用註釋和可測試性。 1.使用明確的變量、函數(shù)和類名,如$userData和calculateTotalPrice();2.遵循PSR-12標準統(tǒng)一代碼風格;3.按職責拆分代碼結(jié)構(gòu),使用MVC或Laravel式目錄組織;4.避免麵條式代碼,將邏輯拆分為單一職責的小函數(shù);5.在關鍵處添加註釋並撰寫接口文檔,明確參數(shù)、返回值和異常;6.提高可測試性,採用依賴注入、減少全局狀態(tài)和靜態(tài)方法。這些做法提升代碼質(zhì)量、協(xié)作效率和後期維護便利性。

如何使用PHP執(zhí)行SQL查詢? 如何使用PHP執(zhí)行SQL查詢? Jun 24, 2025 am 12:54 AM

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

See all articles