PHPのstream wrapperをさわってみた
PHPには、ストリームラッパーというファイルなどへのアクセスを抽象化するしくみがあります。普段よく使う http:// とかがそうです。
組み込みで提供されるストリームラッパーは以下です。
PHP: Supported Protocols and Wrappers - Manual
それぞれどういうふうに使うものなのか試してみました。
ファイルシステム
- /path/to/file.ext
- relative/path/to/file.ext
- fileInCwd.ext
- C:/path/to/winfile.ext
- C:\path\to\winfile.ext
- \\smbserver\share\path\to\winfile.ext
- file:///path/to/file.ext
PHP で使用されるデフォルトのラッパーは Filesystem で、 これはローカルファイルシステムを表します。
PHP: file:// - Manual
デフォルトで利用されているラッパーということで、何も無い場合はこれになるわけですが、明示的に指定する場合には file://
となります。
HTTP と HTTPS
- http://example.com
- http://example.com/file.php?var1=val1&var2=val2
- http://user:password@example.com
- https://example.com
- https://example.com/file.php?var1=val1&var2=val2
- https://user:password@example.com
HTTP 1.0 により HTTP GET メソッドを用いてファイル/リソースに読み込みのみの アクセスが可能です。
PHP: http:// - Manual
これもよく使うHTTPとHTTPS。
ちなみにPOSTを利用したい場合や、ヘッダをつけてリクエストしたい場合は、fopen や file_get_contents といった関数には、contextというオプション引数があり、そこに stream_context_create関数で、メソッドやヘッダを指定することで、PEARやcurlを利用することなく対応できます。
このストリームは読み込みのみに対応しているため、例えば、file_put_contentsでファイルを作成したりすることはできません。
FTP と FTPS
FTPクライアントでファイルの受け渡しをすることはあるものの、PHPで使いたいと思ったことはないですね。
PHP: ftp:// - Manual
- ftp://example.com/pub/file.txt
- ftp://user:password@example.com/pub/file.txt
- ftps://example.com/pub/file.txt
- ftps://user:password@example.com/pub/file.txt
ファイルの内容を取得する場合はこんな感じ。
<?php echo file_get_contents("ftp://user:pass@ftp.example.jp/home/user/tmp.php");
読み込みだけでなく、書き込み・追記が可能です。
追記は、こんな感じで (FILE_APPENDで追記)
<?php file_put_contents("ftp://user:pass@ftp.example.jp/home/user/tmp.php", "<?php phpinfo();", FILE_APPEND);'
上書きはcontextの指定が必要です。追記は普通にできるのになんだか微妙。
<?php $context = stream_context_create(array('ftp' => array('overwrite' => true))); file_put_contents("ftp://user:pass@ftp.example.jp/home/user/tmp.php", "<?php phpinfo();", 0, $context);
ディレクトリの走査も可能です。
<?php $d = dir("ftp://user:pass@example.jp/home/user"); while (false !== ($entry = $d->read())) { echo $entry . PHP_EOL; } $d->close();
PHP 入出力ストリーム
これは何かのプロトコルのラッパーというわけではなく、PHP入出力関連をまとめたものです。
PHP: php:// - Manual
- php://stdin
- php://stdout
- php://stderr
- php://output
- php://input
- php://filter (PHP 5.0.0 以降で使用可能)
- php://memory (PHP 5.1.0 以降で使用可能)
- php://temp (PHP 5.1.0 以降で使用可能)
上から5つは、あんまり利用する用途が無い感じですが、例えば、入出力の指定をstreamで受けるように実装することで抽象化して、取替可能にするとかはありかもしれません。
php://stdin、php://stdout および php://stderr
それぞれ、標準入力、標準出力、標準エラー出力に対応します。STDIN、STDOUT、STDERR という定数で、オープン済みのハンドルとしてアクセスできるものと同等です。
<?php $fp = fopen('php://stdin', 'r'); while (!feof($fp)) { echo fgets($fp); }
は以下と等価です。
<?php while (!feof(STDIN)) { echo fgets(STDIN); }
php://output
print() および echo() と同じ方法での出力バッファへの書き込み。ということで、あんまり意味ない感じ。
php://input
php://input は、POST の生データの読み込みを 許可します。これは $HTTP_RAW_POST_DATA に比べて メモリ消費量が少なく、特別な php.ini ディレクティブを設定する 必要がありません。php://input は、 enctype="multipart/form-data" に対しては 使用できません。
ということで、POSTの生データを参照する際に推奨されています。
<html> <body> <form method="POST"> <input name="first" type="text" /> <input name="second" type="text" /> <input type="submit" /> </form> <?php var_dump(file_get_contents("php://input")); ?> </body> </html>
php://filter
php://filter は、フィルタアプリケーションが ストリームをオープンすることを許可するために設計されたメタラッパーです。 これは、readfile()、file() および file_get_contents() のようなオールインワンのファイル関数とともに使用すると有用です。これらの関数には、コンテンツが 読み込まれる前にストリームにフィルタを適用する手段がありません。
メタラッパーというなんだか大げさな名前ですが、ストリームにフィルタをかませることができます。
例えば、読み込みにstring.strip_tags フィルタをかませて、タグを抜く場合、このようになります。
<?php echo file_get_contents("php://filter/read=string.strip_tags/resource=http://www.yahoo.co.jp");
逆にfopenなど、オールインワンで無い場合は、stream_filter_append や stream_filter_prepend で、フィルタを適用できます。stream_filter_appendの例を参照してください。
ストリームフィルタで何が使えるかは、stream_get_filters関数で知ることができます。もちろんフィルタは自作することができます。
参考
php://memory、php://temp
php://memory ラッパーは、データをメモリに保存します。
<?php $fp = fopen("php://memory", 'r+'); fputs($fp, "hello!"); rewind($fp); echo stream_get_contents($fp);
stream_get_contents() は、file_get_contentsと同じようなもので、引数にファイル名でなくオープン済みのストリーム (ファイルハンドル)を渡すところが違います。
php://temp も同様にメモリに保存しますが、メモリの制限(デフォルトは 2 MB)を超過した際にはテンポラリファイルを使用します。制限値を変更する場合は、/maxmemory:1000 とバイト単位で指定します。
<?php $fiveMBs = 5 * 1024 * 1024; $fp = fopen("php://temp/maxmemory:$fiveMBs", 'r+'); fputs($fp, "hello\n"); // 先ほど書き込んだデータを読み込みます。 rewind($fp); echo stream_get_contents($fp); ?>
参考
圧縮ストリーム
- compress.zlib://
- compress.bzip2://
compress.zlib:// および compress.bzip2:// は、それぞれ gzopen() および bzopen() と等価です。
PHP: zlib:// - Manual
ということで、これもあんまり意味の無い感じ。gzopen()、bzopen()は、fopen()の圧縮ファイル版という感じのもので、それぞれ gzip、bzipファイルを開いたファイルハンドルを返却します。
<?php $fp = fopen("compress.zlib:///tmp/file.gz", "r"); ?>
は、以下と等価
<?php $fp = gzopen("/tmp/file.gz", "r"); ?>
データ (RFC 2397)
いわゆるdata URLスキームに対応するものです。たとえば、画像データなんかを
<img src="data:image/gif;base64,R0lGODlhEAAQAPcAAIZcD..."/>
とHTML内で完結して読み込ませることができるあれです。
このように、デコードして表示できます。(読み込みのみで、書き出しはサポートしていません)
<?php // "I love PHP" と表示します echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo='); ?>
デコードしてbodyだけ表示するならあんまり意味が無いですが、以下のようにしてmediatypeを取得できます。
<?php $fp = fopen('data://text/plain;base64,', 'r'); $meta = stream_get_meta_data($fp); // "text/plain" と表示します echo $meta['mediatype']; ?>
ちなみに、data:// で始まるのはPHPのストリームラッパーの仕様で、RFCでは、data:text/plain... と // は含みません。
マニュアルには記載が無く、User Contributed Notesで補足されていますが、 // を抜いた状態でも正常に動作するように実装されています。
Glob
PHP5.3から利用可能です。globというのは、あのglob関数と同じようなものです。
これは、fopenやfile_get_contentsでは利用できず、dir() や SPL の DirectoryIterator などのディレクトリ走査系で利用可能となっています。
PHPドキュメントの例は、DirectoryIteratorで利用しているものです。
<?php // ext/spl/examples/ ディレクトリのすべての *.php ファイルについて、 // そのファイル名とサイズを表示します $it = new DirectoryIterator("glob://ext/spl/examples/*.php"); foreach($it as $f) { printf("%s: %.1FK\n", $f->getFilename(), $f->getSize()/1024); } ?>
dir()の場合
<?php $d = dir("glob://*.php"); while (false !== ($entry = $d->read())) { echo $entry . PHP_EOL; } $d->close();
Phar
PHP 5.3 からもしくは PECL の phar を導入時に有効です。
Pharは、PHP Archiveの略で、phar形式に固められたアーカイブを参照する際に phar:// を利用します。
Phar形式のアーカイブに限らず、tar gzip、bzipのアーカイブから直接、中のファイルを参照することができます。仕組みについての詳しいことは別に書きます。
アーカイブをそのまま指定すると、Pharのスタブが読み込まれます。
<?php include 'phar://hoge.phar'; ?>
デフォルトのラッパーはファイルだと一番上で書きましたが、ファイル名に.pharが含まれる場合は、pharとして扱われます。なので以下のように、phar://を省略できます。
<?php include 'hoge.phar'; ?>
アーカイブ内のファイルも参照できます。
<?php echo file_get_contents("phar://foo.phar.bz/dir/bar.txt"); ?>