簡単なお知らせを表示する(1)
まぁそういうことで、さっそくタイトルの通り、簡単なお知らせを表示させるものをPHPで作ってみる。トップページに載ってるような感じのちょこっとしたお知らせをイメージしてる。更新情報みたいなものか。でも更新情報って詳細はこちら的なリンクがたいがいついてますよね。今日はリンクをつける気はないので敢えて「お知らせ」。
完成目標
- 入力内容は「タイトル」「本文」の2つだけ。
- 「日付」は更新された時の時間を表示する。
- 作るファイルはフォームページ(index.html)と処理・表示するページ(news.php)とログ用ファイル(data.dat)の3つにする。
- エラー処理とかなし!後で!
- 件数指定とかなし!後で!
- パスワードとかなし!後で!
- リンクを入れたいとかなし!後で!
- 画像を入れたいとかなし!後で!
- その他モロモロなし!後で!
考えた流れ
目次的な・・・クリックしたら同ページ内アンカーで飛びます。
- 入力用フォームを作る。
- 受け取ったデータを変数に入れる。日付、ログ用ファイルも変数に。
- フォームの送信ボタンが押されたら。
- タイトルと本文の中身が入ってたらエスケープ。本文の改行文字はbrに変換。
- それぞれの変数を配列にしてタブ区切りで結合する。
- ログ用ファイルを開いてロック。
- 一行に結合してたデータをログ用ファイルに書き込む。
- ロックを解除してログ用ファイルを閉じる。
- 表示のためにログ用ファイルを読み込んで配列に。
- 分解して変数に値をセットしてログ用ファイルの中身の数だけループ。
- 完成した処理・表示するページのソース
普通にフォームを作る。
ここはもういつも通りのhtmlでサクっと作った。とりあえずこれをindex.htmlとして保存。見た目とかはどうでもいいのでデフォのままで。
ここから処理・表示をするnews.phpにデータを渡す。
<form method="post" action="news.php">
<dl>
<dt>タイトル</dt>
<dd><input type="text" name="subject" /></dd>
<dt>メッセージ</dt>
<dd><textarea name="body" cols="50" rows="6"></textarea></dd>
</dl>
<p><input type="submit" name="submit" value="送信" /></p>
</form>
受け取ったデータを変数に入れる。
日付、ログ用ファイルも変数に。
$subject = @$_POST["subject"];
$body = @$_POST["body"];
$time = date("Y/m/d H:i:s");
$data_file = "data.dat";
フォームからpostで受け取ったデータを$_POST["なんたら"]の形で受けとる。今回参考にしているPHPによるWebアプリケーションスーパーサンプルという素敵な本によると$_POST["なんたら"]の代わりに「$なんたら」と書くだけでもPHP.iniの設定次第では表示される事もできるらしい。でもセキュリティの面からOffにするのが推奨されてるようなので素直に$_POST["なんたら"]の書き方に従う。あと「@」は初回アクセス時に$_POSTが存在しないためにエラーが出るのを防ぐって書いてあった。それは1つのファイルで完結する場合のお話?とりあえず言われるがままに書いておく。おまじない的なものと思っていいのかしら。
日付はdata関数で取得。(関数については下のほうにまとめの表あり)「data.dat」というログ用ファイルを用意して変数に入れておく。
フォームの送信ボタンが押されたら。
if($_POST["submit"]){
ここに処理内容
}
こういうifの省略形で書かれてるソースを多々目にしたのでそういうもんなのかなという発想で省略形にしてみた。省略する前は($_POST["submit"] == TRUE)なんだと思う。submitボタンにvalueも指定してる場合は($_POST["submit"] == "valueの値")でもいいんだと思う。
タイトルと本文の中身が入ってたらエスケープ。
本文の改行文字はbrに変換。
if(($subject !== "") && ($body !== "")){
$subject = htmlspecialchars($subject, ENT_QUOTES);
$body = htmlspecialchars($body, ENT_QUOTES);
$body = nl2br($body);
$body = ereg_replace("\n|\r|\r\n","",$body);
}
($subject !== "") && ($body !== "")で$subjectと$bodyの中身が両方空じゃなかったらの条件を指定。この否定の「!」はよく見かける。「&&」は演算子の論理積「$a && $b ・・・ $a および $b が共に TRUE の場合に TRUE」っていうよく見かけるやつ。「and」との違いは優先順位らしい。「&&」の方が「and」より優先順位が高かったのでとりあえずこっちを採用。というか「&&」はいろんなソースでよく見かけるから何かしらんがこっちが無難なんだろうと判断。
フォームから受け取ったタイトルと本文の内容はhtmlspecialcharsでエンティティ変換。本文には改行が入る場合も考えられるのでnl2brで改行文字の前に<br />を挿入する。
で、最後の一行。最初この一行書いてなくてエライ目にあった。今回は1つのデータは1行として扱う事にしてたのに、この行がないと改行がログ用ファイル内でも反映されて改行されちゃって、件数が増殖してた。nl2brを入れたらそれでいいと思ってたけどそんなに甘くなかったと。とりあえず改行コードについてもっかい調べなおしたら「\n」「\r」「\r\n」の3つがあるって分かった。それらのどれかが本文の中に入ってるか正規表現で調べて置換という発想に至った。
それぞれの変数を配列にしてタブ区切りで結合する。
$news = implode("\t",array($subject,$body,$time));
区切り文字を何にするのがいいのか迷ったけどとりあえずタブ区切りにしてみた。他によく目にするのは「,」とか半角スペースとか。
ログ用ファイルを開いてロック。
$file = fopen($data_file,"a") or die("エラー");
flock($file,LOCK_EX);
普通にログ用ファイルを追記モードで開いて排他的ロック。「or die("エラー")」の部分はそういうのがちゃんと書いてあるのも見たことあるし、書いてないのも見たことある。とりあえず書いた方が丁寧ってことなんかなと思って書いてみた。
ロックについてなんだけど、上に書いた本によるとシステムを1人で使い続けるならロックは必要ないでしょう
って書いてあったんだけれども、一応入れておいた。もうこれはおまじないとして覚え込もうと思った。
一行に結合してたデータをログ用ファイルに書き込む。
fputs($file,"$news\n");
タブ区切りで結合して一行にしたものを書き込んで行の最後に改行の\nが入るようにした。
ロックを解除してログ用ファイルを閉じる。
flock($file,LOCK_UN);
fclose($file);
上でロックしたから当然ロック解除もする。んでファイルを閉じる、と。
ログ用ファイルの中はこんな下記のような感じで1件1行でタブ区切りでタイトル、本文、日付が並んで保存されてる。改行してる部分も大丈夫。
表示のためにログ用ファイルを読み込んで配列に。
$news_list = file($data_file);
$news_list = array_reverse($news_list);
さっき書き込んだファイルをfileで全部読み込んで配列にセット。書き込んである順番が古いのが上、最新が一番下になってる状態なので「お知らせ」としてはやっぱ最新情報が一番上でしょということでarray_reverseで配列を逆順にする。古いのが一番上がいい場合は下の行はいらない。
分解して変数に値をセットしてログ用ファイルの中身の数だけループ。
$news_num = count($news_list);
for($i = 0; $i < $news_num; $i++){
list($subject2,$body2,$time2) = explode("\t",$news_list[$i]);
echo "<h1>$subject2</h1>\n";
echo "<p>更新日時:$time2</p>\n";
echo "<p>$body2</p>\n";
}
「$news_num = count($news_list);」は別に変数に入れなくてもforの「$i < $news_num;」の部分を直接「i < count($news_list);」にしてもいいんかなって思ったけど、結局どっちがキレイな書き方なのか判断つかなくて変数に一旦入れてみた。まぁもしも表示ページに全部で何件あるか表示しようと思ったときとかは入れておいた方が便利ですよね。
とりあえずその数の分だけforで繰り返し処理。繰り返しの中身は一行に合体している中身をexplodeでタブ区切りで分解してバラバラに戻す。それをlistを使って表示用に使うそれぞれの変数に値を入れていく。
あとはそれらの変数をマークアップしたものをechoで表示させる。
今はテストだからh1とpでマークアップしたけど、これを配置したい場所のマークアップに書き換えてincludeで読み込ませたら完成。
やっほー! ヽ(´∀`ヽ)
完成した処理・表示するページのソース
いつものごとくサンプルをアップしようと思ったけど、書き込みできるものなのでやめた。入力フォームのソースは上の方に書いたとおり。
<?php
//受け取ったデータを変数に入れる
$subject = @$_POST["subject"];
$body = @$_POST["body"];
$time = date("Y/m/d H:i:s");
//ログ用データファイル
$data_file = "data.dat";
//送信ボタンが押されたら
if($_POST["submit"]){
//タイトルと本文の中身があるか確認
//中身がなかったら何もならない
if(($subject !== "") && ($body !== "")){
//エスケープ
$subject = htmlspecialchars($subject, ENT_QUOTES);
$body = htmlspecialchars($body, ENT_QUOTES);
//$bodyの改行文字を<br />へ
$body = nl2br($body);
//改行コードを正規表現で置換
$body = ereg_replace("\n|\r|\r\n","",$body);
//変数を配列にしてタブ区切りで結合
$news = implode("\t",array($subject,$body,$time));
//ログ用ファイルを開く
$file = fopen($data_file,"a") or die("エラー");
//ロック
flock($file,LOCK_EX);
//データを書き込む
fputs($file,"$news\n");
//ロック解除
flock($file,LOCK_UN);
//ファイルを閉じる
fclose($file);
//表示用にファイルを読み込んで配列にセット
$news_list = file($data_file);
$news_list = array_reverse($news_list);
$news_num = count($news_list);
//書き込まれたデータの数だけ繰り返し
//上で結合してたのをタブ区切りで分解して変数に値をセット
for($i = 0; $i < $news_num; $i++){
list($subject2,$body2,$time2) = explode("\t",$news_list[$i]);
echo "<h1>$subject2</h1>\n";
echo "<p>更新日時:$time2</p>\n";
echo "<p>$body2</p>\n";
}
}
}
?>
今回使用した関数
date | ローカルの日付/時刻を書式化する |
---|---|
htmlspecialchars | 特殊文字を HTML エンティティに変換する |
nl2br | 改行文字の前に HTML の改行タグを挿入する |
ereg_replace | 正規表現による置換を行う |
implode | 配列要素を文字列により連結する |
array | 配列を作ってデータをセットする |
fopen | ファイルまたは URL をオープンする |
die | メッセージを出力し、現在のスクリプトを終了する |
flock | 汎用のファイルロックを行う |
fputs | バイナリセーフなファイル書き込み処理 |
fclose | オープンされたファイルポインタをクローズする |
file | ファイル全体を読み込んで配列に格納する |
array_reverse | 要素を逆順にした配列を返す |
count | 変数に含まれる要素、 あるいはオブジェクトに含まれるプロパティの数を数える |
list | 配列と同様の形式で、複数の変数への代入を行う |
explode | 文字列を文字列により分割する |
echo | 1つ以上の文字列を出力する |
※関数って書いたけど「array()」と「list」に関してはマニュアルにこの関数は実際には関数ではなく言語の構成要素です
と書いてあった。言語の構成要素とか言われてもその意味はイマイチよく分からないけど、なんせ特別扱いの人達らしい。
今日の分からなかった事・疑問だった事メモ
フォームの送信ボタンが押されたら・・・の部分
今回は「ボタンが押されたら・・・」ってそればっかり考えてたけど、こういう時って「タイトルと本文の中身が空じゃなかったら(中身が入ってたら)」っていう書き方した方がいいのかなぁ、と思った。次は中身が入ってなかったらエラー文がでるようにしてみる予定なのでその時にもっかい考える。
ログ用ファイルの拡張子
今回は「data.dat」としたんだけど、手持ちの本や今まで見たことのあるプログラムはデータの記録ファイルの拡張子がdatのものとtxtのものがある。(データベースとかは置いておいて)どっちがいいのか分かんなかったけど、なんとなくdatファイルの方がよく見かける気がしたからこっちにした。何か違いあるのかな。分からない。どっちにしろ動いたから好みの問題?
たいしたことできてないのにちゃんと自分で考えた事まとめて書いたら賢くなった気がした。多分土日とかまとまった時間がある時しか書けないけど、続きも頑張るぞ!なんか楽しくなってきた。
<< 今年はPHPの勉強も頑張る。 | 簡単なお知らせを表示する(2) >>
トラックバック
このエントリーのトラックバックURL:
http://redline.hippy.jp/cgi/mt/mt-tb.cgi/206