@blog.justoneplanet.info

日々勉強

PHP Accessing Network Resources(ネットワークアクセス)

■ファイルアクセスの同じ手法

以下のようにfopen関数を用いてファイルアクセスと同じように、ネットワークリソースを利用できる。但し、当然ながらhttpを使っているので書き込みなどは行えない。

<?php
$data = '';
if($fh = fopen('http://google.com', 'r')){
    while($str = fread($fh, 1000)){
        $data .= $str;
    }
}
else{
    //error
}
print($data);
?>

以下のようにして外部のファイルをインクルードすることもできる。但し、JavaScriptなどではお馴染みの手法もPHPでは大きなリスクを伴うことを理解する必要がある。

<?php
require_once('http://sample.org');
?>

■ストリームコンテキスト

以下のようにstream_context_create関数を使って、特定の関数を使用したときに任意の(HTTP)ヘッダを使うよう指定することができる。

<?php
$strmtxt = stream_context_create(array(
    'http' => array(
        'user_agent' => 'Super Browser Like FF',
        'max_redirects' => 3
    )
));
$data = file_get_contents('http://localhost', false, $strmtxt);
?>

各関数について

string file_get_contents(string $filename[, bool $use_include_path=false[, resource $context[, int $offset=-1[, int $maxlength=-1]]]])
$filenameのファイルを文字列として全て読み込む。$use_include_pathをtrueにするとインクルードパスから検索してくれる。第三引数にストリームコンテキストを用いて通信の挙動を指定できる。
resource stream_context_create([array $options])
ストリームコンテキストを生成する。引数は$array[‘wrapper’][‘option’] = $valueのような連想配列でなければならない。

■ソケット接続

以下のようにしてホスト間のソケット接続を確立する。

サーバ側

<?php
$socket = stream_socket_server('tcp://0.0.0.0:1037');
while($conn = stream_socket_accept($socket)){
    fwrite($conn, "Hello, World!n");
    fclose($conn);
}
fclose($socket);
?>
php ./server.php &

クライアント側

<?php
$socket = stream_socket_client('tcp://0.0.0.0:1037');
while(!feof($socket)){
    print(fread($socket, 100));
}
fclose($socket);
?>
php ./client.php
#Hello, World!

各関数について

resource stream_socket_client(string $remote_socket)
インターネットドメインもしくはUNIXドメインのソケット接続を開く。
resource stream_socket_server(string $local_socket)
インターネットドメインもしくはUNIXドメインのサーバソケットを生成する。
resource stream_socket_accept(resource $server_socket)
サーバソケットの接続を受け入れる。

■ストリームフィルタ

以下のようにstream_filter_append関数を使用してストリームフィルタを付加する。フィルタ「string.toupper」は「全ての文字を大文字」にし、「zlib.inflate」は「データの圧縮」をする。

<?php
$socket = stream_socket_server('tcp://0.0.0.0:1037');
while($conn = stream_socket_accept($socket)){
    stream_filter_append($conn, 'string.toupper');
    stream_filter_append($conn, 'zlib.deflate');
    fwrite($conn, "Hello, World!n");
    fclose($conn);
}
fclose($socket);
?>
<?php
$socket = stream_socket_client('tcp://0.0.0.0:1037');
stream_filter_append($socket, 'zlib.inflate');
while(!feof($socket)){
    print(fread($socket, 100));
}
fclose($socket);
?>

各関数について

resource stream_filter_append(resource $stream, string $filtername)
第一引数で指定したストリームに、第二引数のフィルタを末尾に付加する。
resource stream_filter_prepend(resource $stream, string $filtername)
第一引数で指定したストリームに、第二引数のフィルタを先頭に付加する。

PHP Controlling Directories and Files(ディレクトリとファイルの操作)

■ディレクトリの操作

通常はあまり使用しないが、以下のようにchdir関数を使って、インタープリタのワーキングディレクトリを変更することができる。

<?php
if(chdir('/usr/bin')){
    //code
}
print(getcwd());//'usr/bin'
?>

また、ディレクトリを作りたい場合は、以下のようにmkdir関数を用いる。

<?php
if(!mkdir('/usr/bin/test', 0777, true)){
    //code
    print('failed to make a directory.');
}
?>

各関数について

bool chdir(string $dirname)
アクセス権が無かったり、ディレクトリそのものが無かったりするとfalseを返す。
string getcwd(void)
カレントワーキングディレクトリを文字列で返す。
bool mkdir(string $dirname[, int $mode=0777[, bool $recursive=false]])
$dirnameのディレクトリをアクセス権$modeで生成する。ディレクトリを生成する場所の親ディレクトリが存在しない場合は失敗し「false」を返す。但し、第三引数で「true」が設定されていた場合は、親ディレクトリも生成される。

■ファイルアクセスの操作

対象文字列がディレクトリかファイルを調べるには以下のような関数を用いる。

<?php
if(is_dir('/usr/bin')){
    //code
}
if(is_file('/usr/bin/test.txt')){
    //code
}
?>

対象ファイルがPHPからどのような状態にあるか調べるには以下のような関数を使用する。但し、ファイルへのアクセスはシステムのボトルネックとなり得るため、実行結果はキャッシュされ、再度同じ関数をコールするとキャッシュから結果が返る。従って、アクセス権変更後に正しい値を返す必要がある場合は、clearstatcache関数をコールする必要がある。

<?php
$file = 'count.txt';
if(is_executable($file)){
    //code
}
if(is_writable($file)){
    //code
}
if(is_readable($file)){
    //code
}
if(is_link($file)){
    //code
}
if(is_uploaded_file($file)){
    //code
}
?>

ファイルのアクセス権を設定するには、以下のようにchmod関数を用いる。

<?php
if(chmod('count.txt', 0644)){
    //code
}
?>

各関数について

bool is_dir(string $dirname)
対象ディレクトリが存在し、ディレクトリであるならば「true」を返す。
bool is_file(string $filename)
対象ファイルが存在し、ファイルであるならば「true」を返す。
bool is_executable(string $filename)
ファイルが実行可能かどうか調べる。
bool is_writable(string $filename)
ファイルが書き込み可能かどうか調べる。
bool is_readable(string $filename)
ファイルが読み込み可能かどうか調べる。
bool is_link(string $filename)
ファイルがシンボリックリンクかどうか調べる。
bool is_uploaded_file($_FILE[$key][‘tmp_name’])
引数で指定されたファイルがアップロードされたものかどうか調べる。
bool chmod(string $filename, int $mode)
$filenameのファイルのアクセス権を$mode(8進数)に設定する。

PHP Accessing Files(ファイルへのアクセス)

ファイルから読み込みを行うと、読み込みが完了した位置にファイルポインタが移動する。

■一般的なファイル操作

ファイルを操作するには以下のようにする。まず、fopen関数でファイルをオープンし、flockでファイルをロックする。ファイルハンドラを使う場合、ファイルの全てのデータをメモリに読み込むわけではないので。メモリの節約に繋がる。

<?php
$mask = umask(077);
$fh = fopen('count.txt', 'a+');//0600
umask($mask);
if($fh === false){
    die('Failed to open the file.');
}
if(flock($fh, LOCK_EX)){
    if(filesize('count.txt') === 0){
        $counter = 0;
    }
    else{
        $counter = (int) fgets($fh);
    }
    ftruncate($fh, 0);
    $counter ++;
    fwrite($fh, $counter);
}
fclose($fh);
print($counter);
?>

但し、fgets関数については注意しなくてはならない。

<?php
print(fgets($fh, 1));
//nothing!!
?>

上述のコードを書いてもファイルから文字が読み出されることはない。なぜならば$length-1バイトしか読み込まないためである。従って、以下のように文字数+1の値を記述する必要がある。

<?php
print(fgets($fh, 2));
//1 character is displayed
?>

つうかコノ仕様が非常に覚えづらい。他の関数と統一して欲しい。

各関数について

resource fopen(string $filename, string $mode[, bool $use_inc_path=false[, resource $stream_context]])
第一引数で指定したファイルを、第二引数で指定したモードで開く。ファイルがオープンできなかった場合は、falseを返す。
bool flock(resource $fh, int $mode)
第一引数で指定したファイルハンドラのファイルを、第二引数で指定したモードでロックする。
int filesize(string $filename)
引数で指定したファイルのサイズを取得する。この関数の結果はキャッシュされるので、1リクエスト内に何回もコールし、そのたびに正確な値が必要なときは、clearstatcahche関数をコールする。単位はバイト。
string fgets(resource $fh[, int $length])
ファイルポインタから1行データを取得する。$lengthが設定されていた場合は、$length-1バイト読み出す。
string fgetc(resource $fh)
ファイルポインタから1文字データを取得する。
bool ftruncate(resource $fh, int $size)
第一引数で指定したファイルハンドラのファイルを、第二引数で指定した値の長さにする。元のファイルの余分な部分はカットされ、足りない場合はヌルバイトで補う。
int fwrite(resource $fh, string $data)
第一引数で指定したファイルポインタのファイルに、第二引数で指定した文字列を書き込む。戻り値は書き込んだバイト数(失敗した場合はfalse)。
bool fclose(resource $fh)
オープンしたファイルハンドラをクローズする。ファイルロックをしていた場合は解除する。
int umask(int $mask)
現在のumaskを設定し古いumaskを返す。ファイルを作成する前に適切な権限でファイルが作成されるように調整するために使う。

ファイルオープンのモード

r 読み取り専用(ファイルポインタは先頭)
r+ 読み取り、書き込み専用(ファイルポインタは先頭)
w 書き込み専用(ファイルポインタは先頭、ファイルサイズを0に)
ファイルが存在しない場合は作成を試みる
w+ 書き込み、読み取り専用(ファイルポインタは先頭、ファイルサイズを0に)
ファイルが存在しない場合は作成を試みる
a 書き込み専用(ファイルポインタは最後)
ファイルが存在しない場合は作成を試みる
a+ 書き込み、読み取り専用(ファイルポインタは最後)
ファイルが存在しない場合は作成を試みる
x 書き込み専用でファイルを作る
ファイルが存在する場合はエラーとなる
x+ 読み込み専用でファイルを作る
ファイルが存在する場合はエラーとなる

ファイルのロックモード

LOCK_SH 共有ロック
LOCK_EX 排他ロック

書き込みを行う場合は、排他ロックを使用する。

また、以下のような方法でファイルの文字を全て読み込むことができる。

<?php
if(file_exists('count.txt')){
    $fh = fopen('count.txt', 'r');
    $txt = '';
    while(!feof($fh)){
        $txt .= fread($fh, 1);
    }
    print($txt);
}
?>

各関数について

bool file_exists(string $filename)
引数で指定したファイルもしくはディレクトリが存在するか調べる。但し、ファイルが存在しても、PHPの実行ユーザからアクセスできない場合はfalseを返す。
bool feof(resource $fh)
ファイルポインタがファイル末端に達しているか調べる。
string fread(resource $fh, int $length)
第一引数で指定したファイルハンドラのファイルから、第二引数で指定した$lengthバイト数分だけ読み込む。

■ファイルポインタの移動

<?php
$fh = fopen('count.txt', 'r+');
fseek($fh, 2, SEEK_SET);
?>

各関数について

int fseek(resource $fh, int $offset[, int $whence])
第一引数で指定したファイルハンドラのファイルポインタを、第二引数で指定したバイト数目にセットする。但し、第三引数でSEEK_SETでなく、SEEK_CURを指定した場合は現在の位置からのカウントになり、SEEK_ENDの場合はファイルの末端からカウントする。従って、SEEK_ENDの場合、第二引数は0もしくは負の値となる。
int ftell(resource $fh)
現在のファイルポインタの場所を返す。

■csvファイルの操作

以下のCSVファイルを例に解説する。ちなみに最終行にも改行が必要である。

"Jack","54","USA"
"Emily","18","Japan"

以下のようにfgetcsv関数を用いてcsvファイルの内容を読み出す。また、fputcsv関数を用いて行を追加する。

<?php
$fh = fopen('member.csv', 'r+');
while($row = fgetcsv($fh)){
    print("{$row[0]}({$row[1]})-{$row[2]}" . PHP_EOL);
}
$row = array('John', '28', 'Canada');
fputcsv($fh, $row);
/*
Jack(54)-USA
Emily(18)-Japan
John(28)-Canada
*/
?>

各関数について

array fgetcsv(resource $handle[, int $length[, string $delimiter[, string $enclosure[, string $escape]]]])
第一引数以外はオプションである。デフォルトのデリミターはカンマで、括りはダブルクォーテーションである。
int fputcsv(resource $handle, array $data[, string $delimiter[, string $enclosure]])
第一引数で指定したファイルポインタのファイルに、第三引数以降の引数に基づき、CSV形式の1行分に整形を行った第二引数のデータを書き込む。

■簡易化されたファイル操作

以下のように動画を出力するような場合は、上述の関数を使用するよりもリソースを抑えられる。

<?php
header('Content-type: video/mpeg');
readfile('test.mpg');
?>

また、以下のようにfile_get_contents関数を使用すると、ファイルの内容を全て変数に読み込むことができる。この方法ではメモリに全てのデータを読み込むが、(OSが対応している場合のみ)リソースの消費を抑えるためメモリマッピング技術が使われる。

<?php
$data = file_get_contents('count.txt');
?>
<?php
$data = implode('n', file('count.txt'));
?>

さらに、PHP5では以下のようにfile_put_contents関数を使用し、ファイルに書き込みを行うこともできる。

<?php
$data = 'This is a pen!';
file_put_contents('count.txt', $data, FILE_APPEND | LOCK_EX);
?>

各関数について

int readfile(string $filename[, bool $use_inc_path=false[, resource $stream_context]])
ファイルを読み込んで標準出力に書き出す。
string file_get_contents(string $filename[, bool $use_inc_path=false[, resource $stream_context]])
引数で指定したファイルの内容を文字列として全て読み込む。失敗した場合はfalseを返す。空白のような文字が含まれるURLをオープンする際は、urlencode関数でエンコードする必要がある。
int file_put_contents(string $filename, mixed $data[, int $flags[, resource $context]])
第一引数で指定したファイルに、第二引数で指定したデータを書き込む。第三引数でFILE_APPENDを指定しない場合は、ファイルが上書きされる。また、ファイルを多数のユーザが操作する場合は必ずLOCK_EXを指定する。
array file(string $filename)
ファイル全体を読み込んで配列に格納する。