Stream은 PHP 개발에서 가장 간과되는 기능 시리즈(SPL 시리즈, Stream 시리즈, 팩 기능, 패키징 프로토콜) 중 하나이지만 매우 유용하고 중요한 기능입니다. 스트림은 "스트림"으로 번역될 수 있습니다. Java에서 스트림은 매우 중요한 개념입니다.
스트림 개념은 유닉스의 파이프 개념에서 유래됐다. UNIX에서 파이프는 프로그램이나 프로세스 간의 통신을 구현하거나 주변 장치, 외부 파일 등을 읽고 쓰는 데 사용되는 중단 없는 바이트 스트림입니다. 스트림의 방향에 따라 입력 스트림과 출력 스트림으로 나눌 수 있으며 동시에 버퍼 스트림과 같은 다른 스트림을 배치할 수 있으므로 더 많은 스트림 처리 방법을 얻을 수 있습니다.
PHP의 스트림과 Java의 스트림은 실제로는 동일한 개념이지만 조금 더 간단합니다. PHP는 주로 웹 개발에 사용되기 때문에 "흐름"이라는 개념은 거의 언급되지 않습니다. Java 기초가 있으면 PHP의 스트림을 이해하는 것이 더 쉬울 것입니다. 실제로 SPL, 예외, 필터 등과 같은 PHP의 많은 고급 기능은 모두 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의 개념이 여러 측면에서 동일하다는 것을 보여줍니다. 한 언어를 익히는 것이 다른 언어를 이해하는 데 큰 도움이 됩니다. 반면에 이 예는 아래에서 언급할 필터 흐름 필터에도 도움이 됩니다. 사실 이는 디자인 패턴의 발현이기도 하다.
먼저 몇 가지 예를 통해 스트림 계열 함수의 사용법을 이해할 수 있습니다.
다음은 소켓을 사용하여 데이터를 캡처하는 예입니다.
$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을 사용할 수도 있습니다. 소켓을 여는 코드만 변경하면 됩니다. 다음은
$fp = stream_socket_client("tcp://nowamagic.net:80", $errno, $errstr, 3);
스트림 예제를 다시 살펴보겠습니다.
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 스트림과 소켓 스트림을 생성하고 해당 기능에 설정할 수도 있습니다.
stream_context_create에 대한 자세한 내용은 다음을 참조하세요. PHP 함수 완성: stream_context_create()는 POST/GET을 시뮬레이션합니다.
위에 언급된 두 가지 스트림 계열 함수는 특정 프로토콜의 입력 및 출력 스트림에 대해 작동하는 래퍼형 스트림입니다. 이러한 종류의 사용법과 개념은 실제로 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 클래스의 구조와 내장 메소드만 보면 됩니다.
필터 스트림이 가장 적합한 것은 압축, 인코딩, 디코딩 등을 포함한 파일 형식 변환입니다. 이러한 "일탈적인" 용도 외에도 필터 스트림의 더 유용한 측면 중 하나는 디버깅 및 소켓 개발에서는 로그 기록을 위해 필터 스트림을 등록한다고 합니다. 예를 들어, 다음 예는 다음과 같습니다.
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의 스트림 함수 시리즈에는 래퍼 클래스 스트림 streamWrapper라는 매우 중요한 스트림도 있습니다. 래퍼 스트림을 사용하면 다양한 유형의 프로토콜이 동일한 인터페이스를 사용하여 데이터를 조작할 수 있습니다.