今天跟大家來說一下講述了PHP流Streams、包裝器wrapper概念與用法。總結給大家看一下。
我們知道,流Streams這個概念是在php4.3引進的,是對串流資料的抽象,用於統一資料操作,例如檔案資料、網路資料、壓縮資料等,以使可以共享相同套函數,php的檔案系統函數就是這樣的共享,例如file_get_contents()函數即可開啟本機檔案也可以存取url就是這一體現。簡單點講,流就是表現出串流資料行為的資源物件。
以線性方式進行讀寫,並且可以在流裡面任意位置進行搜尋。
流有點類似資料庫抽象層,在資料庫抽象層方面,不管使用何種資料庫,在抽象層之上都使用相同的方式操作數據,而流是對資料的抽象,它不管是本地文件還是遠端文件還是壓縮文件等等,只要來的是串流數據,那麼操作方式就是一樣的。
有了流這個概念就引申出了包裝器wrapper這個概念,每個流都對應一種包裝器,流是從統一操作這個角度產生的一個概念,而包裝器呢是從理解流資料內容出發產生的一個概念,也就是這個統一的操作方式怎麼操作或配置不同的內容;
這些內容都是以流的方式呈現,但內容規則是不一樣的,比如http協議傳來的數據是流的方式,但只有http包裝器才理解http協議傳來的數據的意思,可以這麼理解,流就是一根流水的管子,只不過它流出的是數據,包裝器就是套在流這根管子外層的一個解釋者,它理解流出的資料的意思,並且能操作它。
官方手冊說:「一個包裝器是告訴流怎麼處理特殊協議或編碼的附加代碼」明白這句話的意思了嗎?
包裝器可以嵌套,一個流外包裹了一個包裝器後,還可以在外層繼續包裹包裝器,這個時候裡層的包裝器相對於外層的包裝器充當流的角色
在php自身底層實現的c語言開發文件有這樣的解釋:
流API操作一對不同級別:在基本級別,api定義了php_stream物件表示串流資料來源,在稍微高一點的級別,api定義了php_stream_wrapper物件。
它包覆低一級的php_stream對象,以提供取回URL的內容和元資料、新增上下文參數的能力,調整包裝器行為;
每一種流打開後都可以應用任意數量的過濾器在上面,流資料會經過過濾器的處理,筆者認為過濾器這個詞用得有點不準確,有些誤導人。
從字面意思看好像是去掉一些數據的感覺,應該稱為數據調整器,因為它既可去掉一些數據,也可以添加,還可以修改,但歷史原因約定俗成,也就稱為過濾器了,大家心裡明白就好。
我們經常看到下面的詞,來解釋下他們的區別:
資源和數據:資源是比較宏觀的說法,通常包含數據,而數據是比較具象的說法,在開發程式的時候常說是數據,而在軟體規劃時說是資源,他們是近義詞,就像軟體設計和程式開發的區別一樣。
上下文與參數:上下文是比較宏觀的說法,常用在溝通上面,具體點講就是一次溝通本身的參數,而參數這個說法往往用在比較具體的事情上面,比如說函數
上面解釋了概念性的東西,下面來看看具體內容:
php支援的協定和包裝器請看這裡:http://php.net/manual/zh/wrappers .php:
(筆者註:原標題是:支援的協議和封裝協議,中文翻譯有點誤導,準確的講就是支持的協議和包裝器,從英文版面就很清楚)
默認的支持了一些協定和包裝器,請用stream_get_wrappers()函數查看.也可以自訂一個包裝器,用stream_wrapper_register()註冊
儘管RFC 3986裡面可以使用:做分割符,但php只允許://,所以url請使用"scheme://target"這樣的格式
file:// — 访问本地文件系统,在用文件系统函数时默认就使用该包装器 http:// — 访问 HTTP(s) 网址 ftp:// — 访问 FTP(s) URLs php:// — 访问各个输入/输出流(I/O streams) zlib:// — 压缩流 data:// — 数据(RFC 2397) glob:// — 查找匹配的文件路径模式 phar:// — PHP 归档 ssh2:// — Secure Shell 2 rar:// — RAR ogg:// — 音频流 expect:// — 处理交互式的流
如何實作一個自訂的包裝器:
在用fopen、fwrite、fread、fgets、feof、rewind、 file_put_contents、file_get_contents等等檔案系統函數操作流時,資料是先傳給定義的包裝器類別對象,包裝器再去操作流。
如何實作一個自訂的流包裝器呢? php提供了一個類別原型,只是原型而已,不是介面也不是類,不能用來繼承:
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 ) }
在这个原型里面定义的方法,根据自己需要去定义,并不要求全部实现,这就是为什么不定义成接口的原因,因为有些实现根本用不着某些方法,
这带来很多灵活性,比如包装器是不支持删除目录rmdir功能的,那么就不需要实现streamWrapper::rmdir
由于未实现它,如果用户在包装器上调用rmdir将有错误抛出,要自定义这个错误那么也可以实现它并在其内部抛出错误
streamWrapper也不是一个预定义类,测试class_exists("streamWrapper")就知道,它只是一个指导开发者的原型
官方手册提供了一个例子:http://php.net/manual/zh/stream.streamwrapper.example-1.php
本博客提供一个从drupal8系统中抽取修改过的包装器例子,请看drupal8源码分析关于流那一部分
流系列函数,官方手册:http://php.net/manual/zh/ref.stream.php
常用的函数如下:
stream_bucket_append函数:为队列添加数据 stream_bucket_make_writeable函数:从操作的队列中返回一个数据对象 stream_bucket_new函数:为当前队列创建一个新的数据 stream_bucket_prepend函数:预备数据到队列 stream_context_create函数:创建数据流上下文 stream_context_get_default函数:获取默认的数据流上下文 stream_context_get_options函数:获取数据流的设置 stream_context_set_option函数:对数据流、数据包或者上下文进行设置 stream_context_set_params函数:为数据流、数据包或者上下文设置参数 stream_copy_to_stream函数:在数据流之间进行复制操作 stream_filter_append函数:为数据流添加过滤器 stream_filter_prepend函数:为数据流预备添加过滤器 stream_filter_register函数:注册一个数据流的过滤器并作为PHP类执行 stream_filter_remove函数:从一个数据流中移除过滤器 stream_get_contents函数:读取数据流中的剩余数据到字符串 stream_get_filters函数:返回已经注册的数据流过滤器列表 stream_get_line函数:按照给定的定界符从数据流资源中获取行 stream_get_meta_data函数:从封装协议文件指针中获取报头/元数据 stream_get_transports函数:返回注册的Socket传输列表 stream_get_wrappers函数:返回注册的数据流列表 stream_register_wrapper函数:注册一个用PHP类实现的URL封装协议 stream_select函数:接收数据流数组并等待它们状态的改变 stream_set_blocking函数:将一个数据流设置为堵塞或者非堵塞状态 stream_set_timeout函数:对数据流进行超时设置 stream_set_write_buffer函数:为数据流设置缓冲区 stream_socket_accept函数:接受由函数stream_ socket_server()创建的Socket连接 stream_socket_client函数:打开网络或者UNIX主机的Socket连接 stream_socket_enable_crypto函数:为一个已经连接的Socket打开或者关闭数据加密 stream_socket_get_name函数:获取本地或者网络Socket的名称 stream_socket_pair函数:创建两个无区别的Socket数据流连接 stream_socket_recvfrom函数:从Socket获取数据,不管其连接与否 stream_socket_sendto函数:向Socket发送数据,不管其连接与否 stream_socket_server函数:创建一个网络或者UNIX Socket服务端 stream_wrapper_restore函数:恢复一个事先注销的数据包 stream_wrapper_unregister函数:注销一个URL地址包
相信看了这些案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
相关阅读:
以上是PHP的Streams以及包裝器wrapper如何使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!