PHPのPDOでMySQLデータベースのデータを更新する2(投稿内容の変更)

前回の続き。詳細ページの編集ボタンから、編集ページに移動できるようにした。移動の際に、編集・削除キーが合っているか確認している。また、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)
小川 雄大 柄沢 聡太郎 橋口 誠
技術評論社
売り上げランキング: 24,082

関連エントリー

  1. PHPのPDOでMySQLデータベースのデータを削除する(投稿の削除)
  2. PHPでフォームの値をMySQLデータベースに格納する
  3. PHPのPDOでMySQLデータベースのデータを取得する2
  4. PHPのPDOでMySQLデータベースのデータを更新する(アクセスカウンタ)
  5. (PHP/MySQL)ファイル操作、データベース更新メモ
This entry was posted in 未分類 and tagged , . Bookmark the permalink.