cakephperの日記(CakePHP, Laravel, PHP) Twitter


継続的WebセキュリティテストサービスVAddyを始めました!

2009-04-17

意外と知られていない? queryメソッドで値をbindできるってこと

CakePHP 1.2.2を利用してます。この話は1.2だったらどれでも適用できると思います(cake1.1は分かりません)

ーーーーーーーーーーーーーーーーー

CakePHP2であれば、Model::query()が、擬似bindではなくPrepared StatementでSQL発行するので安心です

http://d.hatena.ne.jp/cakephper/20120204/1328324327

ーーーーーーーーーーーーーーーーー

集計用SQLなど、SQLが複雑になったりする場合や、SQL文を直接書いたほうが開発効率が上がる個所に関しては、Model::query()を利用して、下記のように直接SQL文を発行してます。

$this->Model->query("SELECT `Post`.`id` FROM `posts` AS `Post` WHERE `Post`.`id` = 100");

実はqueryメソッドは、第2引数配列を与えると、値をbindしてくれます。(SQL文の中でbindさせたい個所は?にしておきます)

$sql = "SELECT `Post`.`id` FROM `posts` AS `Post` WHERE `Post`.`id` = ? LIMIT ?";
$this->Model->query($sql, array(100,1));

こうすると、下記のSQL文が発行されます

SELECT `Post`.`id` FROM `posts` AS `Post` WHERE `Post`.`id` = 100 LIMIT 1

変数をダイレクトにSQL文に埋め込む場合、エスケープ処理などをし忘れると大変ですが、このbindの機能を使っておけば、引数で与えた配列の値は自動でエスケープ処理してくれるので、安心です。


bindしてくれる話って、CakeBookにも書いてないしAPIにも書いてないんだけどw



追記2

ここに、queryメソッドを使うとデフォルトではキャッシュされた結果が利用されるとありました。バインドする場合で、キャッシュを効かせないようにするためには、第3引数にfalseを指定すればOK(1.2.2のソースを読む限りはそれでいける)。

$sql = "SELECT `Post`.`id` FROM `posts` AS `Post` WHERE `Post`.`id` = ? LIMIT ?";
$this->Model->query($sql, array(100,1), false);


追記

miauさんから下記の意見を頂きました。ありがとうございます。

「bind できる」っていうと DB のバインド機構が使われると思われてしまうような・・・。
ソースちゃんと読んでませんけど、
CakePHPプレースホルダを自前で置換してる擬似バインド機構ですよね?
http://twitter.com/miau_jp/status/1539713129

結論は、Cakeが自前でやってる擬似バインドです。



ちょっとソースを追ってみました。

まず、Model::query()を呼び出すと、DboSource::query()に渡されます。ここで第1引数しかない場合(SQL文のみ)は、DboSource::fetchAllメソッドが呼ばれてSQLが実行されます。


今回のように、第2引数配列の場合は、その配列に対してvalueメソッドが呼ばれて(呼ばれるのは、MySQLであればDboMysql::value()とか)、値のエスケープ処理(mysql_real_escape_string)が実行されます。そのエスケープ処理された配列を、String::insert()に渡して、擬似的にバインドさせて一つのSQL文を作ります。(String::insertなんてあったんだ、知らなかった。)


最後にそれを、DboSource::fetchAll()に渡して、DboSource::execute()が呼ばれて、SQLが実行されます。余談ですが、execute()では、SQLの実行時間とかSQL文を保持させてます。これを使って最初のSQL Explainコンポーネントを作りました。

executeするとDboMysql::_execute()が呼ばれて、その中でmysql_query()が実行されてSQL処理が走ります。

おじさんSEおじさんSE 2009/05/04 12:20 貴重な情報、ありがとうございます。 私のような高齢SEにとっては、フレームワークごとにDBアクセスのための書式を習得するのはつらいことで、SQLを直接書くほうがずっと楽です。 さらにインジェクション対策としては、ずっとプレースホルダでやってきましたので、cakePHPで「プレースホルダを使って、直接SQLを書く」ことが可能というのは、助かります。(なぜSQLをバラバラにするのがいいことなのか、私にはよくわかりません。 とりわけwhere句の比較演算子をなぜあそこに書くのか、ずっと疑問です)

cakephpercakephper 2009/05/05 15:32 コメントありがとうございます。
僕もPerlではSQL直書きの、プレースホルダでずっとやってたので、最初は違和感があったのですが、Cakeの流れに身を任せたら慣れました。ただ、どうしてもSQLはすぐ出るのにCakeでどうやって書いたらよいか悩む時には、SQL直接書くことが多いです。
僕のスタンスとしてはあまり規約に縛られすぎるのも好きじゃないので、Cakeを使ったほうが楽なところは取り込んで、そうじゃなさそうなところは規約を回避するようにしてます。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/cakephper/20090417/1239939705