原副标题:PHP 标准化天然资源处置 API —— 流(Stream)的简述与采用简述
在现代PHP 优点中,流或许是为数不多但采用率最低的。尽管 PHP 4.3 就导入了流,但许多开发人员无人知晓流的存在,因为人们很少提到流,而且流的文件格式也很贫乏。PHP 非官方文件格式环流的说明如下表所示:
流的促进作用是提供更多标准化的公用表达式来处置文件格式、网络和统计压缩算法等操作。简单而言,内龙具有INS13ZD行为的天然资源对象,换句话说,流能非线性随机存取,并且能透过 fseek() 等等的表达式功能定位到过程中将的任何人位置。
可能看完那段说明后还是称奇,他们精简一下,流的促进作用是在著陆点和出发地之间传输统计数据。著陆点和出发地能是文件格式、配置文件进程、数据传输、ZIP 或 TAR 剪贴板、临时缓存、标准输入或输入,或是是透过 PHP 流PCB协定同时实现的任何人其他天然资源。
假如你随机存取过文件格式,就用过载;假如你从 php://stdin 加载过统计数据,或是把输入载入 php://stdout ,也用过载。流为 PHP 的许多 IO 表达式提供更多了下层同时实现,如 file_get_contents 、 fopn、 fread 和 fwrite 等。PHP 的流表达式提供更多了不同天然资源的标准化USB。
他们能把流想像成管线,把水(天然资源统计数据)从两个地方性拉到另两个地方性。在水从著陆点到出发地的操作过程中,他们能过滤器水,能改变水体,能加进水,也能排泄水。
流PCB协定
INS13ZD统计数据的种类各有不同,五种类型需要独特的协定,以期随机存取统计数据,他们称那些协定为 流PCB协定 。例如,他们能随机存取文件格式系统,能透过 HTTP、HTTPS 或 SSH 与远距 Web 伺服器通讯,还能打开并随机存取 ZIP、RAR 或 PHAR 剪贴板。那些通讯形式都包含前述相同的操作过程:
开始通讯 加载统计数据 载入统计数据 结束通讯尽管操作过程是一样的,但随机存取文件格式系统中文件格式的形式与递送 HTTP 消息的形式略有不同,流PCB协定的促进作用是采用通用型的USBPCB这种差异。
每个流都有两个协定和两个最终目标。选定协定和最终目标的方法是采用流URL: <scheme>://<target> ,其中 <scheme> 是流的PCB协定, <target> 是流的统计数据源。
http://流PCB协定
下面采用 HTTP 流PCB协定建立了两个与 Flicker API 通讯的 PHP 流:
<?php
$json = file_get_contents(
http://api.flickr.com/services/feeds/photos_public.gne?format=json);
不要以为这是普通的网页 URL, file_get_contents() 表达式的字符串参数其实是两个流URL。 http 协定会让 PHP 采用 HTTP 流PCB协定,在这个参数中, http 之后是流的最终目标。
注:许多 PHP 开发人员可能无人知晓普通的 URL 其实是 PHP 流PCB协定URL的伪装。
file://流PCB协定
他们通常采用 file_get_contents() 、 fopen() 、 fwrite() 和 fclose() 等表达式随机存取文件格式系统,因为 PHP 默认采用的流PCB协定是 file:// ,所以他们很少认为那些表达式采用的是 PHP 流。下面的示例演示了采用 file:// 流PCB协定建立两个随机存取 /etc/hosts 文件格式的流:
$handle = fopen(file:///etc/hosts, rb);
while(feof($handle) !== TRUE) {
echofgets($handle);}fclose($handle);
他们通常会省略掉 file:// 协定,因为这是 PHP 采用的默认值。
php://流PCB协定
编写配置文件脚本的 PHP 开发人员会感激 php:// 流PCB协定,这个流PCB协定的促进作用是与 PHP 脚本的标准输入、标准输入和标准错误文件格式描述符通讯。他们能采用 PHP 提供更多的文件格式系统表达式打开、加载或载入下面四个流:
php://stdin :这是个只读 PHP 流,其中的统计数据来自标准输入。PHP 脚本能采用这个流接收配置文件传入脚本的信息; php://stdout :把统计数据载入当前的输入缓冲区,这个流只能写,无法读或寻址; php://memory :从系统缓存中加载统计数据,或是把统计数据载入系统缓存。缺点是系统缓存有限,所有采用 php://temp 更安全; php://temp :和 php://memory 类似,不过,没有可用缓存时,PHP 会把统计数据载入这个临时文件。 其他流PCB协定PHP 和 PHP 扩展还提供更多了许多其他流PCB协定,例如,与 ZIP 和 TAR 剪贴板、FTP 伺服器、统计压缩算法库、Amazon API、Dropbox API 等通讯的流PCB协定。需要注意的是,PHP 中的fopen() 、 fgets() 、 fputs() 、 feof() 以及 fclose() 等表达式不仅能用来处置文件格式系统中的文件格式,还能在所有支持那些函数的流PCB协定中采用。
注:更多流PCB协定,请参考非官方网站: http://php.net/manual/zh/wrappers.php
自定义流PCB协定
他们还能自己编写 PHP 流PCB协定。PHP 提供了两个示例 StreamWrapper 类,演示如何编写自定义的流PCB协定,支持部分或全部 PHP 文件格式系统表达式。关于如何编写,具体请参考以下文件格式:
http://php.net/manual/zh/class.streamwrapper.php http://php.net/manual/zh/stream.streamwrapper.example-1.php 流上下文有些 PHP 流能够接受一系列可选的参数,那些参数叫流上下文,用于定制流的行为。不同的流PCB协定采用的流上下文略有不同,流上下文采用 stream_context_create() 表达式建立,这个表达式返回的上下文对象能传入大多数文件格式系统表达式。
例如,你知道能采用 file_get_contents() 发送 HTTP POST 请求吗?采用两个流上下文对象即可同时实现:
$requestBody = {“username”:”nonfu”};$context = stream_context_create([
http=> [
method=> POST,
header=> “Content-Type: application/json;charset=utf-8;
rnContent-Length: “. mb_strlen($requestBody),
content=> $requestBody ]]);$response = file_get_contents(https://my-api.com/users,
false, $context);
流上下文是个关联数组,最外层键是流PCB协定的名称,流上下文数组中的值针对不同的流PCB协定略有不同,可用的设置参考各个 PHP 流PCB协定的文件格式。
流 过滤器器
目前为止他们讨论了如何打开流,加载过程中将的统计数据,以及把统计数据载入流。不过,PHP 流真正强大的地方性在于过滤器、转换、加进或删除过程中将传输的统计数据,例如,他们能打开两个流处置 Markdown 文件格式,在把文件格式内容读入缓存的操作过程中自动将其转化为 HTML。
注:PHP 所有可用流过滤器器请参考非官方文件格式: http://php.net/manual/zh/filters.php 。
若想把过滤器器附加到现有的流上,要采用 stream_filter_append() 表达式,下面他们以 string.toupper 过滤器器演示如何把文件格式中的内容转换成大写字母:
$handle = fopen( test.txt, rb);stream_filter_append($handle, string.toupper);
while(feof($handle) !==true) { echo fgets($handle);}fclose($handle);
运行该脚本,输入的都是大写字母:
ABCDEEFGHIJKLMNHELLO LARAVELACADEMY!
他们还能采用 php://filter 流PCB协定把过滤器器附加到流上,不过,采用这种形式之前必须先打开 PHP 流:
$handle = fopen( php://filter/read=
string.toupper/resource=test.txt, rb);
while(feof($handle) !== true) { echo fgets($handle);}fclose($handle);
这个形式同时实现效果和 stream_filter_append() 表达式一样,但相比之下更为繁琐。不过,PHP 的某些文件格式系统表达式在调用后无法附加过滤器器,例如 file() 和 fpassthru() ,采用那些表达式时只能采用 php://filter 流PCB协定附加流过滤器器。
自定义流过滤器器
他们还能编写自定义的流过滤器器。其实,大多数情况下都要采用自定义的流过滤器器,自定义的流过滤器器是个 PHP 类,继承内置的 php_user_filter 类( http://php.net/manual/zh/class.php-user-filter.php ),且必须同时实现 filter() 、 onCreate() 和 onClose() 方法,最后,必须采用 stream_filter_register() 表达式注册自定义的流过滤器器。
注:PHP 流会把统计数据分成按次序排列的桶,两个桶中盛放的流统计数据是固定的(如 4096 字节),假如还用管线比喻,就是把水放在两个个水桶中,顺着管线从著陆点漂流到出发地,在漂流操作过程中会经过过滤器器,过滤器器一次能接收并处置两个或多个桶,一定时间内过滤器器接收到的桶叫做桶队列。桶队列中的每个桶对象都有两个公用属性: data 和 datalen ,分别表示桶的内容和长度。
下面他们自定义两个流过滤器器 DirtyWordsFilter ,把流统计数据读入缓存时审查其中的脏字:
<?php
classDirtyWordsFilterextendsphp_user_filter{
/** * @paramresource $in 流入的桶队列 * @paramresource $out 流出的桶队列 *@paramint $consumed 处置的字节数 * @parambool $closing 是否是过程中将最后两个桶队列 * @returnint * 接收、处置再转运桶中的流统计数据,在该方法中,
他们迭代桶队列对象,把脏字替换成审查后的值 */publicfunctionfilter($in, $out, &$consumed, $closing){ $words = [grime, dirt, grease]; $wordData = [];
foreach($words as$word) { $replacement = array_fill(0, mb_strlen($word),*); $wordData[$word] = implode(, $replacement); } $bad = array_keys($wordData); $good = array_values($wordData);
// 迭代桶队列中的每个桶while($bucket = stream_bucket_make_writeable($in)) {
// 审查桶对象中的脏字$bucket->data = str_replace($bad, $good, $bucket->data);
// 增加已处置的统计数据量$consumed += $bucket->datalen;
// 把桶放入流向下游的队列中stream_bucket_append($out, $bucket); }
returnPSFS_PASS_ON; }}
然后,他们必须采用 stream_filter_register() 表达式注册这个自定义的 DirtyWordsFilter 流过滤器器:
stream_filter_register( dirty_words_filter, DirtyWordsFilter);
第两个参数用于标识这个自定义过滤器器的过滤器器名,第二个参数是这个自定义过滤器器的类名。接下来就能采用这个自定义的流过滤器器了:
$handle = fopen( test.txt, rb);stream_filter_append($handle,dirty_words_filter);
while(feof($handle) !== true) { echo fgets($handle);}fclose($handle);
修改 test.txt 内容如下表所示:
abcdeefghijklmnHello LaravelAcademy!grimeI hate dirty things!
运行上面的自定义过滤器器脚本,结果如下表所示:
abcdeefghijklmnHello LaravelAcademy! *****I hate ****y things!