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

Rumah pembangunan bahagian belakang tutorial php [翻譯][php擴(kuò)展開發(fā)和嵌入式]第14章-php中流的訪問

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

Feb 10, 2017 am 10:18 AM
php


訪問流

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

流的概覽

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

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

打開流

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

<?php
    /* fopen包裝
     * 操作文件/URI方式指定遠(yuǎn)程文件類資源 */
    $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);
?>

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

fopen包裝

我們首先從實(shí)現(xiàn)fopen()函數(shù)開始. 現(xiàn)在你應(yīng)該已經(jīng)對(duì)創(chuàng)建擴(kuò)展骨架很熟悉了, 如果還不熟悉, 請(qǐng)回到第5章"你的第一個(gè)擴(kuò)展"復(fù)習(xí)一下, 下面是我們實(shí)現(xiàn)的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()的目的應(yīng)該是完全繞過底層. path指定要讀寫文件名或URL, 讀寫行為依賴于mode的值.

options是位域的標(biāo)記值集合, 這里是設(shè)置為下面介紹的一組固定值:

USE_PATH將php.ini文件中的include_path應(yīng)用到相對(duì)路徑

上. 內(nèi)建函數(shù)fopen()在指定第三個(gè)參數(shù)為TRUE

時(shí)將會(huì)設(shè)置這個(gè)選項(xiàng).

STREAM_USE_URL設(shè)置這個(gè)選項(xiàng)后, 將只能打開遠(yuǎn)端URL. 對(duì)于

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

認(rèn)為它們是遠(yuǎn)端URL.

ENFORCE_SAFE_MODE盡管這個(gè)常量這樣命名, 但實(shí)際上設(shè)置這個(gè)選項(xiàng)

后僅僅是啟用了安全模式(php.ini文件中的

safe_mode指令)的強(qiáng)制檢查. 如果沒有設(shè)置這個(gè)

選項(xiàng)將導(dǎo)致跳過safe_mode的檢查(不論INI設(shè)置

中safe_mode如何設(shè)置)

REPORT_ERRORS在指定的資源打開過程中碰到錯(cuò)誤時(shí), 如果設(shè)置

了這個(gè)選項(xiàng)則將產(chǎn)生錯(cuò)誤報(bào)告.

STREAM_MUST_SEEK對(duì)于某些流, 比如套接字, 是不可以seek的(隨機(jī)

訪問); 這類文件句柄, 只有在特定情況下才可以

seek. 如果調(diào)用作用域指定這個(gè)選項(xiàng), 并且包裝

器檢測(cè)到它不能保證可以seek, 將會(huì)拒絕打開這

個(gè)流.

STREAM_WILL_CAST如果調(diào)用作用域要求流可以被轉(zhuǎn)換到stdio或

posix文件描述符, 則應(yīng)該給open_wrapper函數(shù)

傳遞這個(gè)選項(xiàng), 以保證在I/O操作發(fā)生之前就失敗

STREAM_ONLY_GET_HEADERS標(biāo)識(shí)只需要從流中請(qǐng)求元數(shù)據(jù). 實(shí)際上這是用于

http包裝器, 獲取http_response_headers全局變

量而不真正的抓取遠(yuǎn)程文件內(nèi)容.

STREAM_DISABLE_OPEN_BASEDIR類似safe_mode檢查, 不設(shè)置這個(gè)選項(xiàng)則會(huì)檢查

INI設(shè)置open_basedir, 如果指定這個(gè)選項(xiàng)則可以

繞過這個(gè)默認(rèn)的檢查

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

化分配, 并將關(guān)聯(lián)的資源注冊(cè)到持久化列表中.

IGNORE_PATH如果不指定, 則搜索默認(rèn)的包含路徑. 多數(shù)URL

包裝器都忽略這個(gè)選項(xiàng).

IGNORE_URL提供這個(gè)選項(xiàng)時(shí), 流包裝層只打開本地文件. 所

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

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

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

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

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

傳輸層包裝

盡管傳輸流和fopen包裝流是相同的組件組成的, 但它的注冊(cè)策略和其他的流不同. 從某種程度上來說, 這是因?yàn)橛脩艨臻g對(duì)它們的訪問方式的不同造成的, 它們需要實(shí)現(xiàn)基于套接字的其他因子.

從擴(kuò)展開發(fā)者角度來看, 打開傳輸流的過程是相同的. 下面是對(duì)fsockopen()的實(shí)現(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);
}

這個(gè)函數(shù)的基礎(chǔ)構(gòu)造和前面的fopen示例是一樣的. 不同在于host和端口號(hào)使用不同的參數(shù)指定, 接著為了給出一個(gè)傳輸流URL就必須將它們合并到一起. 在產(chǎn)生了一個(gè)有意義的"路徑:后, 將它傳遞給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);

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

xport基于URI的傳輸描述符. 對(duì)于基于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的長(zhǎng)

度, 因此xport是二進(jìn)制安全的.

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

或組成的值.

flags由STREAM_XPORT_CLIENT或STREAM_XPORT_SERVER之一

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

組合得到的值.

persistent_id如果請(qǐng)求的傳輸流需要在請(qǐng)求間持久化, 調(diào)用作用域可以提供一個(gè)

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

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

找到時(shí)就創(chuàng)建一個(gè)新的持久化流.

timeout在超時(shí)返回失敗之前連接的嘗試時(shí)間. 如果這個(gè)值傳遞為NULL則使

用php.ini中指定的默認(rèn)超時(shí)值. 這個(gè)參數(shù)對(duì)服務(wù)端傳輸流沒有意義.

errstr如果在選定的套接字上創(chuàng)建, 連接, 綁定或監(jiān)聽時(shí)發(fā)生錯(cuò)誤, 這里傳遞

的char *引用值將被設(shè)置為一個(gè)描述發(fā)生錯(cuò)誤原因的字符串. errstr初

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

責(zé)任去釋放這個(gè)字符串相關(guān)的內(nèi)存.

errcode通過errstr返回的錯(cuò)誤消息對(duì)應(yīng)的數(shù)值錯(cuò)誤代碼.

php_stream_xport_create()的flags參數(shù)中使用了STREAM_XPORT_*一族常量定義如下:

STREAM_XPORT_CLIENT本地端將通過傳輸層和遠(yuǎn)程資源建立連接. 這個(gè)

標(biāo)記通常和STREAM_XPORT_CONNECT或

STREAM_XPORT_CONNECT_ASYNC聯(lián)合使

用.

STREAM_XPORT_SERVER本地端將通過傳輸層accept連接. 這個(gè)標(biāo)記通常

和STREAM_XPORT_BIND以及

STREAM_XPORT_LISTEN一起使用.

STREAM_XPORT_CONNECT用以說明建立遠(yuǎn)程資源連接是傳輸流創(chuàng)建的一部

分. 在創(chuàng)建客戶端傳輸流時(shí)省略這個(gè)標(biāo)記是合法

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

php_stream_xport_connect().

STREAM_XPORT_CONNECT_ASYNC嘗試連接到遠(yuǎn)程資源, 但不阻塞.

STREAM_XPORT_BIND將傳輸流綁定到本地資源. 用在服務(wù)端傳輸流時(shí),

這將使得accept連接的傳輸流準(zhǔn)備端口, 路徑或

特定的端點(diǎn)標(biāo)識(shí)符等信息.

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

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

unix://.

目錄訪問

fopen包裝器支持目錄訪問, 比如file://和ftp://, 還有第三種流打開函數(shù)也可以用于目錄訪問, 下面是對(duì)opendir()的實(shí)現(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);
}

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

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

特殊流

還有一些特殊類型的流不能歸類到fopen/transport/directory中. 它們中每一個(gè)都有自己獨(dú)有的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)建一個(gè)可seek的緩沖區(qū)流用于讀寫. 在關(guān)閉時(shí), 這個(gè)流使用的所有臨時(shí)資源, 包括所有的緩沖區(qū)(無論是在內(nèi)存還是磁盤), 都將被釋放. 使用這一組API中的后一個(gè)函數(shù), 允許臨時(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個(gè)API方法接受已經(jīng)打開的FILE *資源或文件描述符ID, 使用流API的某種操作包裝. fd格式的接口不會(huì)搜索匹配你前面看到過的fopen函數(shù)打開的資源, 但是它會(huì)注冊(cè)持久化的資源, 后續(xù)的fopen可以使用到這個(gè)持久化資源.

訪問流

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

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

int php_stream_getc(php_stream *stream);

從數(shù)據(jù)流中接收一個(gè)字符. 如果流上再?zèng)]有數(shù)據(jù), 則返回EOF.

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

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

php_stream_read()不同于其他的流讀取函數(shù). 如果使用的流不是普通文件流, 哪怕數(shù)據(jù)流中有超過請(qǐng)求字節(jié)數(shù)的數(shù)據(jù), 并且當(dāng)前也可以返回, 它也只會(huì)調(diào)用過一次底層流實(shí)現(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);

這兩個(gè)函數(shù)從stream中讀取最多maxlen個(gè)字符, 直到碰到換行符或流結(jié)束. buf可以是一個(gè)指向預(yù)分配的至少maxlen字節(jié)的內(nèi)存空間的指針, 也可以是NULL, 當(dāng)它是NULL時(shí), 則會(huì)自動(dòng)的創(chuàng)建一個(gè)動(dòng)態(tài)大小的緩沖區(qū), 用從流中實(shí)際讀出的數(shù)據(jù)填充, 成功后函數(shù)返回指向緩沖區(qū)的指針, 失敗則返回NULL. 如果returned_len傳遞了非NULL值, 則在返回時(shí)它將被設(shè)置為實(shí)際從流中讀取的字節(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()類似, 這個(gè)函數(shù)將讀取最多maxlen, 或到達(dá)EOF/行結(jié)束第一次出現(xiàn)的位置. 但是它也有和php_stream_get_line()的不同指出, 這個(gè)函數(shù)允許指定任意的停止讀取標(biāo)記.

讀取目錄項(xiàng)

從php流中讀取目錄項(xiàng)和上面從普通文件中讀取普通數(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;

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

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

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

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

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

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

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的版本實(shí)際上是一個(gè)提供便利的宏, 它允許寫一個(gè)NULL終止的字符串,而不用顯式的提供長(zhǎng)度. 返回的是實(shí)際寫到流中的字節(jié)數(shù). 要特別小心的是嘗試寫大數(shù)據(jù)的時(shí)候可能導(dǎo)致流阻塞, 比如套接字流, 而如果流被標(biāo)記為非阻塞, 則實(shí)際寫入的數(shù)據(jù)量可能會(huì)小于傳遞給函數(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()寫入一個(gè)字符或一個(gè)字符串到流中. 要注意, php_stream_puts()不同于php_stream_write_string(), 雖然它們的原型看起來是一樣的, 但是php_stream_puts()會(huì)在寫出buf中的數(shù)據(jù)后自動(dòng)的追加一個(gè)換行符.

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

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

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

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

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

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

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

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

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

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

位置偏移0字節(jié)處. 當(dāng)使用SEEK_SET時(shí), 如果offset傳遞負(fù)值被認(rèn)為是錯(cuò)誤

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

通常是一個(gè)錯(cuò)誤或文件被擴(kuò)大以滿足指定的偏移量.

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

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

+ offset, SEEK_SET);

SEEK_ENDoffset是相對(duì)于當(dāng)前的EOF位置的. 負(fù)值的offset表示在EOF之前的位置, 正

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

int php_stream_rewinddir(php_stream *dirstream);

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

off_t php_stream_tell(php_stream *stream);

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

int php_stream_flush(php_stream *stream);

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

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

調(diào)用php_stream_stat()可以獲取到流實(shí)例的其他信息, 它的行為類似于fstat()函數(shù). 實(shí)際上, php_stream_statbuf結(jié)構(gòu)體現(xiàn)在僅包含一個(gè)元素: 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);
}

關(guān)閉

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

int php_stream_free(php_stream *stream, int options);

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

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

進(jìn)行顯式釋放.

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

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

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

PERSISTENT作用在持久化流上時(shí), 它的行為將是永久的而不局限于當(dāng)前請(qǐng)

求.

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

非持久化流的一般選項(xiàng).

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

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

般選項(xiàng).

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

#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交換流

因?yàn)榱魍ǔS成涞絲val上, 反之亦然, 因此提供了一組宏用來簡(jiǎn)化操作, 并統(tǒng)一編碼(格式):

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

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

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

靜態(tài)資源操作

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

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

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

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

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

LINK原始的php_stream_stat_path()對(duì)于符號(hào)鏈接或目錄將會(huì)進(jìn)行解析直到碰到

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

php_stream_stat_path()返回請(qǐng)求資源的信息而不會(huì)進(jìn)行符號(hào)鏈接的解析.

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

底層使用lstat(), 關(guān)于stat()和lstat()的區(qū)別, 請(qǐng)查看*nix手冊(cè))

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

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

php_stream_stat_path()返回而不報(bào)告錯(cuò)誤.

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)建和刪除目錄也會(huì)如你期望的工作. 這里的options參數(shù)和前面的php_stream_open_wrapper()函數(shù)的同名參數(shù)含義一致. 對(duì)于php_stream_mkdir(), 還有一個(gè)參數(shù)mode用于指定一個(gè)八進(jìn)制的值表明讀寫執(zhí)行權(quán)限.

小結(jié)

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

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


Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Bagaimanakah saya tetap terkini dengan perkembangan php terkini dan amalan terbaik? Bagaimanakah saya tetap terkini dengan perkembangan php terkini dan amalan terbaik? Jun 23, 2025 am 12:56 AM

Tostaycurrentwithphpdevelopmentsandbestpractices, followeyNewsssourcesLikePhp.netandphpweekly, engageWithCommunitiesonforumsandconference, keeptoolingupdatedandgraduallyAdoptNewFeatures, dan readribcoursourcourceSource

Apakah php, dan mengapa ia digunakan untuk pembangunan web? Apakah php, dan mengapa ia digunakan untuk pembangunan web? Jun 23, 2025 am 12:55 AM

Phpbecamepopularforwebdevelopmentduetoitseaseoflearning, seamlessintegrationwithhtml, widespreadhostingsupport, andalargeecosystemincludingframeworkslikelaravelandcmsplatformsLikeWordPress.itexcelsinhandessubmissions

Bagaimana cara menetapkan zon waktu php? Bagaimana cara menetapkan zon waktu php? Jun 25, 2025 am 01:00 AM

TosetTheRightTimeZoneinPhp, USEDATE_DEFAULT_TIMEZONE_SET () functionAtthestArtAfyourscriptwithavalididentifiersuchas'america/new_york'.1.usedate_default_timeSet ()

Bagaimanakah saya mengesahkan input pengguna dalam PHP untuk memastikan ia memenuhi kriteria tertentu? Bagaimanakah saya mengesahkan input pengguna dalam PHP untuk memastikan ia memenuhi kriteria tertentu? Jun 22, 2025 am 01:00 AM

TOVALIDATEUSERIputInphp, UsEbuilt-InvalidationFunctionsLikeFilter_var () danFilter_Input (), applyRegularExpressionsforcustomformatssuchasususerorphonenumbers, checkdatatypesfornumericressplimeSpriceSprice, setLengthacheAngeAgeorpriceSprice, setLengthacheArpesenprice,

Apakah Serialization Data dalam PHP (Serialize (), Unserialize ())? Apakah Serialization Data dalam PHP (Serialize (), Unserialize ())? Jun 22, 2025 am 01:03 AM

ThPhpFunctionserialize () andUnserialize () diselaraskanToConvertComplexDataStructructDestoresIntoStorasandabackagain.1.Serialize () C overtsdatalikeCarraysorObjectSrayStringContainingTyPeanStructureStructureStructureStructure.2.2Serialize ()

Bagaimana saya membenamkan kod php dalam fail html? Bagaimana saya membenamkan kod php dalam fail html? Jun 22, 2025 am 01:00 AM

Anda boleh membenamkan kod PHP ke dalam fail HTML, tetapi pastikan fail itu mempunyai lanjutan .php supaya pelayan dapat menghuraikannya dengan betul. Gunakan tag standard untuk membungkus kod PHP, masukkan kandungan dinamik di mana sahaja di HTML. Di samping itu, anda boleh menukar PHP dan HTML beberapa kali dalam fail yang sama untuk merealisasikan fungsi dinamik seperti rendering bersyarat. Pastikan anda memberi perhatian kepada konfigurasi pelayan dan ketepatan sintaks untuk mengelakkan masalah yang disebabkan oleh label pendek, kesilapan tanda petikan atau label akhir yang ditinggalkan.

Apakah amalan terbaik untuk menulis kod PHP yang bersih dan boleh dipelihara? Apakah amalan terbaik untuk menulis kod PHP yang bersih dan boleh dipelihara? Jun 24, 2025 am 12:53 AM

Kunci untuk menulis kod PHP yang bersih dan mudah dijaga terletak pada penamaan yang jelas, berikutan piawaian, struktur yang munasabah, menggunakan komen dan kesesuaian yang baik. 1. Gunakan pembolehubah, fungsi dan nama kelas yang jelas, seperti $ userData dan calculateToTalPrice (); 2. Ikuti gaya kod bersatu piawai PSR-12; 3. Pecahkan struktur kod mengikut tanggungjawab, dan aturnya menggunakan katalog MVC atau Laravel; 4. Elakkan kod gaya mi dan memecah logik ke dalam fungsi kecil dengan satu tanggungjawab; 5. Tambah komen pada mata utama dan tulis dokumen antara muka untuk menjelaskan parameter, pulangan nilai dan pengecualian; 6. Meningkatkan kebolehlihatan, mengguna pakai suntikan pergantungan, mengurangkan kaedah keadaan global dan statik. Amalan ini meningkatkan kualiti kod, kecekapan kerjasama dan kemudahan pasca penyelenggaraan.

Bagaimana saya melaksanakan pertanyaan SQL menggunakan php? Bagaimana saya melaksanakan pertanyaan SQL menggunakan php? Jun 24, 2025 am 12:54 AM

Ya, youpanrunsqlqueriesusingphp, danTheProcessinvolveschoosingadatabaseextension, connectingTothedatabase, executingqueriSafely, andclosingconnectionswhendone.todothis, firstChoosebetweBetbeSquLiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpiorpdob

See all articles