스트림의 개념은 UNIX의 파이프 개념에서 유래되었습니다. 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의 개념이 여러 측면에서 동일하다는 것을 알 수 있습니다. 한 언어를 익히는 것은 다른 언어를 이해하는 데에도 큰 도움이 됩니다. us 아래에서 언급할 필터 스트림은 -filter입니다. 사실 이는 디자인 패턴의 발현이기도 하다.
먼저 몇 가지 예를 통해 스트림 계열 함수의 사용법을 이해할 수 있습니다.
다음은 소켓을 사용하여 데이터를 캡처하는 예입니다.
$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을 사용할 수도 있습니다. 소켓을 여는 코드를 다음과 같이 변경하기만 하면 됩니다.
스트림의 또 다른 예를 살펴보겠습니다.
file_get_contents 함수는 일반적으로 파일 내용을 읽는 데 사용되지만, 이 함수는 컬과 비슷한 역할을 하며 원격 URL을 가져오는 데에도 사용할 수 있습니다.
$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는 다음과 같이 작성하는 경우가 많습니다.
한 흐름 레이어는 다른 흐름 레이어 내에 중첩되어 있으며 이는 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 클래스의 구조와 내장 메소드만 보면 됩니다.
필터 스트림에 가장 적합한 것은 압축, 인코딩 및 디코딩 등을 포함한 파일 형식 변환입니다. 이러한 "일탈적인" 사용법 외에도 필터 스트림의 더 유용한 측면 중 하나는 다음과 같은 디버깅 및 로깅 기능입니다. in 소켓 개발 중에 로그 기록을 위한 필터 스트림을 등록합니다. 예를 들면 다음과 같습니다.
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"); };
이 필터는 입력 스트림을 처리할 수 있을 뿐만 아니라 로깅을 위한 함수를 콜백할 수도 있습니다.
다음과 같이 사용할 수 있습니다.
PHP의 스트림 계열 함수에는 래퍼 클래스 스트림 streamWrapper라는 매우 중요한 스트림도 있습니다. 래퍼 스트림을 사용하면 다양한 유형의 프로토콜이 동일한 인터페이스를 사용하여 데이터를 조작할 수 있습니다. 이에 대해서는 나중에 이야기합시다.
위 내용은 이 글의 전체 내용입니다. 모두 마음에 드셨으면 좋겠습니다.