<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 14.1 流的概覽 通常, 直接的文件描述符相比調用流包裝層消耗更少的CPU和內存; 然而, 這樣會將實現某個特定協議的所有工作都堆積到作為擴展開發者的你身上. 通過掛鉤到流包裝層, 你的擴展代碼可以透明的使用各種內建的流包裝, 比如HTTP, FTP, 以及它們對應的SSL版本, 另外還有gzip和bzip2壓縮包裝. 通過include特定的PEAR或PECL模塊, 你的代碼還可以訪問其他協議, 比如SSH2, WebDav, 甚至是Gopher! 本章將介紹內部基于流工作的基礎API. 后面到第16章"有趣的流"中, 我們將看到諸如應用過濾器, 使用上下文選項和參數等高級概念. ## 打開流 盡管是一個統一的API, 但實際上依賴于所需的流的類型, 有四種不同的路徑去打開一個流. 從用戶空間角度來看, 這四種不同的類別如下(函數列表只代表示例, 不是完整列表): ```php <?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); ``` 無論你打開的是什么類型的流, 它們都存儲在一個公共的結構體php_stream中. ## fopen包裝 我們首先從實現fopen()函數開始. 現在你應該已經對創建擴展骨架很熟悉了, 如果還不熟悉, 請回到第5章"你的第一個擴展"復習一下, 下面是我們實現的fopen()函數: ```c 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是位域的標記值集合, 這里是設置為下面介紹的一組固定值: <table> <tr> <td>USE_PATH</td> <td>將php.ini文件中的include_path應用到相對路徑上. 內建函數fopen()在指定第三個參數為TRUE時將會設置這個選項.</td> </tr> <tr> <td>STREAM_USE_URL</td> <td>設置這個選項后, 將只能打開遠端URL. 對于php://, file://, zlib://, bzip2://這些URL包裝器并不認為它們是遠端URL.</td></tr> <tr><td>ENFORCE_SAFE_MODE</td> <td>盡管這個常量這樣命名, 但實際上設置這個選項后僅僅是啟用了安全模式(php.ini文件中的safe_mode指令)的強制檢查. 如果沒有設置這個選項將導致跳過safe_mode的檢查(不論INI設置中safe_mode如何設置)</td></tr> <tr><td>REPORT_ERRORS</td> <td>在指定的資源打開過程中碰到錯誤時, 如果設置了這個選項則將產生錯誤報告.</td></tr> <tr><td>STREAM_MUST_SEEK</td> <td>對于某些流, 比如套接字, 是不可以seek的(隨機訪問); 這類文件句柄, 只有在特定情況下才可以seek. 如果調用作用域指定這個選項, 并且包裝器檢測到它不能保證可以seek, 將會拒絕打開這個流.</td></tr> <tr><td>STREAM_WILL_CAST</td> <td>如果調用作用域要求流可以被轉換到stdio或posix文件描述符, 則應該給open_wrapper函數傳遞這個選項, 以保證在I/O操作發生之前就失敗</td></tr> <tr><td>STREAM_ONLY_GET_HEADERS</td> <td>標識只需要從流中請求元數據. 實際上這是用于http包裝器, 獲取http_response_headers全局變量而不真正的抓取遠程文件內容.</td></tr> <tr><td>STREAM_DISABLE_OPEN_BASEDIR</td> <td>類似safe_mode檢查, 不設置這個選項則會檢查INI設置open_basedir, 如果指定這個選項則可以繞過這個默認的檢查</td></tr> <tr><td>STREAM_OPEN_PERSISTENT</td> <td>告知流包裝層, 所有內部分配的空間都采用持久化分配, 并將關聯的資源注冊到持久化列表中.</td></tr> <tr><td>IGNORE_PATH</td> <td>如果不指定, 則搜索默認的包含路徑. 多數URL包裝器都忽略這個選項.</td></tr> <tr><td>IGNORE_URL</td> <td>提供這個選項時, 流包裝層只打開本地文件. 所有的is_url包裝器都將被忽略.</td></tr> </table> 最后的NULL參數是char **類型, 它最初是用來設置匹配路徑, 如果path指向普通文件URL, 則去掉file://部分, 保留直接的文件路徑用于傳統的文件名操作. 這個參數僅僅是以前引擎內部處理使用的. 此外, 還有php_stream_open_wrapper()的一個擴展版本: ```c php_stream *php_stream_open_wrapper_ex(char *path, char *mode, int options, char **opened_path, php_stream_context *context); ```` 最后一個參數context允許附加的控制, 并可以得到包裝器內的通知. 你將在第16章看到這個參數的細節. ## 傳輸層包裝 盡管傳輸流和fopen包裝流是相同的組件組成的, 但它的注冊策略和其他的流不同. 從某種程度上來說, 這是因為用戶空間對它們的訪問方式的不同造成的, 它們需要實現基于套接字的其他因子. 從擴展開發者角度來看, 打開傳輸流的過程是相同的. 下面是對fsockopen()的實現: ```c PHP_FUNCTION(sample5_fsockopen) php_stream *stream; char *host, *transport, *errstr = NULL; int host_len, transport_len, implicit_tcp = 1, errcode = 0; long port = 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'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); } ``` 這個函數的基礎構造和前面的fopen示例是一樣的. 不同在于host和端口號使用不同的參數指定, 接著為了給出一個傳輸流URL就必須將它們合并到一起. 在產生了一個有意義的路徑后, 將它傳遞給php_stream_xport_create()函數, 方式和fopen()使用的php_stream_open_wrapper()API一樣. php_stream_xport_create()的原型如下: ```c 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); ``` 每個參數的含義如下: <table> <tr> <td>xport</td> <td>基于URI的傳輸描述符. 對于基于inet的套接字流, 它可以是tcp://127.0.0.1:80, udp://10.0.0.1:53, ssl://169.254.13.24:445等. 此外, UNIX域傳輸協議unix:///path/to/socket,udg:///path/to/dgramsocket等都是合法的. xport_len指定了xport的長度, 因此xport是二進制安全的.</td> </tr> <tr> <td>options</td> <td>這個值是由前面php_stream_open_wrapper()中介紹的選項通過按位或組成的值.</td> </tr> <tr> <td>flags</td> <td>由STREAM_XPORT_CLIENT或STREAM_XPORT_SERVER之一與下面另外一張表中將列出的STREAM_XPORT_*常量通過按位或組合得到的值.</td> </tr> <tr> <td>persistent_id</td> <td>如果請求的傳輸流需要在請求間持久化, 調用作用域可以提供一個key名字描述連接. 指定這個值為NULL創建非持久化連接; 指定為唯一的字符串值將嘗試首先從持久化池中查找已有的傳輸流, 或者沒有找到時就創建一個新的持久化流.</td> </tr> <tr> <td>timeout</td> <td>在超時返回失敗之前連接的嘗試時間. 如果這個值傳遞為NULL則使用php.ini中指定的默認超時值. 這個參數對服務端傳輸流沒有意義.</td> </tr> <tr> <td>errstr</td> <td>如果在選定的套接字上創建, 連接, 綁定或監聽時發生錯誤, 這里傳遞的char *引用值將被設置為一個描述發生錯誤原因的字符串. errstr初始應該指向的是NULL; 如果在返回時它被設置了值, 則調用作用域有責任去釋放這個字符串相關的內存.</td> </tr> <tr> <td>errcode</td> <td>通過errstr返回的錯誤消息對應的數值錯誤代碼.php_stream_xport_create()的flags參數中使用了STREAM_XPORT_*一族常量定義如下: <table> <tr> <td>STREAM_XPORT_CLIENT</td> <td>本地端將通過傳輸層和遠程資源建立連接. 這個標記通常和STREAM_XPORT_CONNECT或STREAM_XPORT_CONNECT_ASYNC聯合使用.</td> </tr> <tr> <td>STREAM_XPORT_SERVER</td> <td>本地端將通過傳輸層accept連接. 這個標記通常和STREAM_XPORT_BIND以及STREAM_XPORT_LISTEN一起使用.</td> </tr> <tr> <td>STREAM_XPORT_CONNECT</td> <td>用以說明建立遠程資源連接是傳輸流創建的一部分. 在創建客戶端傳輸流時省略這個標記是合法的, 但是這樣做就要求手動的調用php_stream_xport_connect().</td> </tr> <tr> <td>STREAM_XPORT_CONNECT_ASYNC</td> <td>嘗試連接到遠程資源, 但不阻塞。</td> </tr> <tr> <td>STREAM_XPORT_BIND</td> <td>將傳輸流綁定到本地資源. 用在服務端傳輸流時,這將使得accept連接的傳輸流準備端口, 路徑或特定的端點標識符等信息.</td> </tr> <tr> <td>STREAM_XPORT_LISTEN</td> <td>在已綁定的傳輸流端點上監聽到來的連接. 這通常用于基于流的傳輸協議, 比如: tcp://, ssl://,unix://.</td> </tr> </table> </td> </tr> </table> ## 目錄訪問 fopen包裝器支持目錄訪問, 比如file://和ftp://, 還有第三種流打開函數也可以用于目錄訪問, 下面是對opendir()的實現: ```c 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); } ``` 同樣的, 也可以為某個特定目錄打開一個流, 比如本地文件系統的目錄名或支持目錄訪問的URL格式資源. 這里我們又看到了options參數, 它和原來的含義一樣, 第三個參數NULL原型是php_stream_context類型. 在目錄流打開后, 和文件以及傳輸流一樣, 返回給用戶空間. ## 特殊流 還有一些特殊類型的流不能歸類到fopen/transport/directory中. 它們中每一個都有自己獨有的API: ```c php_stream *php_stream_fopen_tmpfile(void); php_stream *php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path); ``` 創建一個可seek的緩沖區流用于讀寫. 在關閉時, 這個流使用的所有臨時資源, 包括所有的緩沖區(無論是在內存還是磁盤), 都將被釋放. 使用這一組API中的后一個函數, 允許臨時文件被以特定的格式命名放到指定路徑. 這些內部API調用被用戶空間的tmpfile()函數隱藏. ```c 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方法接受已經打開的FILE *資源或文件描述符ID, 使用流API的某種操作包裝. fd格式的接口不會搜索匹配你前面看到過的fopen函數打開的資源, 但是它會注冊持久化的資源, 后續的fopen可以使用到這個持久化資源. ## links * [目錄](<preface.md>) * 14 [流式訪問](<14.md>) * 14.2 [訪問流](<14.2.md>)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看