アプリ版:「スタンプのみでお礼する」機能のリリースについて

いつもお世話になります。
類似する質問も見つけられず、いろいろ試して悩んだ挙句、アプローチを変えてもう一度投稿することにしました。
発端は、下記のページです。
http://oshiete.goo.ne.jp/qa/8604578.html

問題は、phpmyadminからカラムまたはフィールドに入力したものは重複していればエラー表示されるのですが、PHPのフォームからMYSQLのデータベースに保存したもの(phpmyadmin上ではちゃんと登録されている)は重複とみなされず、エラーとして検出されません。

phpフォームからMYSQLのデータベースを呼び出してカラムまたはフィールド上に同じものがなければ保存、そうでなければエラー表示、といった形で重複させないプログラムを作成するつもりでした。

上記のページでご返信いただいたように、重複させないカラムにユニーク設定してみたのですが、問題は解決されませんでした。

文字コードが違うからと思ったのですが、MYSQL、PHPどちらもUTF-8で統一させています。

フォームからMYSQLへの保存時に問題があるのでは?と思うのですが、どのような形で修正すればうまくいくのか、わかりません。

どうか、知識の足りない私にご教授いただけないでしょうか?

PHPの設定とMYSQLへの登録のプログラムは下記の通りです。

PHP・・・5.4.19
XAMPP・・・1.8.2
phpmyadmin・・・4.0.4.1

データベース文字コード(UTF-8)
phpの文字コード(php.iniのdefault_characterをUTF-8に設定)

<?php
//データベースの接続設定
$DB_HOST = "localhost";
$DB_NAME = "○○";
$DB_USER = "○○";
$DB_PASS = "○○";

//データベースに接続する
$conn = mysql_connect($DB_HOST,$DB_USER,$DB_PASS) or die("接続エラー");
mysql_select_db($DB_NAME) or die("接続エラー");

       //データベース登録前にメール送信する
$userID = $_POST['userID'];
$password = $_POST['password'];
$name = $_POST['name'];
$mail1 = $_POST['mail1'];

$userID = htmlspecialchars($userID);
$password = htmlspecialchars($password);
$name = htmlspecialchars($name);
$mail1 = htmlspecialchars($mail1);

$mail_sub = '登録を受け付けました。';
$mail_body = 'ご登録、誠にありがとうございました。';
$mail_body = html_entity_decode($mail_body,ENT_QUOTES,"UTF-8");
$mail_head = 'From:blowin@horae.dti.ne.jp';
mb_language('Japanese');
mb_internal_encoding("UTF-8");
mb_send_mail($mail1, $mail_sub, $mail_body,$mail_head);
?>

<?php
//デフォルトのタイムゾーンを日本に設定
date_default_timezone_set("Japan");

//フォーム情報をすべて受信
extract($_POST);

//そのまま代入する形ではエラーが起きるため、isset関数を使ってエラー回避する

if(isset($_POST['$userID'])){
$userID = cnv_dbstr($_POST['$userID']);
}
if(isset($_POST['$password'])){
$password = cnv_dbstr($_POST['$password']);
}
if(isset($_POST['$name'])){
$name = cnv_dbstr($_POST['$name']);
}
if(isset($_POST['$mail1'])){
$mail1 = cnv_dbstr($_POST['$mail1']);
}

//データを追加する
$sql = "INSERT IGNORE INTO touroku(userID,password,name,email,date)";
$sql .= "VALUES( " ;
$sql .= " ' " .$userID. " ', " ;
$sql .= " ' " .$password. " ', " ;
$sql .= " ' " .$name. " ', " ;
$sql .= " ' " .$mail1. " ', " ;
$sql .= " ' " .date("Y/m/d H:i:s") . " ' " ;
$sql .= " ) " ;
$res = mysql_query($sql,$conn) or die("データ追加エラー");

//SQLコマンド用の文字列に変換する関数
function cnv_dbstr($string){

//タグを無効にする
$string = htmlspecialchars($string);

//magic_quotes_gpcがOnの場合はエスケープを解除する
if(get_magic_quotes_gpc()){
$string = stripslashes($string);
}

//SQLコマンド用の文字列にエスケープする
$string = mysql_real_escape_string($string);
return $string;
}

header('location:./testkanryou.php');
exit;
?>

大変申し訳ありませんが、どうか、よろしくお願い申し上げます。

A 回答 (2件)

私なりに書き直してみました。



Pastebin.com - V8pST9nc
http://pastebin.com/V8pST9nc

もしかしてINSERT文に「IGNORE」をつけているのが原因じゃないですか…?このオプションはユニークキーがダブってたときにエラーを出さなくするためのものですね。
    • good
    • 0
この回答へのお礼

ご連絡ならびにご報告が大変遅くなって申し訳ありません。

まだ、ご回答にありましたページをしっかりと読み込めてないので、とりあえず書き直していただいたプログラムで動かしてみたところ、MYSQLへの保存とフォームからの入力時に重複を確認し、エラーとして表示することができました。
有難うございました。

INSERT文の「IGNORE」は、質問にあるページでそのようにしてみたら?と回答がありました。
性質は少し調べてある程度は理解してましたが、親切に教えていただいたこともありますし、藁をもすがる気持ちで試してみていました。

しかし、元々は「IGNORE」をつけていませんでしたので、今もそうですが、なぜ同じ文字列で登録されているにもかかわらず、重複と認識しなかったのか、また、var_dump()で中身が入っていなかったのか、理由がわかりません。
MYSQLには保存できる、でも重複として認識しない。保存する文字コードが違う可能性くらいしか思い浮かびませんでした。

書き直していただいたプログラムを有効に活用させていただくために、他のプログラムも手続き型からオブジェクト型にしようと思います。

繰り返しになってしましますが、本当に有難うございました。

お礼日時:2014/05/30 02:41

気が遠くなるほど指摘すべき内容がありますが…(汗)さて、まず回答に入る前に前提としてもらいたい内容があるので、先に以下の記事をお読みください。



Qiita - $_GET, $_POSTなどを受け取る際の処理
http://qiita.com/mpyw/items/2f9955db1c02eeef43ea

Qiita - PHPでデータベースに接続するときのまとめ
http://qiita.com/mpyw/items/b00b72c5c95aac573b71

オブジェクト指向についての理解が足りない場合はこちらもどうぞ。今まさに書いている最中で未完成ですが…

Qiita - PHPオブジェクト指向入門
http://qiita.com/mpyw/items/41230bec5c02142ae691

手当たり次第になりますが、修正すべき個所を指摘していきます。

1.
全てのエラーメッセージを表示させる設定にしましょう。これを行っておかないとデバッグ作業の過程で大きな障壁になります。(既に設定済みの場合はスルーしてください)

2.
「mysql_*」に該当する関数は全て非推奨なので使わないでください。現在は「PDO」がよく用いられます。

3.
「or die("エラー")」だと原因が分からないので、mysql関数を使用するにしてもせめて「or die(mysql_error($conn))」としましょう。PDOを用いる場合は毎回こんな面倒なこと書かなくても「例外」という形で面倒見てくれてとてもラクです。

4.
POSTで値を受け取る際にはfilter_input関数を使いましょう。

5.
htmlspecialchars関数でエスケープするのはprintやechoで表示する直前にしましょう。あらかじめエスケープを行っておいてはいけません。hという名前のラッパー関数を作成しておくことを推奨します。

6.
SQL文中に安全に埋め込むためのエスケープ(mysql_real_escape_string)とHTMLとして安全に表示させるためのエスケープ(htmlspecilchars)は全く内容が異なります。「とりあえずどっちも行っておけば大丈夫」という横暴なことはせず、必要なものだけを必要なときに使用してください。

7.
extract($_POST) はやってはいけないとしてextract関数のマニュアルで警告されています。filter_input関数と可変変数への代入のループをうまく使って対応しましょう。

8.
PDOを使う場合、ユーザー入力をSQL文に埋め込む際にはプレースホルダを使用してください。PHPでエスケープしたものを変数展開して埋め込むことは非推奨とされています。

9.
現在の環境ではマジッククオート機能はほとんどOFFとなっていると思うので、stripslashesを通すかどうかのチェックはいらないと思います。…あ、PHP5.4ならOFFどころかもう削除されていてONにすることすらできませんね。


さて…直接の原因ですが…PDOに乗り換えたときに PDO::ATTR_ERRMODE オプションを PDO::ERRMODE_EXCEPTION にしておけばそれだけですぐ判明するので、かなり労力はかかると思いますがmysql関数からPDOへの書き換えを行ってみてください。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!