PHP+SQLite3で２ちゃんねるっぽい掲示板を作る


# 環境
CentOS 6.5
Apache 2.2.15
php 5.5.19
sqlite3 3.6.20

# 構成
- dbファイルはdbフォルダを作ってそこに入れておく
-- 各掲示板情報を管理するdb(list.db)
-- ちなみにパーミッション注意。dbフォルダは777,dbファイルはたしか755or766)

- phpファイルは以下のとおり。
-- 掲示板リストを表示する(list.php)
-- 各掲示板(bbs.php)
-- アンカーをクリックしたときの処理(link.php)
-- 新規投稿処理、結果(ins.php)
-- 掲示板追加処理(build.php)
-- 掲示板削除処理、結果(del.php)


# 予め準備するDBの内容
```list.db
CREATE TABLE list (
id INTEGER PRIMARY KEY, 
dbname TEXT, 
dbnamekana TEXT, 
kensu INTEGER, 
updtime TEXT DEFAULT (DATETIME('now', '+09:00:00')), 
flg INTEGER DEFAULT 0
);

// 具体的な各掲示板のDBは動的に生成するので予め用意する必要はない。
// dbnameは掲示板生成時に自動的にbbs1,bbs2・・・と名付けされる
// kensuは各掲示板に投稿されている投稿件数のこと
// updtimeは掲示板更新日時で、リスト表示するときにこの順番で並べる
// flg（フラグ）は将来的に過去スレ行きを識別するために使うとかです。

```
# 具体的なコード

```list.php
<?php
$db = new PDO("sqlite:db/list.db");

// 掲示板の数を取得しておく
$stmt = $db->query("SELECT COUNT(*) FROM list");
$listcount = $stmt->fetchColumn();
$stmt->closeCursor();

$i = 1;
$html = "";

// 掲示板が1つ以上存在すればリストをリンク付きで表示する
if ($listcount > 0) {
  // 新規投稿があった順に並べる
  $stmt = $db->query("SELECT * FROM list WHERE flg=0 ORDER BY updtime DESC");
  $list = $stmt->fetchAll(PDO::FETCH_ASSOC);
  foreach ($list as $row) {
    $html .= "<a href='bbs.php?dbname={$row["dbname"]}&dbnamekana={$row["dbnamekana"]}'>".$i.":".$row["dbnamekana"]." (".$row['kensu'].") ".$row['updtime']."</a>"." <a href='del.php?dbname={$row["dbname"]}'>削除する</a><br><br>";
  $i++;
  }
}
$db = null;

// 掲示板の数が10未満であれば新スレ作成フォームを表示する。
if ($listcount < 10) {
  $listcount++;
  $newdbname = "bbs".$listcount;
  $build = <<< EOM
  <form action="build.php" method="post">
  <input type="hidden" name="newdbname" value="{$newdbname}">
  名称：<input type="text" name="newdbnamekana" value="">
  <input type="submit" name="newbbs" value="新しく掲示板を立てる（制限10個）">
  </form>
EOM;
} else {
  $build = "これ以上掲示板は立てられません。";
}

?>
<!DOCTYPE html>
<html lang="ja">
<head>
<title>サンプル掲示板</title>
<meata charset="UTF-8">
<meta name="viewport" content="initial-scale=1.0">
<link rel="stylesheet" href="style.css" media="all">
</head>
<body>
<h2 style="color:#ff0000;">サンプル掲示板</h1>
<?php echo $html; ?>
<?php echo $build; ?>
</body>
</html>
```

```bbs.php
<?php
if (isset($_GET["dbname"]) && isset($_GET["dbnamekana"])) {
  $dbname = htmlspecialchars($_GET["dbname"], ENT_QUOTES);
  $dbnamekana = htmlspecialchars($_GET["dbnamekana"], ENT_QUOTES);
  if (isset($_GET["page"])) {
    $page = htmlspecialchars($_GET["page"], ENT_QUOTES);
  } else {
    $page = "";
  }
} else {
  header("Location: list.php");
}

// ページングするための情報取得、設定
$name = "sqlite:db/".$dbname.".db";
$db = new PDO($name);
$stmt = $db->query("SELECT COUNT(*) FROM bbs");
$kensu = $stmt->fetchColumn(); // 総件数
$hyouji = 3; // 1ページあたり表示件数
$pcount = ceil($kensu/$hyouji); // 総ページ数
$plast = $kensu % $hyouji; // 最終ページの件数
if ($page == "") {
  $page = $pcount;
}

// ページ移動リンク
$link = "";
for ($i=1; $i<=$pcount; $i++) {
  $link .= "<a href=bbs.php?dbname=".$dbname."&dbnamekana=".$dbnamekana."&page=".$i.">".$i."</a>&nbsp;&nbsp;";
}

// ページ表示
$stmt = $db->query("SELECT * FROM bbs LIMIT ($page-1)*$hyouji, $hyouji");
$html = "<h2 style='color:#ff0000;'>".$dbnamekana."</h2>";
$list = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($list as $row) {
  $html .= $row["id"]." : ".$row["hiduke"]."<br>";
  $html .= "<div id='commnet'>".$row["comment"]."</div><br><br>";
}

// コメントにアンカーがあればアンカーに変換する
$html = anchor($dbname, $html);
$db = null;

// 総件数が10件未満であれば投稿画面を表示する
if ($kensu < 10) {
  $form = <<< EOM
  <form action="ins.php" method="post">
  <input type="hidden" name="dbname" value="{$dbname}">
  投稿内容:<br>
  <textarea name="comment" cols=100 rows=10></textarea><br>
  <input type="submit" name="ins" value="送信">
  </form>
  <br>
EOM;
} else {
  $form = "10件以上は書き込めません。<br><br>";
}

// コメント中にアンカーがあればアンカーリンクを付与する
function anchor($dbname, $comment) {
  $pattern = "/&gt;&gt;([0-9])+/";
  $replace = '<a href="link.php?dbname='.$dbname.'&num=$0">$0</a>';
  $comment = preg_replace($pattern, $replace, $comment);
  return $comment;
}

?>

<!DOCTYPE html>
<html lang="ja">
<head>
<title><?php echo $dbnamekana; ?></title>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1.0">
<link rel="stylesheet" href="style.css" media="all">
</haed>
<body>
<?php echo $html; ?>
<?php echo $form; ?>
<?php echo $link; ?>
<br><br>
<a href="list.php">掲示板に戻る</a>
</body>
</html>
```

```ins.php
<?php
// 投稿日時等を取得する
$w = date("w");
$week_name = ["日", "月", "火", "水", "木", "金", "土"];
$hiduke = date("Y-m-d H:i:s")."(".$week_name[$w].")";

$html = "";

// データ受取、変換処理
if (isset($_POST["ins"])) {
  $dbname = htmlspecialchars($_POST["dbname"], ENT_QUOTES);
  $comment = nl2br(htmlspecialchars($_POST["comment"], ENT_QUOTES));
  $comment = anchor($comment);

　// 軽く制限かけてみた
  if (check_br($comment) > 10) {
    $html .= "改行が多過ぎます。10行以内でお願いします。<br><br>";
  } else if (check_strcount($comment) > 100) {
    $html .= "文字数が多過ぎます。100文字以内でお願いします。<br><br>";
  } else {
　　
　　// 投稿処理
    $name = "sqlite:db/".$dbname.".db";
    $db = new PDO($name);
    $db->exec("INSERT INTO bbs (hiduke, comment) VALUES ('$hiduke', '$comment')");

　　// 件数を取得し直す
    $stmt = $db->query("SELECT COUNT(*) FROM bbs");
    $kensu = $stmt->fetchColumn();
    
　　// list.dbの件数、更新日時を更新しておく（これで掲示板リスト画面で一番上に表示されるようになる）
　　$db = new PDO("sqlite:db/list.db");
　　$db->exec("UPDATE list SET kensu=$kensu, updtime=(SELECT datetime('now', '+09:00:00')) WHERE dbn
ame='$dbname'");
    $db = null;
    $html .= "投稿しました。<br><br>";
  }
} else {
  header("Location: list.php");
}

// アンカーをリンク付きに変換する
function anchor($comment) {
  $pattern = "/>>([0-9])+/";
  $replace = '<a href="link.php?num=$0">$0</a>';
  $comment = preg_replace($pattern, $replace, $comment);
  return $comment;
}

// 改行数をカウントする
function check_br($comment) {
  $brcount = mb_substr_count($comment, "<br />");
  return $brcount;
}

// 文字数をカウントする
function check_strcount($comment) {
  $strcount = strlen($comment);
  return $strcount;
}

?>
<!DOCTYPE html>
<html lang="ja">
<head>
<title>サンプル掲示板</title>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1.0">
<link rel="stylesheet" href="style.css" media="all">
</head>
<body>
<?php echo $html; ?>
<a href=list.php>掲示板に戻る</a>
</body>
</html>
```


```link.php
// アンカーをクリックしたときにそのコメントを別ページに表示
<?php
if (isset($_GET["dbname"]) && isset($_GET["num"])) {
  $dbname = htmlspecialchars($_GET["dbname"]);
  $num = htmlspecialchars($_GET["num"]);
  $num = substr($num, 8);
  $name = "sqlite:db/".$dbname.".db";
  $db = new PDO($name);
  $stmt = $db->query("SELECT * FROM bbs WHERE id=$num");
  $html = "";
  while ($row = $stmt->fetch()) {
    $html .= $row["id"].":".$row["hiduke"]."<br>".$row["comment"];
  }
  $db = null;
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<title>サンプル掲示板</title>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1.0">
<link rel="stylesheet" href="style.css" media="all">
</head>
<body>
<?php echo $html; ?>
</body>
</html>
```
```build.php
// 掲示板の追加処理
<?php
if (isset($_POST["newbbs"])) {
  $newdbname = htmlspecialchars($_POST["newdbname"], ENT_QUOTES);
  $newdbnamekana = htmlspecialchars($_POST["newdbnamekana"], ENT_QUOTES);
  $db = new PDO("sqlite:db/list.db");
  $db->exec("INSERT INTO list (dbname, dbnamekana) VALUES ('$newdbname', '$newdbnamekana')");
  $name = "db/".$newdbname.".db";
  $db = new SQLite3($name);
  $db->exec("CREATE TABLE bbs (id INTEGER PRIMARY KEY, hiduke TEXT, comment TEXT)");
  $db = null;
  echo "新しい掲示板「".$newdbnamekana."」を作りました。<br><br>";
  echo "<a href='list.php'>掲示板へ戻る</a>";
}
?>
```

```del.php
// 掲示板の削除処理
<?php
if (isset($_GET["dbname"])) {
  $dbname = htmlspecialchars($_GET["dbname"], ENT_QUOTES);
  $db = new PDO("sqlite:db/list.db");
  $db->exec("DELETE FROM list WHERE dbname='$dbname'");
  $name = "db/".$dbname.".db";
  // ファイルごとにスレがあるのでファイルごと消せばいい
  unlink($name);
  echo "削除しました。<br><br>";
  echo "<a href='list.php'>掲示板へ戻る</a>";
}
?>
```
```style.css
body {
  // background-color: #D3D3D3;
  background-color: #F5F5F5;
}

#comment {
  margin-left: 35px;
}
```

# 最後に
いろいろと説明が下手くそで申し訳ありません。より本格的に作るためにはクッキーやセッション、正規表現などを駆使してある程度の荒らし対策が必要になってくるかと思います。