PHP レッスン — レッスン 2 — ファイルの操作:開く、書き込む、読み込む。
前のレッスンでは、ゲストブック、ブログ、あるいはチャットの骨組みを作りました。あとは機能を追加するだけです。このレッスンでは、レコードをファイルに記録し、ファイルからレコードを出力します。
PHP にはファイルを扱う関数が多くあります。そのいくつかを取り上げます。おそらくあなたのサイトはデータをファイルではなくデータベースに記録するでしょう(これは確信しています)。ですから、要点を理解できるよう、そのうちのいくつかだけを取り上げます。データベースの操作は後で扱います。
では、前のレッスンで作ったファイルがなければ、1 つ前のレッスンに戻ってそれらのファイルをダウンロードしてください。
レコード追加のフォームに移りましょう:
/index.php?admin=1
このフォームからレコードを追加します。これらのレコードを処理するには、index.php ページ(このページにフォームからデータが送られます)で、$_POST 変数に値があるか確認する必要があります。確認はとても簡単です:
if($_POST){
}
簡単でしょう? では、波括弧の中に $_POST 配列の処理を置く必要があります。処理のコードを波括弧の中に直接書くのではなく、私たちのクラスに切り出し、処理自体はオブジェクトのメソッドを通じて呼び出します。index.php ファイルでメソッドを呼び出します:
if($_POST){
$obj->write($_POST);
}
そして simpleCMS.php ファイルには、write() メソッドの実装があります:
public function write($p) { // メッセージ記録のメソッド
}
なぜこうするのか? index.php ファイルのコードを短くするためです。index.php ファイルは管理のためだけに必要で、配列の処理やデータの出力は別のファイル、例えば simpleCMS クラスのファイルから行うべきです。この区分けにより、コードの効率を高められます。他の誰かが私たちのコードを見れば、どこに何があるかすぐに把握できます。あなた自身も、すべてが整然と置かれていれば、コードを把握しやすくなります。
処理は簡単なことから書き始めましょう。フォームから渡される $_POST 配列を出力します。そのために、次のコードを挿入します:
public function write($p) { // メッセージ記録のメソッド
print_r($p);
}
ご覧のとおり、ローカル変数 $p を参照しています。write() メソッドのパラメーターにはスーパーグローバル変数が渡されるのですが。スーパーグローバル変数を直接参照しないのは、他の場合には、フォームを通じてではなく XML ファイルからデータを取得してブログに記録したい、ということがあり得るからです。ローカル変数を使うことで、このメソッドの使い方を統一し、多くの場合に共通のものにできます。
では、フォームを埋めて送信します。私の場合、次のデータになりました:
Array ( [title] => イワン [bodytext] => こんにちは、世界! )
別の内容を埋めれば、配列も違うものになります。どのキーを使えるか見るために配列を出力しました。私たちの場合は $p['title'] と $p['bodytext'] です。これで、どんなデータになるか分かりました。あとはそれをファイルに記録するだけです。では、ファイルへの記録から始めましょう。
PHP ファイルへの記録
ファイルに何かを記録する前に、それを「書き込み用に開く」必要があります。書き込み用に開くとは、ファイルを PHP の変数に結びつけることです。PHP でのファイルに対するすべての作業は、resource 型の変数に対する操作に帰着します。それがどういう意味か示します:
public function write($p) { // メッセージ記録のメソッド
$message = fopen("messages/file.txt","w");
print_r($message);
}
そのために、messages フォルダに file.txt ファイルを作りました。これで、フォームを埋めて送信すると、次が得られます:
Resource id #4
Resource は変数 $message の型で、4 は連番、他の resource 変数の中でのこの変数の id です。これで、変数 $message に操作を行うと、それが自動的にファイルにも反映されます。後で同じように行単位で読めるよう、ファイルへの記録は行単位で行います:
public function write($p) { // メッセージ記録のメソッド
$message = fopen("messages/file.txt","w");
fputs ($message, $p['title']. "\r\n");
fputs ($message, $p['bodytext']);
}
二重引用符の中の \r\n という文字(単一引用符の中では動かないことがあります)は、新しい行への改行を意味します。その結果、file.txt ファイルには次のテキストが入ります:
イワン
こんにちは、世界!
ファイルに対するすべての操作を行った後は、オペレーティングメモリの場所を占有しないよう、ファイルの変数を削除する必要があります。
public function write($p) { // メッセージ記録のメソッド
$message = fopen("messages/file.txt","w"); // ファイルを開く
fputs ($message, $p['title']. "\r\n");
fputs ($message, $p['bodytext']);
fclose ($message); // ファイルを閉じる
}
おそらく、ファイルを新しく書くたびにファイルが上書きされることに気づいたでしょう。これはすべて fopen 関数のパラメーターに依存します。ファイルに追記したり、単に読み込み用に開いたりすることもできます。例えばこのように:
$message = fopen("messages/file.txt","r"); // ファイルを読み込み専用で開く
- r – ファイルを読み込み専用で開く。
- r+ - ファイルを読み込みと書き込みの両方で同時に開く。
- w – 新しい空のファイルを作る。呼び出し時にそのようなファイルがすでに存在する場合は、破棄される。
- w+ - r+ と同様だが、呼び出し時にそのようなファイルが存在する場合、その内容は削除される。
- a – 既存のファイルを書き込みモードで開く。その際、ポインターはファイルの最後のバイト(ファイルの末尾)に移動する。
- a+ - ファイルを読み込みと書き込みのモードで開く。その際、ポインターはファイルの最後のバイト(ファイルの末尾)に移動する。ファイルの内容は削除されない。
上に書いたように、パラメーター w でファイルを開くと、第 1 パラメーターから名前が取られる新しいファイルが作られます。time() 関数で得られる時刻に対応するファイル名を作りましょう。
time() 関数は、1970 年 1 月 1 日からの秒数を返します。
次のようになるはずです:
public function write($p) { // メッセージ記録のメソッド
$message = fopen('messages/'.time(),"w"); // ファイルを開く
fputs ($message, $p['title']. "\r\n");
fputs ($message, $p['bodytext']. "\r\n");
fputs ($message, time());
fclose ($message); // ファイルを閉じる
}
こうして、毎秒 time() 関数がますます大きな秒数を返し、ファイル名はますます大きな数になります。新しいレコードを追加してみると、新しいファイルが messages フォルダに現れます。あとは、ファイルを読み込んでレコードを出力するだけです。
PHP ファイルの読み込み
ファイルから読み込むには、ファイルを読み込み用に開く必要があります。そのために、fopen() 関数でパラメーター r を使います。必要なファイルはすべて messages フォルダにあります。すべてのファイルを読むために、while ループ(ファイルが尽きるまで)を使います。
フォルダ内のファイルは、フォルダを読み込み用に開く opendir() 関数を使って見ていきます:
if (is_dir('messages')) { // フォルダの有無を確認する
if ($dh = opendir('messages')) { // フォルダを読み込み用に開く
while (($file = readdir($dh)) !== false) { // ファイルが存在する間、読み込む
// 必要な操作を行う
}
closedir($dh); // ディレクトリを閉じる
}
}
ファイル名は readdir() 関数を使って取得します。つまり、名前は変数 $file に格納されます。ループの終了後、オペレーティングメモリの場所を解放するためにフォルダを閉じます。
ファイルを読めるようになったので、display_public() メソッドを仕上げていきましょう:
public function display_public() { // メッセージ出力のメソッド
$content = '';
if (is_dir('messages')) { // フォルダの有無を確認する
if ($dh = opendir('messages')) { // フォルダを読み込み用に開く
while (($file = readdir($dh)) !== false) { // ファイルが存在する間、読み込む
if(substr($file, -4) == '.txt'){ // .txt ファイルだけを取る
$filename = 'messages/' . $file; // ファイルの完全な名前
$message = fopen($filename, 'r'); // ファイルを開く
$title = fgets($message); // 1 行目を読む
$body = fgets($message); // 2 行目を読む
$time = fgets($message); // 3 行目を読む
print '<div class="post">'; // レコードを包む div
print '<span class="time">' . date('d-m-Y', $time) . '</span><h2>' . $title . '</h2>'; // 時刻とタイトルを出力する
print '<p>' . $body . '</p>'; // メッセージのテキストを出力する
print '</div>'; // 包む div の終わり
fclose($message); // ファイルを閉じる
}
}
closedir($dh); // ディレクトリを閉じる
}
}
$content .= '<p><a href="/index.php?admin=1">メッセージを追加</a></p>';
return $content;
}
各行にコメントを付けました。何か分からないことがあれば、コメントに書いてください。説明します。
実務でファイルからの行単位の読み込みが必要になることはないと思いますが、php 関数の動作原理、ファイルとフォルダの操作を理解するには、このようなレッスンはとても役立ちます。
以下に、途中段階のサイトのソースコードを載せます。