【この記事を読むのに必要な時間は約 14 分です。】
ドットインストールでPHPの勉強をしている。ドットインストールのPHP関連で用意されている動画レッスンを終えた後に、DEMOを見ただけでスクリプトが書けるようになるための練習。今回は、ドットインストールの動画レッスン「PHPで作る簡単クイズ」-PHP(5)に続き6回目。
最初にDEMOと考え方のヒントを提示し、それだけで実際にスクリプトを書いてみよう。スクリプトを書き終わったら、完成版と見比べて答え合わせができるようにした。
今回は、PHPで作る簡易掲示板 (全9回)から。
目次
Step1
今回は最初から完成版のDEMOを見るのではなく、3ステップで作っていこう。まずは、図のようにhtml部分を完成させてみよう。
ポイントとヒント
- html5形式のひな形で作ろう。
- 使用タグ h1 / h2 / form / input / label / ul / li
Step1の答え
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>簡易掲示板</title>
</head>
<body>
<h1>簡易掲示板</h1>
<form action="" method="post">
<label>message:<input type="text" name="message"></label>
<label>user:<input type="text" name="user"></label>
<input type="submit" value="投稿" >
</form>
<h2>投稿一覧(件)</h2>
<ul>
<li>まだ投稿はありません。</li>
</ul>
</body>
</html>
Step2
それでは、次に、phpスクリプトを書いていこう。まずは、「bbs.dat」(パーミッションは666に設定)という読み書き用ファイルを同じディレクトリに作成して、そこに書き込めるようにしていくところまで。
同時にエラーチェックと投稿時間もわかるようにしよう。
ポイントとヒント
- bbs.datを指定。message / user / 投稿時間(postedAt)を設定。
- POSTされたらファイルに書き込めるように。/ $_SERVER[‘REQUEST_METHOD’]
- bbs.datへの書き込み。 / fp / fopen() / fwrite() / fclose()
- 書き込みデータはmessage / user / 投稿時間(postedAt)をタブ(\t)で区切って格納しよう。
- POSTされる内容自体にタブ(\t)があった場合の処理。/ str_replace()
- POSTされる内容に前後の空白がないようにしよう。 / trim()
- エラーチェックをしよう。 / $_POST isset()
- エラーチェックをしよう。 / messageが空の場合
- userが空の場合は「ななし」と表示されるようにしよう。
Step2の答え
<?php
// ファイルの存在チェックは今回省略
$dataFile = 'bbs.dat';
//投稿POSTされたらdatファイルに書き込む
if ($_SERVER['REQUEST_METHOD'] == 'POST'
// エラーチェック(1)
&& isset($_POST['message'])
&& isset($_POST['user'])) {
// 前後の空白をトリム trim()
$message = trim($_POST['message']);
$user = trim($_POST['user']);
// エラーチェック(2) メッセージがあるかどうか
if ($message !== '') {
// 名前が空欄の場合
$user = ($user === '') ? 'ななしさん' : $user;
// タブが使われていると困るのでタブをスペースに置き換え
$message = str_replace("\t", ' ', $message);
$user = str_replace("\t", ' ', $user);
// 投稿時間
$postedAt = date('Y-m-d H:i:s');
// タブで区切って message / user / 投稿時間
$newData = $message . "\t" . $user . "\t" . $postedAt . "\n";
// 書き込み処理
$fp = fopen($dataFile, 'a');
fwrite($fp, $newData);
fclose($fp);
}
}
?>
Step3-完成させてみよう
ポイント
- エスケープ処理をしよう。
- CSRF対策をしよう。
- 二重送信防止をしよう。
- 投稿(message / user / postedAt)が表示されるようにしよう。
- fle()関数を使おう。
- 読み込みは書き込んで保存したのと順番を逆にしよう。 / array_reverse
- list() / explode() 関数を使ってタブ(\t)区切りを利用して書きだそう。
- 投稿がある場合、ない場合はif文で。 / count($posts)が0なら。
- foreach文で全ての投稿を表示しよう。
Step3-完成したスクリプト
<?php
$dataFile = 'bbs.dat';
// CSRF対策
session_start();
function setToken() {
$token = sha1(uniqid(mt_rand(), true));
$_SESSION['token'] = $token;
}
function checkToken() {
if (empty($_SESSION['token']) || $_SESSION['token'] != $_POST['token']) {
echo "不正なPOSTをが行われました";
exit;
}
}
// エスケープ
function h($s) {
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
// 二重送信防止
function redirect() {
header('Location: http://' .$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
exit;
}
// POSTされたメッセージ、ユーザー名のファイルへの書き込みと整形後の格納
if ($_SERVER['REQUEST_METHOD'] == 'POST' &&
isset($_POST['message']) &&
isset($_POST['user'])) {
checkToken(); //投稿された後
$message = trim($_POST['message']);
$user = trim($_POST['user']);
if ($message !== '') {
$user = ($user === '') ? 'ななしさん' : $user;
$message = str_replace("\t", ' ', $message);
$user = str_replace("\t", ' ', $user);
$postedAt = date('Y-m-d H:i:s');
$newData = $message . "\t" . $user . "\t" . $postedAt . "\n";
$fp = fopen($dataFile, 'a');
fwrite($fp, $newData);
fclose($fp);
}
redirect();
} else {
setToken(); //投稿される前
}
$posts = file($dataFile, FILE_IGNORE_NEW_LINES);
$posts = array_reverse($posts);
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>簡易掲示板</title>
</head>
<body>
<h1>簡易掲示板</h1>
<form action="" method="post">
message:<input type="text" name="message" >
user:<input type="text" name="user" >
<input type="submit" value="投稿" >
<input type="hidden" name="token" value="<?php echo ($_SESSION['token']); ?>" >
</form>
<h2>投稿一覧(<?php echo count($posts); ?>件)</h2>
<ul>
<?php if(count($posts)) : ?>
<?php foreach ($posts as $post) : ?>
<?php list($message, $user, $postedAt) = explode("\t", $post); ?>
<li><?php echo h($message); ?><?php echo h($user); ?><?php echo h($postedAt); ?> </li>
<?php endforeach; ?>
<?php else : ?>
<li>まだ投稿はありません。</li>
<?php endif; ?>
</ul>
</body>
</html>
PHPマニュアルを見よう
最初は何を書いているかわからないけど、とにかく疑問点があったらPHPマニュアルを見る癖をつけよう。
file()
file — ファイル全体を読み込んで配列に格納する
array file ( string $filename [, int $flags = 0 [, resource $context ]] )
ファイル全体を配列に読み込みます。
パラメータ:filename / ファイルへのパス。
flags / オプションのパラメータ flags は、以下の定数のうちのひとつ、あるいは複数の組み合わせとなります。
FILE_USE_INCLUDE_PATH / include_path のファイルを探します。
FILE_IGNORE_NEW_LINES / 配列の各要素の最後に改行文字を追加しません。
FILE_SKIP_EMPTY_LINES / 空行を読み飛ばします。
返り値 / ファイルを配列に入れて返します。 配列の各要素はファイルの各行に対応します。改行記号はついたままとなります。 失敗すると file() は FALSE を返します。
array_reverse()
array_reverse — 要素を逆順にした配列を返す
array array_reverse ( array $array [, bool $preserve_keys = false ] )
配列を受け取って、要素の順番を逆にした新しい配列を返します。
パラメータ:
array / 入力の配列。
preserve_keys / TRUE の場合は数値のキーを保持します。 非数値のキーは、この設定にかかわらず常に保持されます。
返り値 / 逆転させた配列を返します。
list()
list — 配列と同様の形式で、複数の変数への代入を行う
array list ( mixed $var1 [, mixed $… ] )
array() と同様に、 この関数は実際には関数ではなく言語構造です。 list() は、 単一の操作で一連の変数に値を代入するために使われます。
パラメータ:var1 / 変数。
返り値 / 代入した配列を返します。
explode()
explode — 文字列を文字列により分割する
array explode ( string $delimiter , string $string [, int $limit ] )
文字列の配列を返します。この配列の各要素は、 string を文字列 delimiter で区切った部分文字列となります。
パラメータ:
delimiter / 区切り文字列。
string / 入力文字列。
limit / limit に正の値が指定された場合、返される配列には 最大 limit の要素が含まれ、その最後の要素には string の残りの部分が全て含まれます。
もし limit パラメータが負の場合、 最後の -limit 個の要素を除く全ての構成要素が返されます。
limit パラメータがゼロの場合は、1 を指定したものとみなされます。
返り値 / string の内容を delimiter で分割した文字列の配列を返します。
空の文字列 (“”) が delimiter として使用された場合、 explode() は FALSE を返します。delimiter に引数 string に含まれていない値が含まれている場合は、 limit が負の値なら空の配列、そうでなければ 引数 string を含む配列を返します。
注意:歴史的理由により、implode() はいずれのパラメータ順も受け入れることができますが、 explode() はそうできません。 string 引数の前に必ず delimiter 引数がくるように確認する必要があります。
なお、sha1(uniqid(mt_rand(), true)) / header() 等の過去のブログで記事にしたものは割愛している。
以上
コメント
はじめまして。ライブドアブログのカスタマイズについてお聞きしたいことがあるのですが大丈夫でしょうか?
もしよろしければよろしくお願いします。