大まかに以下の違いがある
- mysql_escape_string : 文字コードを気にせず バックスラッシュ escape する
- mysql_real_escape_string : DB へ connect した際に取得した文字コードを元に複数バイト文字はエスケープしない。不正な文字はバックスラッシュ escape する
日本語で最も影響があるのは、SJIS を使ってるときで、「ソフト」を escape してみると、
<?php
mysql_connect('DB host', 'user', 'pass');
$ret = mysql_set_charset('sjis');
$str = "\x83\x5c\x83\x74\x83\x67"; // ソフト
echo mysql_escape_string($str), "\n";
echo mysql_real_escape_string($str), "\n";
出力は以下のとおり
ソ\フト ソフト
ソ の2バイト目は \ なので mysql_escape_string の方ではエスケープする。 しかし、mysql_real_escape_string の方は文字コード判定するのでエスケープしない。
ソース見ると latin1 かコネクト時に取得した文字コードを使うかの違い以外は、同じ関数を使って処理してる。
// ./libmysql/libmysql.c
1620 ulong STDCALL
1621 mysql_escape_string(char *to,const char *from,ulong length)
1622 {
// default_charset_info は latin1 (1バイトの文字コード(asciiと下位互換))
1623 return (uint) escape_string_for_mysql(default_charset_info, to, 0, from, length);
1624 }
1625
1626 ulong STDCALL
1627 mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
1628 ulong length)
1629 {
// コネクト時の文字コードを使用
1630 if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
1631 return (uint) escape_quotes_for_mysql(mysql->charset, to, 0, from, length);
1632 return (uint) escape_string_for_mysql(mysql->charset, to, 0, from, length);
1633 }
//./mysys/charset.c
675 size_t escape_string_for_mysql(CHARSET_INFO *charset_info,
676 char *to, size_t to_length,
677 const char *from, size_t length)
678 {
679 const char *to_start= to;
680 const char *end, *to_end=to_start + (to_length ? to_length-1 : 2*length);
681 my_bool overflow= FALSE;
682 #ifdef USE_MB
// latin1 の場合は use_mb の値が false
683 my_bool use_mb_flag= use_mb(charset_info);
684 #endif
685 for (end= from + length; from < end; from++)
686 {
687 char escape= 0;
688 #ifdef USE_MB
689 int tmp_length;
// 文字コードが複数バイトの場合の処理(複数バイト場合はここで、continue)
690 if (use_mb_flag && (tmp_length= my_ismbchar(charset_info, from, end)))
691 {
692 if (to + tmp_length > to_end)
693 {
694 overflow= TRUE;
695 break;
696 }
697 while (tmp_length--)
698 *to++= *from++;
699 from--;
700 continue;
701 }
// 不正文字の場合の処理
713 if (use_mb_flag && (tmp_length= my_mbcharlen(charset_info, *from)) > 1)
714 escape= *from;
715 else
// 1バイト文字の場合の処理。switch case 使って いくつかの文字だけエスケープ
717 switch (*from) {
718 case 0: /* Must be escaped for 'mysql' */
719 escape= '0';
720 break;
721 case '\n': /* Must be escaped for logs */
722 escape= 'n';
723 break;
724 case '\r':
725 escape= 'r';
726 break;
727 case '\\':
728 escape= '\\';
729 break;
730 case '\'':
731 escape= '\'';
732 break;
733 case '"': /* Better safe than sorry */
734 escape= '"';
735 break;
736 case '\032': /* This gives problems on Win32 */
737 escape= 'Z';
738 break;
739 }
mysql_real_escape_string だと、以下の場合に不具合が起きるかもしれない。
- データベースの文字コードが SJIS で、バイナリデータに対して、mysql_real_escape_string を使った場合。
SJIS は2バイト目に ASCII と文字がかぶってるものがあるから問題となるが、
もうUTF8しか使わないので、文字コード判定なんてしなくてもいいはず。
PHP の関数は deprecated だが、C API の方は「should instead!」と書いてるだけで、使うなとは書いてない。
http://dev.mysql.com/doc/refman/5.5/en/mysql-escape-string.html
mysql_eacape_string の方は処理がシンプルだし、負荷が大きいような箇所では mysql_escape_string を使うことにしよう
No comments:
Post a Comment