phpな休日 BBS sitemap
1. 親記事と返信記事の表示を区別する

[13] で作成したゲストブックに返信機能を追加して [BBS] を作成します。
まったく個人的見解ですが、私は 「返信出来ないのがゲストブック、返信出来れば掲示板」と勝手に定義しています。

返信機能を追加するとログの行に対して、親記事か返信記事かを区別する材料が必要になります。一般的に用いられている方法で、記事番号の他に親記事番号をログに残すことにします。ゲストブックのログに親記事番号を追加するとログデータは次のようになります。

5<>5<>name<>email<>url<>title<>comment<>time<>iphost<>
6<>5<>name<>email<>url<>title<>comment<>time<>iphost<>
3<>3<>name<>email<>url<>title<>comment<>time<>iphost<>
4<>3<>name<>email<>url<>title<>comment<>time<>iphost<>
1<>1<>name<>email<>url<>title<>comment<>time<>iphost<>
2<>1<>name<>email<>url<>title<>comment<>time<>iphost<>

ひとつめの項目が記事番号で、これは通し番号で 1 から順に 2 ・ 3 ・ 4 …と増えていき、同じ番号は存在しません。行を特定するのに必ず必要になります。
ふたつめの項目が今回追加する親記事番号で、その行が返信投稿されたものであった場合、返信元の親記事の番号を残すようにします。結果、親記事は項目1と項目2の番号が同じものとなります。
私と sakuraさんがBBSでやりとりしたと想定して、次のようなログファイルを手動で作成して表示させてみます。

5<>5<>haku<>email<>http://www.p-ho.net/<>title3<>comment3<>time<>iphost<>
6<>5<>sakura<>email<>http://www.p-ho.net/<>Re:title3<>response3<>time<>iphost<>
3<>3<>haku<>email<>http://www.p-ho.net/<>title2<>comment2<>time<>iphost<>
4<>3<>sakura<>email<>http://www.p-ho.net/<>Re:title2<>response2<>time<>iphost<>
1<>1<>haku<>email<>http://www.p-ho.net/<>title1<>comment1<>time<>iphost<>
2<>1<>sakura<>email<>http://www.p-ho.net/<>Re:title1<>response1<>time<>iphost<>

ログファイル名を [bbslog.cgi] とした場合の PHPソースは以下のようになります。(返信記事の先頭を60ピクセル下げただけですが)

<?php
$log = file("./bbslog.cgi"); //ログファイルを読み込みます
print "<hr>"; //最初に線を一本表示
for($i=0; $i<count($log); $i++) { //1行づつ走査しながら表示します
$list = explode("<>",$log[$i]);
if ($list[0] == $list[1]) { //親記事なら
print $list[5] . " ";;
print $list[2] . " ";
if (isset($list[4])){
print "<a href='" . $list[4] . "' target='_blank'>url</a> ";
}
print $list[7] . " [" . $list[0] . "]<br>";
print $list[6];
print "<hr>"; //記事の最後に線を表示
}
else { //返信記事なら
print "<span style='margin-left:60px;'>" . $list[5] . " "; //先頭に60ピクセルの空きを作る
print $list[2] . " ";
if (isset($list[4])){
print "<a href='" . $list[4] . "' target='_blank'>url</a> ";
}
print $list[7] . " [" . $list[0] . "]</span><br>";
print "<div style='margin-left:60px;'>" . $list[6] . "</div>";
print "<hr style='margin-left:60px;'>"; //記事の最後に線を表示
}
}
?>

実行
2. ログ5行を1ページとして区切って表示する

親記事と返信記事が混在するログファイルをページで区切る場合、例えば上記ログファイルを [7] で作成した方式で5行で1ページ表示にすると

<?php
print "<hr>"; //最初に線を一本表示
if ($_GET['line']) {$line = $_GET['line'];} //GETで渡された番号を $line に代入
else {
$line = 0; //最初のページは 0
}
$pageline = 5; //1ページに表示させる行数
$log = file("./bbslog.cgi"); //ログファイルを読み込みます
for ($i = $line; $i < $line+$pageline; $i++) {
$list = explode("<>",$log[$i]);
if($list[0] != ""){
if ($list[0] == $list[1]) { //親記事なら
print $list[5] . " ";;
print $list[2] . " ";
if (isset($list[4])){
print "<a href='" . $list[4] . "' target='_blank'>url</a> ";
}
print $list[7] . " [" . $list[0] . "]<br>";
print $list[6];
print "<hr>"; //記事の最後に線を表示
}
else { //返信記事なら
print "<span style='margin-left:60px;'>" . $list[5] . " "; //先頭に60ピクセルの空きを作る
print $list[2] . " ";
if (isset($list[4])){
print "<a href='" . $list[4] . "' target='_blank'>url</a> ";
}
print $list[7] . " [" . $list[0] . "]</span><br>";
print "<div style='margin-left:60px;'>" . $list[6] . "</div>";
print "<hr style='margin-left:60px;'>"; //記事の最後に線を表示
}
}
}
$totalline = (count($log)); //総行数
$totalpage = ceil(($totalline / $pageline)); //総ページ数(小数点以下切り上げ)
if ($totalline > $pageline) { //2ページ以上になるなら
$back = $totalline; //最初の $back は総行数
$now = 1; //ページ番号は1から
$next = 0; //行番号に代入される値は0から
for ($i = $line; $back >= 1; $i++) { //1行づつ走査しながら1ページ分を表示します
if ($line == $next) {
print $now . "&nbsp;";
}
else {
print "<a href='bbs.php?line=" . $next . "'>" . $now . "</a>&nbsp;"; //GETで渡すリンク(行番号)
}
$now++; //ページ番号を増やしていく
$next = $next + $pageline; //行番号を増やしていく
$back = $back - $pageline; //行番号を減らしていく
}
}
?>

実行
3. スレッド単位でページを区切る

ひとつの親記事とそれに対する返信記事が別ページに分かれてしまってはまずいので、ページ区切りの単位をスレッドごとに変更します。
同じログファイル(bbslog.cgi)を2スレッドで1ページ表示するようにしてみます。

<?php
print "<hr>"; //最初に線を一本表示
if ($_GET['line']) {$line = $_GET['line'];} //GETで渡された番号を $line に代入
else {$line = 0;} //GETがなければ $line は 0
$pagethread = 2; //1ページに表示するスレッド数
$log = file("./bbslog.cgi"); //ログファイルを開く
$data = fopen("./bbslog.cgi", "r"); //データを読み込む
$totalthread = 0; //とりあえずスレッド数を 0 に
for ($i = 0; $i < count($log); $i++) { //親記事の数を数えるための走査
$list = explode("<>", $log[$i]); //区切り文字 <> で分ける
if ($list[0] == $list[1]) {$totalthread++;} //親記事の数を数える
}
fclose($data); //ファイルを閉じる
$threadno = 0;
foreach ($log as $value) { //また走査
$list = explode("<>", $value);
if ($list[0] == $list[1]) {$threadno++;} //親記事だったら $threadno を増やす
if ($threadno <= $line * $pagethread) { //$pagethread 分読んだら
continue; //繰り返しを抜けて次へ
}
elseif ($threadno > ($line + 1) * $pagethread) { //終わりまできたら
break; //処理を終了する
}
if ($list[0] == $list[1]) { //(表示)親記事なら
print $list[5] . " ";;
print $list[2] . " ";
if (isset($list[4])){
print "<a href='" . $list[4] . "' target='_blank'>url</a> ";
}
print $list[7] . " [" . $list[0] . "]<br>";
print $list[6];
print "<hr>"; //記事の最後に線を表示
}
else { //(表示)返信記事なら
print "<span style='margin-left:60px;'>" . $list[5] . " "; //先頭に60ピクセルの空きを作る
print $list[2] . " ";
if (isset($list[4])){
print "<a href='" . $list[4] . "' target='_blank'>url</a> ";
}
print $list[7] . " [" . $list[0] . "]</span><br>";
print "<div style='margin-left:60px;'>" . $list[6] . "</div>";
print "<hr style='margin-left:60px;'>"; //記事の最後に線を表示
}
}
if ($totalthread > $pagethread) { //2ページ以上になるなら
$lines = ceil(($totalthread/$pagethread-1)); //何ページになるか計算する
$now=1;
$next=0;
for ($i = 0; $lines >= 0; $i++) { //ページ番号表示のための走査
$list = explode("<>", $log[$i]);
if ($line == $next) {
print $now . "&nbsp;";} //表示中のページはリンクなし
else {
print "<a href='./bbs.php?line=" . "$next" . "'>" . "$now" . "</a>&nbsp;";}//アンカータグで GET で渡す
$now++;
$next = $next + 1;
$lines = $lines - 1;
}
}
?>

continuebreak が初出です。continue は繰り返し処理を中断して次の処理から再開します。break は繰り返し処理を中止します。

実行

もうひとつ。返信投稿用のフォームへのリンクとして親記事内にアンカータグを仕込んだ Re: を追加しておきます。 GET で記事番号(resformno)を渡します。

print $list[7] . " [" . $list[0] . "] <a href='./bbs.php?resformno=" . $scan[0] . "'>Re:</a><br>";

つづきは次回。

2014.2.4 last edit

Produced by haku