首页 后端开发 php教程 浅谈PHP中Stream(流)_php技巧

浅谈PHP中Stream(流)_php技巧

May 16, 2016 pm 08:14 PM
php stream

流(stream)的概念源于UNIX中管道(pipe)的概念。在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。根据流的方向又可以分为输入流和输出流,同时可以在其外围再套上其它流,比如缓冲流,这样就可以得到更多流处理方法。

PHP里的流和Java里的流实际上是同一个概念,只是简单了一点。由于PHP主要用于Web开发,所以“流”这块的概念被提到的较少。如果有Java基础,对于PHP里的流就更容易理解了。其实PHP里的许多高级特性,比如SPL,异常,过滤器等都参考了Java的实现,在理念和原理上同出一辙。

比如下面是一段PHP SPL标准库的用法(遍历目录,查找固定条件的文件):

class RecursiveFileFilterIterator extends FilterIterator
{
 // 满足条件的扩展名
 protected $ext = array('jpg','gif');
 /**
  * 提供 $path 并生成对应的目录迭代器
  */
 public function __construct($path)
 {
   parent::__construct(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)));
 }
 /**
  * 检查文件扩展名是否满足条件
  */
 public function accept()
 {
   $item = $this->getInnerIterator();
   if ($item->isFile() && in_array(pathinfo($item->getFilename(), PATHINFO_EXTENSION), $this->ext))
   {
     return TRUE;
   }
 }
}
// 实例化
foreach (new RecursiveFileFilterIterator('D:/history') as $item)
{
 echo $item . PHP_EOL;
}
登录后复制

Java里也有和其同出一辙的代码:

public class DirectoryContents
{
 public static void main(String[] args) throws IOException
 {
   File f = new File("."); // current directory
   FilenameFilter textFilter = new FilenameFilter()
   {
     public boolean accept(File dir, String name)
     {
       String lowercaseName = name.toLowerCase();
       if (lowercaseName.endsWith(".txt"))
       {
         return true;
       }
       else
       {
         return false;
       }
     }
   };
   File[] files = f.listFiles(textFilter);
   for (File file : files)
   {
     if (file.isDirectory())
     {
       System.out.print("directory:");
     }
     else
     {
       System.out.print("   file:");
     }
     System.out.println(file.getCanonicalPath());
   }
 }
}
登录后复制

举这个例子,一方面是说明PHP和Java在很多方面的概念是一样的,掌握一种语言对理解另外一门语言会有很大的帮助;另一方面,这个例子也有助于我们下面要提到的过滤器流-filter。其实也是一种设计模式的体现。

我们可以通过几个例子先来了解stream系列函数的使用。

下面是一个使用socket来抓取数据的例子:

$post_ =array (
 'author' => 'Gonn',
 'mail'=>'gonn@nowamagic.net',
 'url'=>'http://www.nowamagic.net/',
 'text'=>'欢迎访问简明现代魔法');
$data=http_build_query($post_);
$fp = fsockopen("nowamagic.net", 80, $errno, $errstr, 5);
$out="POST http://nowamagic.net/news/1/comment HTTP/1.1\r\n";
$out.="Host: typecho.org\r\n";
$out.="User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13"."\r\n";
$out.="Content-type: application/x-www-form-urlencoded\r\n";
$out.="PHPSESSID=082b0cc33cc7e6df1f87502c456c3eb0\r\n";
$out.="Content-Length: " . strlen($data) . "\r\n";
$out.="Connection: close\r\n\r\n";
$out.=$data."\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp))
{
 echo fgets($fp, 1280);
}
fclose($fp);
登录后复制

我们也可以用stream_socket 实现,这很简单,只需要打开socket的代码换成下面的即可:

复制代码 代码如下:

$fp = stream_socket_client("tcp://nowamagic.net:80", $errno, $errstr, 3);

再来看一个stream的例子:

file_get_contents函数一般常用来读取文件内容,但这个函数也可以用来抓取远程url,起到和curl类似的作用。

$opts = array (
 'http'=>array(
   'method' => 'POST',
   'header'=> "Content-type: application/x-www-form-urlencoded\r\n" .
        "Content-Length: " . strlen($data) . "\r\n",
   'content' => $data)
);
$context = stream_context_create($opts);
file_get_contents('http://nowamagic.net/news/1/comment', false, $context);
登录后复制

注意第三个参数,$context,即HTTP流上下文,可以理解为套在file_get_contents函数上的一根管道。同理,我们还可以创建FTP流,socket流,并把其套在对应的函数在。

更多关于 stream_context_create,可以参考:PHP函数补完:stream_context_create()模拟POST/GET。

上面提到的两个stream系列的函数都是类似包装器的流,作用在某种协议的输入输出流上。这样的使用方式和概念,其实和Java中的流并没有大的区别,比如Java中经常有这样的写法:

复制代码 代码如下:

new DataOutputStream(new BufferedOutputStream(new FileOutputStream(new File(fileName))));

一层流嵌套着另外一层流,和PHP里有异曲同工之妙。

我们再来看个过滤器流的作用:

$fp = fopen('c:/test.txt', 'w+');
/* 把rot13过滤器作用在写入流上 */
stream_filter_append($fp, "string.rot13", STREAM_FILTER_WRITE);
/* 写入的数据经过rot13过滤器的处理*/
fwrite($fp, "This is a test\n");
rewind($fp);
/* 读取写入的数据,独到的自然是被处理过的字符了 */
fpassthru($fp);
fclose($fp);
// output:Guvf vf n grfg
登录后复制

在上面的例子中,如果我们把过滤器的类型设置为STREAM_FILTER_ALL,即同时作用在读写流上,那么读写的数据都将被rot13过滤器处理,我们读出的数据就和写入的原始数据是一致的。

你可能会奇怪stream_filter_append中的 "string.rot13"这个变量来的莫名其妙,这实际上是PHP内置的一个过滤器。

使用下面的方法即可打印出PHP内置的流:

$streamlist = stream_get_filters();
print_r($streamlist);
登录后复制

输出:

Array
(
 [0] => convert.iconv.*
 [1] => mcrypt.*
 [2] => mdecrypt.*
 [3] => string.rot13
 [4] => string.toupper
 [5] => string.tolower
 [6] => string.strip_tags
 [7] => convert.*
 [8] => consumed
 [9] => dechunk
 [10] => zlib.*
 [11] => bzip2.*
)
登录后复制

自然而然,我们会想到定义自己的过滤器,这个也不难:

class md5_filter extends php_user_filter
{
 function filter($in, $out, &$consumed, $closing)
 {
   while ($bucket = stream_bucket_make_writeable($in))
   {
     $bucket->data = md5($bucket->data);
     $consumed += $bucket->datalen;
     stream_bucket_append($out, $bucket);
   }
   //数据处理成功,可供其它管道读取
   return PSFS_PASS_ON;
 }
}
stream_filter_register("string.md5", "md5_filter");
登录后复制

注意:过滤器名可以随意取。

之后就可以使用"string.md5"这个我们自定义的过滤器了。

这个过滤器的写法看起来很是有点摸不着头脑,事实上我们只需要看一下php_user_filter这个类的结构和内置方法即了解了。

过滤器流最适合做的就是文件格式转换了,包括压缩,编解码等,除了这些“偏门”的用法外,filter流更有用的一个地方在于调试和日志功能,比如说在socket开发中,注册一个过滤器流进行log记录。比如下面的例子:

class md5_filter extends php_user_filter
{
 public function filter($in, $out, &$consumed, $closing)
 {
   $data="";
   while ($bucket = stream_bucket_make_writeable($in))
   {
     $bucket->data = md5($bucket->data);
     $consumed += $bucket->datalen;
     stream_bucket_append($out, $bucket);
   }
   call_user_func($this->params, $data);
   return PSFS_PASS_ON;
 }
}
$callback = function($data)
{
 file_put_contents("c:\log.txt",date("Y-m-d H:i")."\r\n");
};
登录后复制

这个过滤器不仅可以对输入流进行处理,还能回调一个函数来进行日志记录。

可以这么使用:

复制代码 代码如下:

stream_filter_prepend($fp, "string.md5", STREAM_FILTER_WRITE,$callback);

PHP中的stream流系列函数中还有一个很重要的流,就是包装类流 streamWrapper。使用包装流可以使得不同类型的协议使用相同的接口操纵数据。这个以后再说。

以上所述就是本文的全部内容了,希望大家能够喜欢。

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
两个点博物馆:所有展览以及在哪里可以找到它们
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

CakePHP 项目配置 CakePHP 项目配置 Sep 10, 2024 pm 05:25 PM

在本章中,我们将了解CakePHP中的环境变量、常规配置、数据库配置和电子邮件配置。

适用于 Ubuntu 和 Debian 的 PHP 8.4 安装和升级指南 适用于 Ubuntu 和 Debian 的 PHP 8.4 安装和升级指南 Dec 24, 2024 pm 04:42 PM

PHP 8.4 带来了多项新功能、安全性改进和性能改进,同时弃用和删除了大量功能。 本指南介绍了如何在 Ubuntu、Debian 或其衍生版本上安装 PHP 8.4 或升级到 PHP 8.4

CakePHP 日期和时间 CakePHP 日期和时间 Sep 10, 2024 pm 05:27 PM

为了在 cakephp4 中处理日期和时间,我们将使用可用的 FrozenTime 类。

CakePHP 文件上传 CakePHP 文件上传 Sep 10, 2024 pm 05:27 PM

为了进行文件上传,我们将使用表单助手。这是文件上传的示例。

CakePHP 路由 CakePHP 路由 Sep 10, 2024 pm 05:25 PM

在本章中,我们将学习以下与路由相关的主题?

讨论 CakePHP 讨论 CakePHP Sep 10, 2024 pm 05:28 PM

CakePHP 是 PHP 的开源框架。它的目的是使应用程序的开发、部署和维护变得更加容易。 CakePHP 基于类似 MVC 的架构,功能强大且易于掌握。模型、视图和控制器 gu

CakePHP 创建验证器 CakePHP 创建验证器 Sep 10, 2024 pm 05:26 PM

可以通过在控制器中添加以下两行来创建验证器。

如何设置 Visual Studio Code (VS Code) 进行 PHP 开发 如何设置 Visual Studio Code (VS Code) 进行 PHP 开发 Dec 20, 2024 am 11:31 AM

Visual Studio Code,也称为 VS Code,是一个免费的源代码编辑器 - 或集成开发环境 (IDE) - 可用于所有主要操作系统。 VS Code 拥有针对多种编程语言的大量扩展,可以轻松编写

See all articles