PHP 流
流是一种概括文件、网络、压缩资源和其他一些东西的方式,允许它们共享一组公共功能。Istream是一个具有可流式传输行为的资源对象。它可以以线性方式读取或写入,但不一定从流的开头开始。
流在PHP中可用已有一段时间(至少从4.3.0版开始)并且被大多数PHP程序员非常透明地使用。它们可用于访问文件、网络资源、命令行参数,以及几乎所有通过PHP输入/输出流的内容。
我最近在看ReactPHP,发现使用流是一个要求,以防止阻塞输入/输出流。虽然,我已经看到在PHP应用程序中使用流,但我自己并不完全确定如何使用它们。结果,我想我应该写一篇关于他们的帖子。
流在访问大文件时特别方便。假设我们需要访问一个大小为几百兆字节的日志文件。我们可以将文件读入内存,file_get_contents()然后遍历内容,直到找到我们需要的内容。这种方法的问题在于,我们很快就会耗尽系统资源,而在我们有机会访问数据之前,程序就会出错。
一个更好的解决方案是使用PHP流。我们可以使用该函数fopen()获取文件句柄,然后一次一点点地流式传输文件,从而减少读取文件所需的内存量。
$handle = fopen('logfile.log', 'r'); while (false !== ($line = fgets($handle))) { //做点什么。 }
这就是我上面提到的透明流用法。虽然我们没有明确使用任何流函数或语法,但我们仍然使用PHP流来读取文件的块。
流语法
您可以使用语法[scheme]://[target]直接访问流。方案是包装器的名称,目标是正在读取或写入的内容,这在很大程度上取决于正在读取的内容。PHP有许多不同的内置包装器、传输器和流过滤器。您可以通过以下三个功能了解它们的用途。
在我当前的系统上,这会输出这个。
Array ( [0] => tcp [1] => udp [2] => unix [3] => udg [4] => ssl [5] => tls [6] => tlsv1.0 [7] => tlsv1.1 [8] => tlsv1.2 ) Array ( [0] => https [1] => ftps [2] => compress.zlib [3] => compress.bzip2 [4] => php [5] => file [6] => glob [7] => data [8] => http [9] => ftp [10] => phar [11] => zip ) Array ( [0] => zlib.* [1] => bzip2.* [2] => convert.iconv.* [3] => string.rot13 [4] => string.toupper [5] => string.tolower [6] => string.strip_tags [7] => convert.* [8] => consumed [9] => dechunk )这提供了很多连接到各种流的选项。
php://流
内置的php://流可能是最通用的流包装器,也是了解如何使用流的良好起点。使用php://包装器的一个例子是stdin目标。这是一个只读流,它将从标准输入读取输入并将其创建为PHP中的流。然后我们可以通过fgets()函数读取流的内容。这一次读取一行,但对于我们正在做的事情来说很好。我们也可以 fread()用来做这个。
$stdin = fopen('php://stdin', 'r'); while (false !== ($line = fgets($stdin))) { echo $line; }可以通过将文件的输出通过管道传输到命令行上的脚本来使用此脚本。
catsomelog.log|phpstream.php
脚本的输出是打印在命令行上的文件内容,但此处概述了管道到PHP脚本的核心思想。
另一个例子是使用php://input读取脚本的输入。这是另一个只读流,可用于读取脚本的POST请求的原始正文。假设我们使用以下curl请求调用了一个脚本。
curl-XPOST-d"data1=thing"-d"data2=anotherthing""http://localhost:8000/stream.php"
我们可以使用$_POST超级全局变量访问POST数据。
print_r($_POST);
这将打印出以下内容。
Array ( [data1] => thing [data2] => anotherthing )通过使用php://input我们可以直接将POST数据的内容作为输入流读出。
$input = fopen('php://input', 'r'); while (false !== ($line = fgets($input))) { echo $line; }这将输出以下内容。
data1=thing&data2=anotherthing
php://memory和php://temp
PHP5.1引入了php://memory和php://temp,它们允许读写内存或临时文件。
php://memory的使用方式与php://input流大致相同,但在这种情况下,我们可以读取和写入它。这是打开内存流然后向其中写入一些数据的示例。
在这段代码中没有创建文件,我们只是在写入内存。我们也可以像通常使用文件和流一样从内存流中读取。请注意,您不能立即执行此操作,我们必须先将指针倒回到流的开头,然后才能读取流的内容。这可以使用该rewind()函数来完成。
//将流倒回到开头。 rewind($memoryStream); //从流中读取数据。 while (false !== ($line = fgets($memoryStream))) { echo $line; }php://temp流的作用类似于php://memory,但关键的区别在于,如果您写入的数据量超过一定数量(默认为2MB),则会创建一个文件来存储这些信息。此文件的位置取决于您的系统,但您可以使用该sys_get_temp_dir()功能找到此信息 。该文件将在PHP脚本结束时自动删除。
还可以控制在创建文件之前可以向php://temp流添加多少数据。这是通过附加“/maxmemory:n”来完成的,其中“n”是在使用临时文件之前保留在内存中的最大数据量,以字节为单位。
// Write to memory, or a file if more than 1028 bytes is written. $tempStream = fopen('php://temp/maxmemory:1028', 'rw+');有了这个,如果我们写入的数据超过1028字节,则会在临时目录中创建一个文件。
流式传输网页
您可能已经看到http和https可用作流包装器。这意味着我们可以将网页作为流资源打开并将其作为数据流式传输。这是一个实际的例子。
$stream = fopen('https://www.hashbangcode.com/', 'r'); echo fread($stream, 50);这将返回以下输出。
流上下文
流的另一个强大部分是创建上下文。这允许我们改变流的使用方式。例如,当我们使用该file_get_contents()函数访问URL时,它将始终使用GET请求来执行此操作。
echofile_get_contents("https://www.hashbangcode.com");
为了改变发出的请求的类型,我们创建了一个流上下文并将其传递给函数。流上下文是使用该stream_context_create()函数创建的,该函数采用一个数组,该数组详细说明了需要创建的上下文。
以下是创建所需上下文以创建POST请求(包括请求的内容)的示例,stream_context_create()然后使用file_get_contents().我正在使用https://postman-echo.com/post/来测试这个post请求,因为它会返回你发送的任何内容,所以这是测试一切正常的好方法。
$data = [ 'data1' => 'hello world', ]; $context = stream_context_create( [ 'http' => [ 'method' => 'POST', 'header' => [ 'Accept: application/json', 'Content-Type: application/x-www-form-urlencoded' ], 'content' => http_build_query($data), ], ] ); echo file_get_contents("https://postman-echo.com/post/", null, $context);运行此返回以下内容。
{"args":{},"data":"","files":{},"form":{"data1":"helloworld"},"headers":{"x-forwarded-proto":"https","host":"postman-echo.com","content-length":"17","accept":"application/json","content-type":"application/x-www-form-urlencoded","x-forwarded-port":"443"},"json":{"data1":"helloworld"},"url":"https://postman-echo.com/post/"}%
另一个使用流上下文的好例子是绕过PHP的严格SSL规则。假设我们试图连接到没有完全签名的SSL证书的本地服务器。我们可以创建一个流上下文,允许绕过SSL验证步骤。
$context = stream_context_create( [ 'ssl' => [ 'verify_peer' => false, 'verify_depth' => 5, 'allow_self_signed' => true, 'verify_peer_name' => false, ] ] ); echo file_get_contents("https://localhost", null, $context);结论
PHP中的流比我在这里详述的要多得多。PHP包含一系列函数,允许您以多种不同的方式与流交互。也可以创建自定义流包装器和过滤器,我可能会在以后的文章中详细介绍。