前回の続き。詳細ページの編集ボタンから、編集ページに移動できるようにした。移動の際に、編集・削除キーが合っているか確認している。また、htmlspecialchars()でエスケープする処理やデータベース接続などよく使う処理を別ファイル(function.php)にまとめてincludeするようにした。
成果物:index.html -> item.php (詳細ページに編集ボタンを追加) -> edit.php (編集ページ)
メモ
- 編集ページ(edit.php)は、詳細ページから移動してきた場合と、編集が終わって送信ボタンを押して移動してきた場合とに分けてる。
if ($_SERVER['REQUEST_METHOD'] == 'GET') { // 詳細ページから移動してきた場合 } else { // 編集が終わって送信ボタンを押して移動してきた場合 } - 編集ページ(edit.php)で送信ボタンを押してデータベースに反映させる前に、sha1()で生成したランダムな文字列で、フォームから送信したものとサーバーで保存したもの(セッション)と一致するか確認している。(クロスサイトリクエストフォージェリ:CSRF対策)
- データベース処理をするときはプリペアドステートメントを使うといいみたい。エスケープ処理を勝手にやってくれるから。
- edit.phpに表示しているフォームはデータベースの内容を反映させている。ただ、select要素の選択状態を実現できていない(どうやるかわかってない。)
- フォームのテキストは、1行ならinput type=”text”、2行以上ならtextarea rows=”xx” cols=”xx”で。
ソースコード
item.php
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>スマホキャプチャーギャラリー</title>
</head>
<body>
<a href="index.html">Home</a> | <a href="upload.html">アップロード</a> | <a href="ranking.html">人気順</a> | <a href="latest.html">新着順</a>
<?php
require_once('config.php');
require_once('function.php');
$dbh = connect_db();
$id = $_GET['id'];
// アクセスカウンタをインクリメント
$sql = sprintf("update mobile_capture_showcase set counter=counter+1 where id=%d", $id);
$dbh->query($sql);
// 該当IDの画像情報を表示
$sql = sprintf("select * from mobile_capture_showcase where id=%d", $id);
foreach ($dbh->query($sql) as $item) {
$path = "./images/" . $item['filename'];
echo sprintf(
'<table>
<tr>
<td>
<img src="%s" alt="image" width="320" />
</td>
<td>
<table>
<tr>
<td>名前:%s</td>
</tr>
<tr>
<td>投稿時刻:%s</td>
</tr>
<tr>
<td>OS:%s</td>
</tr>
<tr>
<td>アクセス数:%s</td>
</tr>
<tr>
<td>コメント:%s</td>
</tr>
<tr>
<td><form action="edit.php" action="get">
<input type="hidden" name="id" value=%d />
<input type="text" name="updelkey" size="20" /><input type="submit" value="編集" />・削除
</form></td>
</tr>
</table>
</td>
</tr>
</table>',
$path,
$item['name'], $item['time'], $item['os'], $item['counter'],
h($item['comment']),
$id);
}
$dbh = null;
?>
</body>
</html>
edit.php
<?php
require_once('config.php');
require_once('function.php');
session_start();
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
// 編集前(編集フォーム表示)
// 編集・削除キーのチェック
// 一致しない場合、キーが誤っている旨を表示する
if (check_updelkey($_GET['id'], $_GET['updelkey']) == false) {
echo "編集・削除キーが誤っています";
exit;
}
// CSRF対策
set_token();
}
else {
// 編集後(データベース更新)
if (check_token() != true) {
echo "不正な処理です";
exit;
}
// *** フォームの情報をデータベースに格納 ***
$dbh = connect_db();
$statement = $dbh->prepare("update mobile_capture_showcase set name=:name, os=:os, comment=:comment, updelkey=:updelkey where id=:id");
$statement->bindParam(":id", $_POST['id']);
$statement->bindParam(":name", $_POST['name']);
$statement->bindParam(":os", $_POST['os']);
$statement->bindParam(":updelkey", $_POST['updelkey']);
$statement->bindParam(":comment", $_POST['comment']);
$statement->execute();
$dbh = null;
// 元の画像ページに戻る
header('Location: http://' . $_SERVER['SERVER_NAME'] . '/study/uploader/mobile_capture_showcase/item.php?id=' . $_POST['id']);
exit;
}
?>
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>スマホキャプチャーギャラリー</title>
</head>
<body>
<a href="index.html">Home</a> | <a href="upload.html">アップロード</a> | <a href="ranking.html">人気順</a> | <a href="latest.html">新着順</a>
<p>編集画面</p>
<?php
$dbh = connect_db();
$statement = $dbh->prepare("select * from mobile_capture_showcase where id=:id");
$statement->bindParam(":id", $_GET['id']);
$statement->execute();
$item = $statement->fetch(PDO::FETCH_ASSOC);
$dbh = null;
$path = "./images/" . $item['filename'];
$form = sprintf('
<form action="" method="post" enctype="multipart/form-data">
<input type="hidden" name="id" value=%d />
<input type="hidden" name="token" value="%s" />
<table>
<tr>
<td>
<img src="%s" alt="image" width="320" />
</td>
<td>
<table>
<tr>
<td>名前:<input type="text" name="name" size="40" value="%s" /></td>
</tr>
<tr>
<td>投稿時刻:%s</td>
</tr>
<tr>
<td>OS:<select name="os">
<option value="default">選択してください</option>
<option value="iPhone">iPhone</option>
<option value="Android">Android</option>
</select></td>
</tr>
<tr>
<td>アクセス数:%d</td>
</tr>
<tr>
<td>コメント:<textarea id="" name="comment" rows="10" cols="30">%s</textarea></td>
</tr>
<tr>
<td><input type="text" name="updelkey" size="20" value="%d" />編集・削除キー</td>
</tr>
<tr>
<td><input type="submit" value="送信" /></td>
</tr>
</table>
</td>
</tr>
</table>
</form>
', h($_GET['id']), h($_SESSION['token']), $path,
$item['name'], $item['time'], $item['counter'],
h($item['comment']), $item['updelkey']);
echo $form;
?>
</body>
</html>
function.php
<?php
function h($s) {
return htmlspecialchars($s, ENT_QUOTES, "UTF-8");
}
function connect_db() {
try {
return new PDO(DSN, DB_USER, DB_PASS);
} catch (PDOException $e) {
echo $e->getMessage();
exit;
}
}
function check_updelkey($id, $updelkey) {
// IDのフォーマット確認
if (!preg_match('/^[1-9][0-9]*$/', $id)) {
return false;
}
// 編集・削除キーのフォーマット確認
if (!preg_match('/[0-9][0-9][0-9][0-9]/', $updelkey)) {
return false;
}
// 編集・削除キーが該当IDのキーと一致するか確認
$dbh = connect_db();
$statement = $dbh->prepare("select * from mobile_capture_showcase where id=:id");
$statement->bindParam(":id", $id);
$statement->execute();
$item = $statement->fetch(PDO::FETCH_ASSOC);
$dbh = null;
if ($updelkey == $item['updelkey']) {
return true;
}
else {
return false;
}
}
function set_token() {
if (!isset($_SESSION['token'])) {
$_SESSION['token'] = sha1(uniqid(mt_rand(), true));
}
}
function check_token() {
if (empty($_POST['token']) ||
$_POST['token'] != $_SESSION['token']) {
return false;
}
else {
return true;
}
}
?>
参考サイト
パーフェクトPHP (PERFECT SERIES 3)
posted with amazlet at 13.01.30
小川 雄大 柄沢 聡太郎 橋口 誠
技術評論社
売り上げランキング: 24,082
技術評論社
売り上げランキング: 24,082
関連エントリー
