あーる学習帳

自分が勉強したことや気になることなど、99%自分用です。コードを書いてるのでPCから閲覧を推奨。

PHP実習:ファイルへの書き込み、ファイルからの読み込み

昼にITに詳しい方々のつぶやきを目にし、モチベーションが高まっております。こんなのを作ってみたいなぁ、という方向性も漠然としているものの見えてきた感じ。

 

※第10章では「メール送信とファイル操作」というタイトルでPHPによるメール送信とファイル操作について扱っているが、書籍で想定している環境と自分が使っている仮想環境が違いすぎるのでメールの設定が一筋縄ではいかないことが判明。今回はメールの部分は飛ばし、ファイルの読み書きについてのみ扱う。

第10章-03

テイストファイルなどへの書き込みをする場合、いきなりファイルに文字列を記録するのではなく、いくつかの手順を踏む必要がある。

  1. ファイルを開く
  2. ファイルをロックする
  3. ファイルに書き込む
  4. ファイルのロックを解除する
  5. ファイルを閉じる 

 ファイルを開く際、書き込み対象となるファイルがない場合は自動で作成するなどの設定も可能。次にファイルに複数人からの同時書き込みが起こらないように回避する対策を施す。これを「ファイルのロック」と呼んでいる。これによって、先に書き込み処理を行ったプログラムの終了を待ってから次の書き込みが行われるようになる。もしこの対策をしなかった場合、ファイルが壊れてしまう可能性もあるので注意する。

今回はアクセスログを作ってみる。

 <?php
 $time=date('H:i:s');
 $ip=$_SERVER['REMOTE_ADDR'];
 $data="{$time}\t{$ip}\r\n";
 $file=@fopen('access.log','a') or die(',ファイルを開けませんでした。');
 flock($file,LOCK_EX);
 fwrite($file,$data);
 flock($file,LOCK_UN);
 fclose($file);
 echo "アクセスログを記録しました。";

IPアドレスは$_SERVER['REMOTE_ADDR']で取得できる。

ファイルを開く際にはfopen()という関数の頭に@をつける。これは「エラー制御演算子」といって、エラーが発生する可能性のあるものに付けた場合、エラーが発生しても警告を発生しなくなる。今回のコードではエラーが発生した場合、自前のメッセージを表示して処理を中止させている。「or」は演算子の一種で、左辺のfopen()が問題なく動作した場合は実行されない。fopen()の第2引数に'a'とつけておけば、第1引数で指定したファイルが存在しない場合は新たにファイルを作成する。ここで$fileに代入されるのは「resource(リソース)」と呼ばれる型のもので、これを取得することによってファイルを操作することが可能になる。

実行するとこのような画面になる。

f:id:R_de_aru:20180705202640p:plain

この画面になった後、Cyberduckを見るとaccess.logができていることがわかる。

f:id:R_de_aru:20180705202714p:plain

このaccess.logを開くと、先ほどの画面を出した時間とどのIPから開かれたのかがログとして残っているのが分かる。

f:id:R_de_aru:20180705202756p:plain

flock()は第1引数にファイルのリソース、第2引数にオペレーションを設定する。

flock( ファイルのリソース , オペレーション )

オペレーション一覧

LOCK_EX...書き手が行う排他的ロック(他者の読み書きを禁止) 

LOCK_SH...読み手が行う共有ロック(他者の書き込みを禁止)

LOCK_UN...ロック解除

今回は自分が書き込む間は他者が書き込みできないようにするためのロックをかけるので、LOCK_EXを使用する。

fwrite()は第1引数にリソース、第2引数に文字列を設定する。「"{$time}\t{$ip}\r\n"」のように、変数や改行を展開させるためにダブルクォーテーションで囲むこと。

最後にLOCK_UNを設定してロックを解除する。これにより、もし書き込みを待っている別のユーザーがいれば新たな書き込みが開始される。fclose()でファイルを閉じることを明記する。この一連の流れを必ず行うこと。

 

第10章-04

今度はファイルの読み込みを行う。その前に開き方についてまとめるが、先ほどは@fopen('access.log','a') というように「a」で追加書き込み専用の設定を行った。これをオープンモードという。主要なモードについては以下の通りとなる。

r...読み込み専用

r+...rに加えて書き込みも可能

w...ファイルをクリアして上書き書き込み

w+...wに加えて読み込みも可能

a...既存の内容に追記書き込み

a+...aに加えて読み込みも可能 

x...ファイルが存在する場合にエラーを出す

今回はファイルを読み込むだけなので「r(=read)」でよい。 実際にコードを書いていく。

$file=@fopen('access.log','r') or die(',ファイルを開けませんでした。');
flock($file,LOCK_SH); // 共有ロック
while (!feof($file)){
 $line=fgets($file);
 echo '<p>'.$line.'</p>';
}
flock($file,LOCK_UN);
fclose($file); 

 

f:id:R_de_aru:20180705204629p:plain

このように出力される。

feof()はファイルポインタ(何行目がフォーカスを持っているか示す)が終端にくるとTrueを返す。fgets()は1行分の文字列を取得した後にファイルポインタを1行進める。

eofは「EndOfFile」の略。今のatomの設定では見ることはできないが、テキストファイルの終端には必ず存在している。

次に、今のコードを改造して行数をカウントして出力する。

$file=@fopen('access.log','r') or die(',ファイルを開けませんでした。');
flock($file,LOCK_SH); // 共有ロック
$count=0; //カウント用変数の初期化
while (!feof($file)){
 $line=fgets($file);
 echo '<p>'.$line.'</p>';
 $count++; // カウント追加
}
flock($file,LOCK_UN);
fclose($file);
echo ($count-1).'回の訪問がありました。';//最後の行数の1を引いて出力

f:id:R_de_aru:20180705205224p:plain

行数のカウントは$countを初期化し、fgets()するごとに1を足して実現する。書き込み時に改行コードを入れているため、「eof」の制御コードは文字列の1つ下の行にある。この行もカウントされてしまうので、$countから1引いて出力すればログと行数が一致する。

また、ファイルの読み込み方法はfgets()だけではない。

<?php

 $file=file('access.log');

 foreach($file as $line){

  echo '<p>'.$line.'</p>';

 }

 file()を使うと、行ごとに格納した配列として取得する。しかし、便利である反面、巨大なテキストを相手にした際のメモリの消費量が大きくなるという問題もある、

今回の実習はメール送信が入っているため割愛。お問い合わせフォームを作るという実習だったのでやりたかった…。