Stream is one of the most overlooked function series (SPL series, Stream series, pack function, packaging protocol) in PHP development, but it is a very useful and important function. Stream can be translated as "stream". In Java, stream is a very important concept.
The concept of stream originates from the concept of pipe in UNIX. In UNIX, a pipe is an uninterrupted byte stream, used to implement communication between programs or processes, or to read and write peripheral devices, external files, etc. According to the direction of the stream, it can be divided into input stream and output stream. At the same time, other streams can be placed around it, such as buffer stream, so that more stream processing methods can be obtained.
Streams in PHP and streams in Java are actually the same concept, just a little simpler. Since PHP is mainly used for web development, the concept of "flow" is rarely mentioned. If you have a Java foundation, it will be easier to understand streams in PHP. In fact, many advanced features in PHP, such as SPL, exceptions, filters, etc., all refer to the implementation of Java and have the same concepts and principles.
For example, the following is a usage of the PHP SPL standard library (traverse the directory and find files with fixed conditions):
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; }
There is also the same code in 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()); } } }
Give this example, on the one hand, it illustrates PHP The concept is the same as Java in many aspects. Mastering one language will be of great help to understanding another language; on the other hand, this example also helps with the filter flow-filter we will mention below. In fact, it is also a manifestation of a design pattern.
We can first understand the use of stream series functions through a few examples.
The following is an example of using socket to capture data:
$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);
We can also use stream_socket to implement it. This is very simple. You only need to open the socket code and change it to the following:
$fp = stream_socket_client("tcp://nowamagic.net:80", $errno, $errstr, 3);
Let’s look at a stream example again. :
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);
Note that the third parameter, $context, is the HTTP stream context, which can be understood as a pipe attached to the file_get_contents function. In the same way, we can also create FTP streams and socket streams and set them in the corresponding functions.
For more information about stream_context_create, please refer to: PHP function completion: stream_context_create() simulates POST/GET.
The two stream series functions mentioned above are wrapper-like streams that act on the input and output streams of a certain protocol. This kind of usage and concept is actually not much different from streams in Java. For example, Java often writes like this:
new DataOutputStream(new BufferedOutputStream(new FileOutputStream(new File(fileName))));
One layer of flow is nested in another layer of flow, which is similar to that in PHP.
Let’s take a look at the role of the filter stream:
$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
In the above example, if we set the filter type to STREAM_FILTER_ALL, which acts on the read and write streams at the same time, then all the read and written data will be rot13 After filter processing, the data we read is consistent with the original data written.
You may be surprised that the variable "string.rot13" in stream_filter_append comes from nowhere. This is actually a filter built into PHP.
Use the following method to print out the PHP built-in stream:
$streamlist = stream_get_filters(); print_r($streamlist);
Output:
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.* )
Naturally, we will think of defining our own filter, which is not difficult:
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");
Note: The filter name can be chosen at will .
After that, you can use our custom filter "string.md5".
The way this filter is written seems a bit confusing. In fact, we only need to look at the structure and built-in methods of the php_user_filter class to understand.
The most suitable thing for filter stream is file format conversion, including compression, encoding and decoding, etc. In addition to these "deviant" usages, one of the more useful aspects of filter stream is debugging and logging functions, such as in socket development. , register a filter stream for log recording. For example, the following example:
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"); };
This filter can not only process the input stream, but also callback a function for logging.
Can be used like this:
stream_filter_prepend($fp, "string.md5", STREAM_FILTER_WRITE,$callback);
There is also a very important stream in the stream series of functions in PHP, which is the wrapper class stream streamWrapper. Using wrapper streams allows different types of protocols to use the same interface to manipulate data.