関数 mysql_real_escape_string を、関数 $wpdb->prepare に置き換えたら使えるようにはなったが・・・
1 月 5 日のこと 。
いつものようにサイトを開こうとしたら、PHP のバージョンを新しくしないと表示できないよ・・・という、多分これでお目にかかるのは 3 度目くらいのアレレなメッセージが出た。
バックアップは昨年末に全部とってあったので、何も考えずに XSERVER のサーバーパネルで PHP を ver 5.6.x に切り替えた。で、インストール済みプラグインの動作確認を実行したところ、タイトルどおりの現象が発生してビックリしたと・・・こういうワケだ。
警告メッセージは、次のようなペアが 16対(つい)表示された。
それぞれのペアで違っているのは行番号だけで、言っていることは全部同じ内容だ。
- 「Warning: mysql_real_escape_string(): Access denied for user 〈user〉@’localhost’ (using password: NO) in…statpress-reloaded/statpress.php on line 行番号」
- 「Warning: mysql_real_escape_string(): A link to the server could not be established in…statpress-reloaded/statpress.php on line 行番号」
どうも PHP のバージョンを上げたため、関数 mysql_real_escape_string が MySQL に「Access denied(アクセス拒否)」されてしまったということらしい。
まず最初に、関数 mysql_real_escape_string を他の 2 種類の関数に置き換えてテストした結果を報告しておく。
その前にお断りしておくが、私は決してコードの書き直しをお勧めしている訳ではない。それどころかむしろ、他の優れたアクセス解析プラグインに切り換えることをお勧めしたいと思っているくらいだ ―― 例えば SlimStat とか。
今回色々やったのは、私自身が WordPress のデータベース処理に興味を持っていたので、これを機にちょっと試してみたくなったというだけのことで、他の人をコード改編の泥沼に引きずり込むつもりなどまったくないのだ。
2 種類のテスト:
- すべての mysql_real_escape_string を esc_sql に置換(Ver 1)。
- 置換可能なすべての mysql_real_escape_string を $wpdb->prepare に置換。
置換できない1 箇所を esc_sql に置換。SQL の Insert ステートメントの php コードを改造(Ver 2)。
結果:
- Ver 1 と Ver 2 で、日次の集計、比較、予測の表示、および日次データの新規挿入が正常に動作した。
- Ver 1 と Ver 2 で、月次の集計、比較、予測の表示、および月次データの新規挿入が正常に動作した。
私は現在テスト Ver 2 の方を使っている。
こちらを使うことにしたのは、Codex や PHP マニュアルなどの信頼できるサイトがプリペアドステートメントを使うよう積極的に推奨しているから ―― と言うよりも、そういったサイトを読んだので $wpdb->prepare の使い方を学習してみたくなったからだ。
それと WordPress development の FAQ の中にちょっと気になる質問を見つけたので、esc_sql の信頼性に確信が持てなかったというのもある。でもこの質問に答えがついていないのも、逆の意味でちょっと気になるんだよね。
WordPress developmen の FAQ からの引用:
But when I use
esc_sql()
I get symbols likeÂ
:$fetch_row='<p>And again for single posts</p>’
echo esc_sql( $fetch_row )Output:
And again for single posts
What does
Â
mean?More example
fifteenÂ
maximumÂ
 your stylesheet,  where you haveÂ
.
関数 $wpdb->prepare の使用例(参考):
下のウインドウのコード例は LAST MONTH、YESTER DAY、THIS MONTH、TODAY などのコメントがついているコードの、最初に出てくる部分を書き直した例だ。
もしこの例のように書き直す場合は、オリジナルコードにシングルクォートが含まれているかどうか、LIKE 演算子が使われているかどうか等々に注意する必要がある。
この例ではプレースホルダ―が全部「%s」なのでパラメータは文字列であることが分かるが、もし間違えて「%d(整数)」を指定してしまったら、比較演算子「=」を使っている所では後で間違いを見つけるのがかなり難しくなる。
なのでオリジナルファイルは必ずどこかにバックアップしておく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/* 最初に置換した 2 個所。LIKE 検索の例。*/ //LAST MONTH // Re-edited by Bonkure. $qry_lmonth = $wpdb->get_row($wpdb->prepare(" SELECT count(DISTINCT ip) AS visitors FROM $table_name WHERE feed='' AND spider='' AND date LIKE %s" , $lastmonth."%")); print "<td>" . $qry_lmonth->visitors . "</td>\n"; //THIS MONTH // Re-edited by Bonkure. $qry_tmonth = $wpdb->get_row($wpdb->prepare(" SELECT count(DISTINCT ip) AS visitors FROM $table_name WHERE feed='' AND spider='' AND date LIKE %s" , $thismonth."%")); /* ********** 中略 ********** */ /* 通常の一致検索の例 */ //YESTERDAY // Re-edited by Bonkure. $qry_y = $wpdb->get_row($wpdb->prepare(" SELECT count(DISTINCT ip) AS visitors FROM $table_name WHERE feed='' AND spider='' AND date = %s", $yesterday)); |
下のウインドウのコード例は MySQL の INSERT ステートメントを書き直した例だ。
ここでは「%s」と「%d」の 2種類のプレースホルダ―が使われている。
この部分は秀丸エディタの 200 文字折り返しで見ると、オリジナルファイルの 1,786 行目から始まっている。縦長になっているのは、通常の SQL 文の表示スタイルに合わせたためだ。私にはこの方がなじみ深い。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
$insert = $wpdb->prepare( "INSERT INTO " . $table_name . " ( date, time, ip, urlrequested, agent, referrer, search, nation, os, browser, searchengine, spider, feed, user, threat_score, threat_type, timestamp ) VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %d, %s )", array( $vdate, $vtime, $ipAddress, $urlRequested, strip_tags($userAgent), $referrer, strip_tags($search_phrase), iriDomain($ipAddress), $os, $browser, $searchengine, $spider, $feed, $userdata->user_login, $threat_score, $threat_type, $timestamp )); |
テスト Ver 2 で 1 箇所だけ esc_sql を使っていると書いたが、それは 200 文字折り返しだと、オリジナルファイルの 1,124 行目に書かれている部分だ。
ここでは StatPress の [検索] で使われる SELECT ステートメントの WHERE 句を動的に作成しているので、$wpdb->prepare で一意のプリペアドステートメントを作成することができない。なので、ここだけ esc_sql を使うことにしたのだ。