訪問流
PHP用戶空間中所有的文件I/O處理都是通過php 4.3引入的php流包裝層處理的. 在內部, 擴展代碼可以選擇使用stdio或posix文件處理和本地文件系統或伯克利域套接字進行通信, 或者也可以調用和用戶空間流I/O相同的API.
流的概覽
通常, 直接的文件描述符相比調用流包裝層消耗更少的CPU和內存; 然而, 這樣會將實現某個特定協議的所有工作都堆積到作為擴展開發者的你身上. 通過掛鉤到流包裝層, 你的擴展代碼可以透明的使用各種內建的流包裝, 比如HTTP, FTP, 以及它們對應的SSL版本, 另外還有gzip和bzip2壓縮包裝. 通過include特定的PEAR或PECL模塊, 你的代碼還可以訪問其他協議, 比如SSH2, WebDav, 甚至是Gopher!
本章將介紹內部基於流工作的基礎API. 後面到第16章"有趣的流"中, 我們將看到諸如應用過濾器, 使用上下文選項和參數等高級概念.
打開流
盡管是一個統一的API, 但實際上依賴於所需的流的類型, 有四種不同的路徑去打開一個流. 從用戶空間角度來看, 這四種不同的類別如下(函數列表只代表示例, 不是完整列表):
<?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()函數:
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應用到相對路徑上. 內建函數fopen()在指定第三個參數為TRUE時將會設置這個選項.
STREAM_USE_URL設置這個選項後, 將只能打開遠端URL. 對於php://, file://, zlib://, bzip2://這些URL包裝器並不認為它們是遠端URL.
ENFORCE_SAFE_MODE盡管這個常量這樣命名, 但實際上設置這個選項後僅僅是啟用了安全模式(php.ini文件中的safe_mode指令)的強制檢查. 如果沒有設置這個選項將導致跳過safe_mode的檢查(不論INI設置中safe_mode如何設置)
REPORT_ERRORS在指定的資源打開過程中碰到錯誤時, 如果設置了這個選項則將產生錯誤報告.
STREAM_MUST_SEEK對於某些流, 比如套接字, 是不可以seek的(隨機訪問); 這類文件句柄, 只有在特定情況下才可以seek. 如果調用作用域指定這個選項, 並且包裝器檢測到它不能保證可以seek, 將會拒絕打開這個流.
STREAM_WILL_CAST如果調用作用域要求流可以被轉換到stdio或posix文件描述符, 則應該給open_wrapper函數傳遞這個選項, 以保證在I/O操作發生之前就失敗STREAM_ONLY_GET_HEADERS標識只需要從流中請求元數據. 實際上這是用於http包裝器, 獲取http_response_headers全局變量而不真正的抓取遠程文件內容.
STREAM_DISABLE_OPEN_BASEDIR類似safe_mode檢查, 不設置這個選項則會檢查INI設置open_basedir, 如果指定這個選項則可以繞過這個默認的檢查
STREAM_OPEN_PERSISTENT告知流包裝層, 所有內部分配的空間都采用持久化分配, 並將關聯的資源注冊到持久化列表中.
IGNORE_PATH如果不指定, 則搜索默認的包含路徑. 多數URL包裝器都忽略這個選項.
IGNORE_URL提供這個選項時, 流包裝層只打開本地文件. 所有的is_url包裝器都將被忽略.