こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

プリペアードステートメントへの変更

PHP、MySQL初級者です。
ある商品を検索するサイトを運営していますが、SQLインジェクション対策がなされておらず、mysql_queryで書いていたものを、プリペアドステートメントに初挑戦しています。

元々作ったプログラムを、ネットの情報や本を参考に修正していますが、検索をすると画面が真っ白になってしまう現象から2日間抜け出せずにいます。
どなたかご教授いただけますでしょうか。PHPのバージョンは5.2、Windows環境です。

ちなみに、$display_fromはページネーションに関係するものです。
また、//でコメントアウトしている箇所は一旦PEAR::DBをつかうやりかたに修正したなごりです。
また、rs2のところは検索結果のヒット数を表示させるためにわざわざ全件を取得しています。(このやり方自体スマートではない感じがしていますが。。)


/*POSTでkeywordを取得*/
if($_POST['keyword']!=""){
$keyword=$_POST['keyword'];
}
elseif($_GET['pageID']!=""){
$keyword=$_SESSION['keyword'];
}
else{
}
//ログイン情報をインクルードする
include('db_login.php');

//PEAR DBの機能が含まれるDB.phpもインクルードする
require_once("DB.php");

//接続する
$connection = DB::connect("mysql://$db_username:$db_password@$db_host/$db_database");
if(DB::isError($connection)){
die("Could not connect to the database: <br />".DB::errorMessage($connection));
}
//$db = mysql_connect("localhost", "root", "root")or die ('DB Connection Error= '. mysql_error());
//mysql_select_db("book_db", $db)or die ('DB Connection Error= '.mysql_error());
//$rs = mysql_query("SET character set UTF8", $db);
//$rs2 = mysql_query("SET character set UTF8", $db);

/*keywordが空白だった場合*/
if($keyword==""){
$query="SET character set UTF8";
$rs=$connection->query($query);
$query="select * from book_card order by point desc limit $display_from, 50";
$rs=$connection->query($query);
if(DB::isError($rs)){
die("Could not query the database:<br />$query ".DB::errorMessage($rs));
}

$query2="SET character set UTF8";
$rs2=$connection->query($query2);
$query2="select * from book_card";
$rs2=$connection->query($query2);
if(DB::isError($rs2)){
die("Could not query the database:<br />$query2 ".DB::errorMessage($rs2));
}

/*もともとのコード
$rs=mysql_query("select * from book_card order by point desc limit $display_from, 50", $db);
$rs2=mysql_query("select * from book_card", $db);
*/
}

/*keywordに何か入っていた場合*/
else{
$query="SET character set UTF8";
$rs=$connection->query($query);

//$query="select * from book_card where Name like '%$keyword%' order by point desc limit $display_from, 50";
//$rs=$connection->query($query);
$query = "select * from book_card where Name like ? order by point desc limit ?, 50";
$rs = $connection->prepare($query);
$data = array("%$keyword%", $display_from);
$connection->execute($rs, $data);

$query2="SET character set UTF8";
$rs2=$connection->query($query2);

//$query2="select * from book_card where Name like '%$keyword%'";
//$rs2=$connection->query($query2);
$query2 = "select * from book_card where Name like ?";
$rs2 = $connection->prepare($query2);
$data2 = "%$keyword%";
$connection->execute($rs2, $data2);


/*もともとのコード
$rs=mysql_query("select * from book_card where Name like '%$keyword%' order by point desc limit $display_from, 50", $db);
rs2=mysql_query("select * from book_card where Name like '%$keyword%'", $db);
*/
}

分かりにくい質問ですみませんが、どなたかお分かりの方ご教授いただけますでしょうか。
何卒宜しくお願い致します。

投稿日時 - 2013-11-18 23:05:46

QNo.8352943

すぐに回答ほしいです

質問者が選んだベストアンサー

まず2点ほど。

「PEAR::DBを使わない」

Mysql関数と同様にPEAR::DBクラスも非推奨です。PEAR系統ならばPEAR::MDB2が代替の役割を担いますが、個人的にはこちらも非推奨とさせていただきたいところです。PEARのクラスって所詮はPHPの関数を利用しているだけなんですよね。PHPで書かれたコードなので動作が遅いのです。それよりはMysqliクラスやPDOクラスといったC言語レベルで組み込まれているものを使う方が得策です。下記にて詳しく解説しています。

「PHPのバージョンを上げる」

レンタルサーバーなどでやむを得ない事情があるなら分かりますが、Windowsということはご自分で管理されているコンピュータですよね?そうであればとても古いPHP5.2を使うのは避けるべきではないでしょうか。言語自体に致命的なバグがたくさんあります。まともに使えるようになったのはPHP5.4.1以降ですね。ちなみに最新版はPHP5.5.6です。

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

最終的に私がオススメしたいのはPDOです。上記リンクと併せてマニュアルもぜひお読みください。

PHP Manual - PHP Data Objects
http://php.net/manual/ja/book.pdo.php

Try~Catchブロックをどこに設けるかについてですが、これはラッパークラス・ラッパー関数側ではなく、それらを呼び出す側に設けることを強く推奨します。

PHP Manual - 例外(exceptions)
http://php.net/manual/ja/language.exceptions.php

【ダメな例】
http://dotinstall.com/lessons/sns_php_v2/9604
初心者に分かりやすくするために例外を扱う範囲をインスタンスを作るラッパー関数だけにとどめていますが、これだと古典的なエラー処理方法に依存することになり、ソースコードが汚くなりやすくなります。

【工夫した例】
https://github.com/Certainist/sns_php
インスタンス生成時の例外に加え、PDOのエラーモードをPDO::ERRMODE_EXCEPTIONに設定することによって、SQL実行において発生したエラーも例外としてスローするようにし、呼び出し側のCatchブロックで全ての例外を捕まえるように工夫しています。

上記スクリプトでは下記のテクニックも利用しています。

Qiita - まとめて例外をスローする小技
http://qiita.com/mpyw/items/6bd99ff62571c02feaa1

LIKE検索をプリペアドステートメントと併せて使う場合は下記の例4を参考にしてください。正しく「%」「_」「\」などの記号も扱えるようにするためにはaddcslashes関数によるエスケープが必須です。

Qiita - 汎用的な変数構造フィルタリング関数
http://qiita.com/mpyw/items/c39b9ee695a5c2e74627

LIMIT句を用いたページング処理に関しては下記を参考にしてください。

COUNT(*) の代わりに FOUND_ROWS() を利用する
http://private.ceek.jp/archives/002864.html

Qiita - ページング処理を採り入れた掲示板サンプル
http://qiita.com/mpyw/items/4ab192e04430119d5599

投稿日時 - 2013-11-19 00:19:26

お礼

多岐にわたる情報ありがとうございます。バージョンについては、確かに古いままなので、作業がひと段落したら上げようと思います。早速PDOについて勉強を始め、すこしずつわかってきました。試しに書いたプログラムではまだ画面が真っ白になってしまってしまうので、サンプルのプログラムを作るなどして一つ一つ確証を得ながら学習しようと思います。いただいた情報を全て理解するには時間がかかりそうですが、じっくり取り組んでみようと思います。

投稿日時 - 2013-11-20 11:18:39

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(1)

あなたにオススメの質問