PHPのmysqli_real_escape_string()は、エスケープ時にMySQLに接続する必要があるため、この処理を自前で実装し接続不要で同等の処理を行いたいのですが、どのような処理になりますでしょうか?


mysqli_real_escape_string:
http://jp2.php.net/manual/ja/mysqli.real-escape-string.php

上記マニュアルによると、

- NUL (ASCII 0), \n, \r, \, ', ", および Control-Zの文字をエスケープ

- 接続先MySQLの文字セットの考慮(?) ※具体的に思いつかない

の2つの処理で済みそうですが、それ以外に必要な処理があれば教えてください。

ちなみに自前で実装する場合、MySQLの文字セットとエスケープ対象の文字列はどちらもutf8mb4で固定になります。

具体的なソースがあると分かりやすくて有り難いです。
宜しくお願いします。

回答の条件
  • 1人5回まで
  • 13歳以上
  • 登録:2013/12/20 19:16:10
  • 終了:2013/12/24 11:10:04

ベストアンサー

id:a-kuma3 No.1

a-kuma3回答回数4596ベストアンサー獲得回数19352013/12/20 20:06:24

ポイント100pt

本物を見るのが一番早いと思います。

PHP の Mysqli は、Mysql の関数を呼び出しているだけです。
MySQL-5.6.14 から実装を抜き出しました。

mysql_real_escape_string() の実装は libmysql/libmysql.c にあります。

/*
  Add escape characters to a string (blob?) to make it suitable for a insert
  to should at least have place for length*2+1 chars
  Returns the length of the to string
 */

ulong STDCALL
mysql_escape_string(char *to,const char *from,ulong length)
{
  return (uint) escape_string_for_mysql(default_charset_info, to, 0, from, length);
}

ulong STDCALL
mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
             ulong length)
{
  if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
    return (uint) escape_quotes_for_mysql(mysql->charset, to, 0, from, length);
  return (uint) escape_string_for_mysql(mysql->charset, to, 0, from, length);
}

実装は、この二つの関数になっています。

  • escape_string_for_mysql()
  • escape_quotes_for_mysql()

後者は、クォーテーションのエスケープだけを行います。


escape_string_for_mysql() の実装は mysys/charset.c にあります。

size_t escape_string_for_mysql(const CHARSET_INFO *charset_info,
                               char *to, size_t to_length,
                               const char *from, size_t length)
{
  const char *to_start= to;
  const char *end, *to_end=to_start + (to_length ? to_length-1 : 2*length);
  my_bool overflow= FALSE;
#ifdef USE_MB
  my_bool use_mb_flag= use_mb(charset_info);
#endif
  for (end= from + length; from < end; from++)
  {
    char escape= 0;
#ifdef USE_MB
    int tmp_length;
    if (use_mb_flag && (tmp_length= my_ismbchar(charset_info, from, end)))
    {
      if (to + tmp_length > to_end)
      {
        overflow= TRUE;
        break;
      }
      while (tmp_length--)
    *to++= *from++;
      from--;
      continue;
    }
    /*
     If the next character appears to begin a multi-byte character, we
     escape that first byte of that apparent multi-byte character. (The
     character just looks like a multi-byte character -- if it were actually
     a multi-byte character, it would have been passed through in the test
     above.)

     Without this check, we can create a problem by converting an invalid
     multi-byte character into a valid one. For example, 0xbf27 is not
     a valid GBK character, but 0xbf5c is. (0x27 = ', 0x5c = \)
    */
    if (use_mb_flag && (tmp_length= my_mbcharlen(charset_info, *from)) > 1)
      escape= *from;
    else
#endif
    switch (*from) {
    case 0:             /* Must be escaped for 'mysql' */
      escape= '0';
      break;
    case '\n':              /* Must be escaped for logs */
      escape= 'n';
      break;
    case '\r':
      escape= 'r';
      break;
    case '\\':
      escape= '\\';
      break;
    case '\'':
      escape= '\'';
      break;
    case '"':               /* Better safe than sorry */
      escape= '"';
      break;
    case '\032':            /* This gives problems on Win32 */
      escape= 'Z';
      break;
    }
    if (escape)
    {
      if (to + 2 > to_end)
      {
        overflow= TRUE;
        break;
      }
      *to++= '\\';
      *to++= escape;
    }
    else
    {
      if (to + 1 > to_end)
      {
        overflow= TRUE;
        break;
      }
      *to++= *from;
    }
  }
  *to= 0;
  return overflow ? (size_t) -1 : (size_t) (to - to_start);
}

ざっと見た感じ、マニュアルに書いた以上のことはやってません(当たり前か)。

escape_quotes_for_mysql() の実装も mysys/charset.c にあります。
省略しますが、escape_string_for_mysql() から、シングルクォートのエスケープだけを抜き出したような実装になってます。

id:wankodon

遅れましたが回答有り難うございます。
ソースを見る発想は全くありませんでした。

非常に参考になります!ありがとうございます!

2013/12/24 10:16:03

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません