本文實(shí)例講述了PHP流Streams、包裝器wrapper概念與用法。分享給大家供大家參考,具體如下:
流Streams這個(gè)概念是在php4.3引進(jìn)的,是對(duì)流式數(shù)據(jù)的抽象,用于統(tǒng)一數(shù)據(jù)操作,比如文件數(shù)據(jù)、網(wǎng)絡(luò)數(shù)據(jù)、壓縮數(shù)據(jù)等,以使可以共享同一套函數(shù),php的文件系統(tǒng)函數(shù)就是這樣的共享,比如file_get_contents()函數(shù)即可打開(kāi)本地文件也可以訪問(wèn)url就是這一體現(xiàn)。簡(jiǎn)單點(diǎn)講,流就是表現(xiàn)出流式數(shù)據(jù)行為的資源對(duì)象。
以線性方式進(jìn)行讀寫(xiě),并可以在流里面任意位置進(jìn)行搜索。
流有點(diǎn)類似數(shù)據(jù)庫(kù)抽象層,在數(shù)據(jù)庫(kù)抽象層方面,不管使用何種數(shù)據(jù)庫(kù),在抽象層之上都使用相同的方式操作數(shù)據(jù),而流是對(duì)數(shù)據(jù)的抽象,它不管是本地文件還是遠(yuǎn)程文件還是壓縮文件等等,只要來(lái)的是流式數(shù)據(jù),那么操作方式就是一樣的。
有了流這個(gè)概念就引申出了包裝器wrapper這個(gè)概念,每個(gè)流都對(duì)應(yīng)一種包裝器,流是從統(tǒng)一操作這個(gè)角度產(chǎn)生的一個(gè)概念,而包裝器呢是從理解流數(shù)據(jù)內(nèi)容出發(fā)產(chǎn)生的一個(gè)概念,也就是這個(gè)統(tǒng)一的操作方式怎么操作或配置不同的內(nèi)容;
這些內(nèi)容都是以流的方式呈現(xiàn),但內(nèi)容規(guī)則是不一樣的,比如http協(xié)議傳來(lái)的數(shù)據(jù)是流的方式,但只有http包裝器才理解http協(xié)議傳來(lái)的數(shù)據(jù)的意思,可以這么理解,流就是一根流水的管子,只不過(guò)它流出的是數(shù)據(jù),包裝器就是套在流這根管子外層的一個(gè)解釋者,它理解流出的數(shù)據(jù)的意思,并能操作它。
官方手冊(cè)說(shuō):“一個(gè)包裝器是告訴流怎么處理特殊協(xié)議或編碼的附加代碼”明白這句話的意思了嗎?
包裝器可以嵌套,一個(gè)流外面包裹了一個(gè)包裝器后,還可以在外層繼續(xù)包裹包裝器,這個(gè)時(shí)候里層的包裝器相對(duì)于外層的包裝器充當(dāng)流的角色
在php自身底層實(shí)現(xiàn)的c語(yǔ)言開(kāi)發(fā)文檔有這樣的解釋:
流API操作一對(duì)不同級(jí)別:在基本級(jí)別,api定義了php_stream對(duì)象表示流式數(shù)據(jù)源,在稍微高一點(diǎn)的級(jí)別,api定義了php_stream_wrapper對(duì)象。
它包裹低一級(jí)別的php_stream對(duì)象,以提供取回URL的內(nèi)容和元數(shù)據(jù)、添加上下文參數(shù)的能力,調(diào)整包裝器行為;
每一種流打開(kāi)后都可以應(yīng)用任意數(shù)量的過(guò)濾器在上面,流數(shù)據(jù)會(huì)經(jīng)過(guò)過(guò)濾器的處理,筆者認(rèn)為過(guò)濾器這個(gè)詞用得有點(diǎn)不準(zhǔn)確,有些誤導(dǎo)人。
從字面意思看好像是去掉一些數(shù)據(jù)的感覺(jué),應(yīng)該稱為數(shù)據(jù)調(diào)整器,因?yàn)樗瓤扇サ粢恍?shù)據(jù),也可以添加,還可以修改,但歷史原因約定俗成,也就稱為過(guò)濾器了,大家心里明白就好。
我們經(jīng)??吹较旅娴脑~,來(lái)解釋下他們的區(qū)別:
資源和數(shù)據(jù):資源是比較宏觀的說(shuō)法,通常包含數(shù)據(jù),而數(shù)據(jù)是比較具象的說(shuō)法,在開(kāi)發(fā)程序的時(shí)候經(jīng)常說(shuō)是數(shù)據(jù),而在軟件規(guī)劃時(shí)說(shuō)是資源,他們是近義詞,就像軟件設(shè)計(jì)和程序開(kāi)發(fā)的區(qū)別一樣。
上下文和參數(shù):上下文是比較宏觀的說(shuō)法,經(jīng)常用在溝通上面,具體點(diǎn)講就是一次溝通本身的參數(shù),而參數(shù)這個(gè)說(shuō)法往往用在比較具體的事情上面,比如說(shuō)函數(shù)
上面解釋了概念性的東西,下面來(lái)看看具體內(nèi)容:
php支持的協(xié)議和包裝器請(qǐng)看這里:http://php.net/manual/zh/wrappers.php:
(筆者注:原標(biāo)題是:支持的協(xié)議和封裝協(xié)議,中文翻譯有點(diǎn)誤導(dǎo),準(zhǔn)確的講就是支持的協(xié)議和包裝器,從英文版面就很清楚)
默認(rèn)的支持了一些協(xié)議和包裝器,請(qǐng)用stream_get_wrappers()函數(shù)查看.也可以自定義一個(gè)包裝器,用stream_wrapper_register()注冊(cè)
盡管RFC 3986里面可以使用:做分割符,但php只允許://,所以u(píng)rl請(qǐng)使用"scheme://target"這樣的格式
file:// — 訪問(wèn)本地文件系統(tǒng),在用文件系統(tǒng)函數(shù)時(shí)默認(rèn)就使用該包裝器
http:// — 訪問(wèn) HTTP(s) 網(wǎng)址
ftp:// — 訪問(wèn) FTP(s) URLs
php:// — 訪問(wèn)各個(gè)輸入/輸出流(I/O streams)
zlib:// — 壓縮流
data:// — 數(shù)據(jù)(RFC 2397)
glob:// — 查找匹配的文件路徑模式
phar:// — PHP 歸檔
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音頻流
expect:// — 處理交互式的流
如何實(shí)現(xiàn)一個(gè)自定義的包裝器:
在用fopen、fwrite、fread、fgets、feof、rewind、file_put_contents、file_get_contents等等文件系統(tǒng)函數(shù)操作流時(shí),數(shù)據(jù)是先傳給定義的包裝器類對(duì)象,包裝器再去操作流。
如何實(shí)現(xiàn)一個(gè)自定義的流包裝器呢?php提供了一個(gè)類原型,只是原型而已,不是接口也不是類,不能用于繼承:
streamWrapper {
/* 屬性 */
public resource $context ;
/* 方法 */
__construct ( void )
__destruct ( void )
public bool dir_closedir ( void )
public bool dir_opendir ( string $path , int $options )
public string dir_readdir ( void )
public bool dir_rewinddir ( void )
public bool mkdir ( string $path , int $mode , int $options )
public bool rename ( string $path_from , string $path_to )
public bool rmdir ( string $path , int $options )
public resource stream_cast ( int $cast_as )
public void stream_close ( void )
public bool stream_eof ( void )
public bool stream_flush ( void )
public bool stream_lock ( int $operation )
public bool stream_metadata ( string $path , int $option , mixed $value )
public bool stream_open ( string $path , string $mode , int $options , string $opened_path )
public string stream_read ( int $count )
public bool stream_seek ( int $offset , int $whence = SEEK_SET )
public bool stream_set_option ( int $option , int $arg1 , int $arg2 )
public array stream_stat ( void )
public int stream_tell ( void )
public bool stream_truncate ( int $new_size )
public int stream_write ( string $data )
public bool unlink ( string $path )
public array url_stat ( string $path , int $flags )
}
在這個(gè)原型里面定義的方法,根據(jù)自己需要去定義,并不要求全部實(shí)現(xiàn),這就是為什么不定義成接口的原因,因?yàn)橛行?shí)現(xiàn)根本用不著某些方法,
這帶來(lái)很多靈活性,比如包裝器是不支持刪除目錄rmdir功能的,那么就不需要實(shí)現(xiàn)streamWrapper::rmdir
由于未實(shí)現(xiàn)它,如果用戶在包裝器上調(diào)用rmdir將有錯(cuò)誤拋出,要自定義這個(gè)錯(cuò)誤那么也可以實(shí)現(xiàn)它并在其內(nèi)部拋出錯(cuò)誤
streamWrapper也不是一個(gè)預(yù)定義類,測(cè)試class_exists("streamWrapper")
就知道,它只是一個(gè)指導(dǎo)開(kāi)發(fā)者的原型
官方手冊(cè)提供了一個(gè)例子:http://php.net/manual/zh/stream.streamwrapper.example-1.php
本博客提供一個(gè)從drupal8系統(tǒng)中抽取修改過(guò)的包裝器例子,請(qǐng)看drupal8源碼分析關(guān)于流那一部分
流系列函數(shù),官方手冊(cè):http://php.net/manual/zh/ref.stream.php
常用的函數(shù)如下:
stream_bucket_append函數(shù):為隊(duì)列添加數(shù)據(jù)
stream_bucket_make_writeable函數(shù):從操作的隊(duì)列中返回一個(gè)數(shù)據(jù)對(duì)象
stream_bucket_new函數(shù):為當(dāng)前隊(duì)列創(chuàng)建一個(gè)新的數(shù)據(jù)
stream_bucket_prepend函數(shù):預(yù)備數(shù)據(jù)到隊(duì)列
stream_context_create函數(shù):創(chuàng)建數(shù)據(jù)流上下文
stream_context_get_default函數(shù):獲取默認(rèn)的數(shù)據(jù)流上下文
stream_context_get_options函數(shù):獲取數(shù)據(jù)流的設(shè)置
stream_context_set_option函數(shù):對(duì)數(shù)據(jù)流、數(shù)據(jù)包或者上下文進(jìn)行設(shè)置
stream_context_set_params函數(shù):為數(shù)據(jù)流、數(shù)據(jù)包或者上下文設(shè)置參數(shù)
stream_copy_to_stream函數(shù):在數(shù)據(jù)流之間進(jìn)行復(fù)制操作
stream_filter_append函數(shù):為數(shù)據(jù)流添加過(guò)濾器
stream_filter_prepend函數(shù):為數(shù)據(jù)流預(yù)備添加過(guò)濾器
stream_filter_register函數(shù):注冊(cè)一個(gè)數(shù)據(jù)流的過(guò)濾器并作為PHP類執(zhí)行
stream_filter_remove函數(shù):從一個(gè)數(shù)據(jù)流中移除過(guò)濾器
stream_get_contents函數(shù):讀取數(shù)據(jù)流中的剩余數(shù)據(jù)到字符串
stream_get_filters函數(shù):返回已經(jīng)注冊(cè)的數(shù)據(jù)流過(guò)濾器列表
stream_get_line函數(shù):按照給定的定界符從數(shù)據(jù)流資源中獲取行
stream_get_meta_data函數(shù):從封裝協(xié)議文件指針中獲取報(bào)頭/元數(shù)據(jù)
stream_get_transports函數(shù):返回注冊(cè)的Socket傳輸列表
stream_get_wrappers函數(shù):返回注冊(cè)的數(shù)據(jù)流列表
stream_register_wrapper函數(shù):注冊(cè)一個(gè)用PHP類實(shí)現(xiàn)的URL封裝協(xié)議
stream_select函數(shù):接收數(shù)據(jù)流數(shù)組并等待它們狀態(tài)的改變
stream_set_blocking函數(shù):將一個(gè)數(shù)據(jù)流設(shè)置為堵塞或者非堵塞狀態(tài)
stream_set_timeout函數(shù):對(duì)數(shù)據(jù)流進(jìn)行超時(shí)設(shè)置
stream_set_write_buffer函數(shù):為數(shù)據(jù)流設(shè)置緩沖區(qū)
stream_socket_accept函數(shù):接受由函數(shù)stream_ socket_server()創(chuàng)建的Socket連接
stream_socket_client函數(shù):打開(kāi)網(wǎng)絡(luò)或者UNIX主機(jī)的Socket連接
stream_socket_enable_crypto函數(shù):為一個(gè)已經(jīng)連接的Socket打開(kāi)或者關(guān)閉數(shù)據(jù)加密
stream_socket_get_name函數(shù):獲取本地或者網(wǎng)絡(luò)Socket的名稱
stream_socket_pair函數(shù):創(chuàng)建兩個(gè)無(wú)區(qū)別的Socket數(shù)據(jù)流連接
stream_socket_recvfrom函數(shù):從Socket獲取數(shù)據(jù),不管其連接與否
stream_socket_sendto函數(shù):向Socket發(fā)送數(shù)據(jù),不管其連接與否
stream_socket_server函數(shù):創(chuàng)建一個(gè)網(wǎng)絡(luò)或者UNIX Socket服務(wù)端
stream_wrapper_restore函數(shù):恢復(fù)一個(gè)事先注銷的數(shù)據(jù)包
stream_wrapper_unregister函數(shù):注銷一個(gè)URL地址包
一個(gè)過(guò)濾器的列子及解釋:
官網(wǎng)相關(guān)鏈接:
用戶過(guò)濾器基類:http://php.net/manual/zh/class.php-user-filter.php
過(guò)濾器注冊(cè):http://php.net/manual/zh/function.stream-filter-register.php
?php
/* 定義一個(gè)過(guò)濾器 */
class strtoupper_filter extends php_user_filter {
function filter($in, $out, $consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) { //從流里面取出一段數(shù)據(jù)
$bucket->data = strtoupper($bucket->data);
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket); //將修改后的數(shù)據(jù)送到輸出的地方
}
return PSFS_PASS_ON;
}
}
/* 注冊(cè)過(guò)濾器到php */
stream_filter_register("strtoupper", "strtoupper_filter")
or die("Failed to register filter");
$fp = fopen("foo-bar.txt", "w");
/* 應(yīng)用過(guò)濾器到一個(gè)流 */
stream_filter_append($fp, "strtoupper");
fwrite($fp, "Line1\n");
fwrite($fp, "Word - 2\n");
fwrite($fp, "Easy As 123\n");
fclose($fp);
//讀取并顯示內(nèi)容 將全部變?yōu)榇髮?xiě)
readfile("foo-bar.txt");
?>
更多關(guān)于PHP相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《PHP基本語(yǔ)法入門教程》、《PHP運(yùn)算與運(yùn)算符用法總結(jié)》、《php面向?qū)ο蟪绦蛟O(shè)計(jì)入門教程》、《PHP網(wǎng)絡(luò)編程技巧總結(jié)》、《php字符串(string)用法總結(jié)》、《php+mysql數(shù)據(jù)庫(kù)操作入門教程》及《php常見(jiàn)數(shù)據(jù)庫(kù)操作技巧匯總》
希望本文所述對(duì)大家PHP程序設(shè)計(jì)有所幫助。
您可能感興趣的文章:- PHP中phar包的使用教程
- 解決PHP 7編譯安裝錯(cuò)誤:cannot stat ‘phar.phar’: No such file or directory
- PHP管理依賴(dependency)關(guān)系工具 Composer 安裝與使用
- PHP管理依賴(dependency)關(guān)系工具 Composer的自動(dòng)加載(autoload)
- PHPer 需要了解的 5 個(gè) Composer 小技巧
- PHP 開(kāi)發(fā)者該知道的 5 個(gè) Composer 小技巧
- 用 Composer構(gòu)建自己的 PHP 框架之基礎(chǔ)準(zhǔn)備
- PHP單元測(cè)試?yán)?PHPUNIT深入用法(三)
- PHP單元測(cè)試?yán)?PHPUNIT深入用法(二)
- PHP單元測(cè)試?yán)?PHPUNIT初探
- PHP開(kāi)發(fā)之歸檔格式phar文件概念與用法詳解【創(chuàng)建,使用,解包還原提取】