LoginSignup
57
63

More than 5 years have passed since last update.

MySQL関数からMySQLi関数への移行作業メモ

Last updated at Posted at 2015-07-31

はじめに

PHPでMySQL関数を利用している場合、早急にMySQLi関数またはPDOへ移行を行う必要がある。
MySQLへの依存を取り除くためにもPDOへの移行が望ましいが、当面MySQLからの移行がないと言い切れる場合、MySQLi関数の手続き型表現を利用することにより、PDOへの乗り換えを行うよりも手間をかけずに移行することが出来る。
繰り返すが、可能ならばPDOへの移行が望ましいため、客先がPDOへの移行にコストがかかるという理由で渋っている場合にのみ参考とすること。

なお、参考コードは全てPHP 5.1系、5.2系、5.4系で動作確認をしている。
世の中には(イントラネット内とはいえ)未だにPHP 5.1系で稼動しているという恐ろしい現場があることを忘れてはならない...。

注意事項

関数の引数順序について

MySQLi関数では全ての関数においてMySQLi接続オブジェクト、または結果オブジェクトを引数に渡す必要がある。
MySQL関数では接続リソース、または結果リソースは省略可能な関数があり、また、省略可能な場合引数の順序が第2引数以降となっている。
そのため、MySQL関数とMySQLi関数では引数の順序が異なっているものが多々存在する。

接続リソース、または結果リソースが省略可能、または指定不要な関数一覧

  • mysql_affected_rows
  • mysql_client_encoding
  • mysql_close
  • mysql_db_query
  • mysql_errno
  • mysql_error
  • mysql_escape_string
  • mysql_get_client_info
  • mysql_get_host_info
  • mysql_get_proto_info
  • mysql_get_server_info
  • mysql_info
  • mysql_insert_id
  • mysql_ping
  • mysql_query
  • mysql_real_escape_string
  • mysql_select_db
  • mysql_set_charset
  • mysql_stat
  • mysql_thread_id
  • mysql_unbuffered_query
  • mysql_create_db
  • mysql_drop_db
  • mysql_list_dbs
  • mysql_list_fields
  • mysql_list_processes
  • mysql_list_tables

引数の順序が異なる関数一覧

  • mysql_connect
  • mysql_escape_string
  • mysql_query
  • mysql_real_escape_string
  • mysql_select_db
  • mysql_set_charset

接続可否判定

mysql_connect()では接続に失敗すると真偽値falseが返っていていたが、mysqli_connect()では接続に失敗してもmysqliオブジェクトが返る。
そのため、接続に成功したかを確認するには mysqli_connect_error() 関数の返り値が空文字か、mysqli_connect_errno() の返り値が整数「0」以外かの判定を行う必要がある。

実装例

$conn = mysqli_connect('host', 'user', 'password', 'dbname');
if(mysqli_connect_errno() > 0){
  echo "接続に失敗しました";
  exit;
}

結果の取得について

mysql_fetch_row()等の結果を取得する関数において、MySQL関数では取得できる行がない場合は真偽値falseを返すが、MySQLi関数ではNULLを返す。
そのため、真偽値falseであることを厳格に判定している場合、処理を書き換える必要がある。
厳格に判定していない場合は関数の置換で対応可能である。

関数対応表

PHPオンラインマニュアルの該当関数ページにも代替関数の情報が記載されているが、 mysql_fetch_field() のように代替することができない関数が記載されていることがあった。
以下筆者が実際に移行作業を行う際に利用した関数を対応表として記す。

MySQL関数 MySQLi関数 備考
mysql_affected_rows mysqli_affected_rows
mysql_client_encoding mysqli_character_set_name
mysql_close mysqli_close
mysql_connect mysqli_connect
mysql_data_seek mysqli_data_seek
mysql_db_query - mysqli_select_db()mysqli_query() の組み合わせで代用可
mysql_errno mysqli_errno
mysql_error mysqli_error
mysql_escape_string mysqli_real_escape_string
mysql_fetch_array mysqli_fetch_array
mysql_fetch_assoc mysqli_fetch_assoc
mysql_fetch_object mysqli_fetch_object
mysql_fetch_row mysqli_fetch_row
mysql_fetch_field mysqli_fetch_field_direct 返り値のオブジェクトプロパティが異なるため要変換
mysql_fetch_lengths mysqli_fetch_length
mysql_field_flags - mysqli_fetch_field_direct() にて取得するオブジェクトで代用可
mysql_field_len - mysqli_fetch_field_direct() にて取得するオブジェクトで代用可
mysql_field_name - mysqli_fetch_field_direct() にて取得するオブジェクトで代用可
mysql_field_table - mysqli_fetch_field_direct() にて取得するオブジェクトで代用可
mysql_field_type - mysqli_fetch_field_direct() にて取得するオブジェクトで代用可
mysql_field_seek mysqli_field_seek
mysql_free_result mysqli_free_result
mysql_get_client_info mysqli_get_client_info
mysql_get_host_info mysqli_get_host_info
mysql_get_proto_info mysqli_get_proto_info
mysql_get_server_info mysqli_get_server_info
mysql_info mysqli_info
mysql_insert_id mysqli_insert_id
mysql_num_fields mysqli_field_count
mysql_num_rows mysqli_num_rows
mysql_pconnect mysqli_connect PHP 5.3 以降にてホストプレフィックス「p:」を付与。 PHP 5.2以下では不可
mysql_ping mysqli_ping
mysql_query mysqli_query
mysql_real_escape_string mysqli_real_escape_string
mysql_result - mysqli_data_seek(), mysqli_field_seek(), mysqli_fetch_row()等の組み合わせで代用可
mysql_select_db mysqli_select_db
mysql_set_charset mysqli_set_charset
mysql_stat mysqli_stat
mysql_thread_id mysqli_thread_id
mysql_unbuffered_query - mysqli_real_query()mysqli_use_result() で代用可
mysql_create_db - mysqli_query() にて CREATE DATABASE クエリ実行で代用可
mysql_drop_db - mysqli_query() にて DROP DATABASE クエリ実行で代用可
mysql_db_name - mysqli_query() にて SHOW DATABASE クエリ実行で代用可
mysql_list_dbs - mysqli_query() にて SHOW DATABASE クエリ実行で代用可
mysql_tablename - mysqli_query() にて SHOW TABLES クエリ実行で代用可
mysql_list_fields - mysqli_query() にて SHOW COLUMNS クエリ実行で代用可
mysql_list_processes - mysqli_query() にて SHOW PROCESSLIST クエリ実行で代用可
mysql_list_tables - mysqli_query() にて SHOW TABLES クエリ実行で代用可

代替手段

ここに記す代替手段は、あくまで筆者の環境で動いたものである。
これらの代替手段で何かしら致命的な不具合が発生したとしても、筆者は責任を負いかねるためご了承いただきたい。

mysql_db_query()

mysqli_select_db($conn, $dbname);
$res = mysqli_query($conn, $query);

mysql_fetch_field()

mysqli_fetch_field_direct() は厳密には mysql_fetch_field() の代替とすることができない。

  • mysql_fetch_field() はフラグをそれぞれプロパティとして持っているが、 mysqli_fetch_field_direct()flags プロパティにビット情報を整数として持っている
  • mysql_fetch_field() は型情報を文字列として持っているが、 mysqli_fetch_field_direct() はビット情報を整数として持っている

ビット情報の整数から文字列に変換するためにはMYSQLI定数を参照する必要があるが、完全に互換性を保つためにはいくつか手を入れる必要がある。
下記のような実装を用いることにより mysql_fetch_field() が返すオブジェクトと完全互換のオブジェクトに変換することが可能となる。

/**
 * MYSQLI_*_FLAG定数を読み込みビットフラグ整数と名称の配列を生成する。
 * 引数に true を渡すと旧MySQL関数互換の配列を返す。
 *
 * @param bool MySQL関数互換フラグ
 * @return array
 */
function get_flag_names($compat = false){
  if($compat){
    return array(
      1 => "not_null",
      2 => "primary_key",
      4 => "unique_key",
      8 => "multiple_key",
      16 => "blob",
      32 => "unsigned",
      64 => "zerofill",
      128 => "binary",
      256 => "blob",
      512 => "auto_increment",
      1024 => "timestamp"
    );
  } else {
    $flags = array();
    $constants = get_defined_constants(true);
    foreach ($constants['mysqli'] as $c => $n){
      if (preg_match('/MYSQLI_(.*)_FLAG$/', $c, $m)){
        if (!array_key_exists($n, $flags)){
          $flags[$n] = strtolower($m[1]);
        }
      }
    }
    return $flags;
  }
}

/**
 * MYSQLI_TYPE_*定数を読み込みビットフラグ整数と名称の配列を生成する。
 * 引数に true を渡すと旧MySQL関数互換の配列を返す。
 *
 * @param bool MySQL関数互換フラグ
 * @return array
 */
function get_type_names($compat = false){
  $types = array();
  $constants = get_defined_constants(true);
  foreach ($constants['mysqli'] as $c => $n){
    if (preg_match('/MYSQLI_TYPE_(.*)$/', $c, $m)){
      $types[$n] = strtolower($m[1]);
    }
  }
  // MySQL関数互換モードの場合、MySQL関数が返す値で置き換える
  if($compat){
    $types[0] = "real";
    $types[1] = "char";
    $types[2] = "int";
    $types[3] = "int";
    $types[4] = "real";
    $types[5] = "real";
    $types[8] = "int";
    $types[9] = "int";
    $types[248] = "string";
    $types[249] = "blob";
    $types[250] = "blob";
    $types[251] = "blob";
    $types[253] = "string";
  }
  return $types;
}

/**
 * mysqli_fetch_field()等で取得したstdClassオブジェクトをmysql_fetch_field()互換の形式で作り直す。
 *
 * @param object mysqli_fetch_field()等で取得したstdClassオブジェクト
 * @param bool MySQL関数互換フラグ
 * @return object
 */
function remake_field_object($field, $compat = true){
  $obj = new stdClass();
  $flags = get_flag_names($compat);
  $types = get_type_names($compat);
  $numeric_types = array(1, 2, 3, 4, 5, 8, 9, 16, 246);
  $compat_flags  = array("not_null", "primary_key", "unique_key", "multiple_key", "blob", "unsigned", "zerofill");
  $nocompat_keys = array("orgname", "orgtable", "db", "catalog", "length", "charsetnr", "decimals");
  $compat_sortkey = array("name", "table", "def", "max_length", "not_null", "primary_key", "multiple_key", "unique_key", "numeric", "blob", "type", "unsigned", "zerofill");
  foreach((array) $field as $key => $value){
    switch($key){
    case "flags":
      foreach($flags as $num => $name){
        // MySQL関数互換モードの場合、mysql_fetch_field() の返り値にないプロパティは飛ばす
        if($compat == true && ! in_array($name, $compat_flags)) continue;
        $obj->$name = ($value & $num) ? 1 : 0;
      }
      break;
    case "type":
      $obj->type = $types[$value];
      // $obj->numericはflagsの中には含まれていないため、typeを見て判断する
      $obj->numeric = (in_array($value, $numeric_types)) ? 1 : 0;
      // TEXT型の場合、typeはblobとなるがflagsではblobとして扱われないためここでフラグを立てる
      if($obj->type == "blob") $obj->blob = 1;
      break;
    default:
      // MySQL関数互換モードの場合、mysql_fetch_field() の返り値にないプロパティは飛ばす
      if($compat == true && in_array($key, $nocompat_keys)) continue;
      $obj->$key = $value;
      break;
    }
  }
  // オブジェクトを配列にキャストして処理する場合、キャスト後の配列の順序はプロパティ定義順となる。
  // そのため、MySQL関数互換モードの場合MySQL関数と同一の定義順に並び替える。
  if($compat == true){
    $sorted = new stdClass();
    foreach($compat_sortkey as $key){
      $sorted->$key = $obj->$key;
    }
    $obj = $sorted;
  }
  return $obj;
}

mysql_field系関数

mysql_field系関数の単一情報を取得する関数は廃止となり、 mysqli_fetch_field_direct() 等で一括で取得する必要がある。
mysql_fetch_field() と同様にフラグ情報や型情報が整数型となるため、変換する必要がある。

上記 mysql_fetch_field() にて実装した関数に加え、下記の実装を利用することで mysql_field系関数と完全互換の値を取得することが可能となる。

/**
 * mysqli_fetch_field_direct()の結果からmysql_field_*()の各種返り値と同一形式のデータを作成し返す
 * XXX: 順次mysqliの正式な関数に対応するときのため、フラグやタイプをMySQLi準拠のものに切り替えられるよう互換フラグを渡せるようにしている。
 *
 * @param mysqli_result MySQLi結果オブジェクト
 * @param int フィールド番号
 * @param string 対象となるmysqli_fetch_field_direct()関数の返り値オブジェクトのプロパティ名
 * @param bool MySQL関数互換フラグ
 * @return mixed
 */
function mysqli_field_compat(&$res, $offset, $key, $compat = true){
  $field = mysqli_fetch_field_direct($res, $offset);
  $result = false;
  if($field){
    switch($key){
    case "flags":
      $list = array();
      $flags = get_flag_names($compat);
      foreach($flags as $num => $name){
        if($field->flags & $num) $list[] = $name;
      }
      $result = implode(" ", $list);
      break;
    case "type":
      $types = get_type_names($compat);
      if(!is_null($types[$field->type])) $result = $types[$field->type];
      break;
    default:
      $result = $field->$key;
      break;
    }
  }
  return $result;
}

/**
 * mysql_field_flags()互換関数
 * XXX: mysqliの正式な関数に対応するときのため、フラグやタイプをMySQLi準拠のものに切り替えられるよう互換フラグを渡せるようにしている。
 *
 * @param mysqli_result MySQLi結果オブジェクト
 * @param int フィールド番号
 * @return string
 */
function mysqli_field_flags(&$res, $offset, $compat = true){
  return mysqli_field_compat($res, $offset, "flags", $compat);
}

/**
 * mysql_field_type()互換関数
 * XXX: mysqliの正式な関数に対応するときのため、フラグやタイプをMySQLi準拠のものに切り替えられるよう互換フラグを渡せるようにしている。
 *
 * @param mysqli_result MySQLi結果オブジェクト
 * @param int フィールド番号
 * @return string
 */
function mysqli_field_type(&$res, $offset, $compat = true){
  return mysqli_field_compat($res, $offset, "type", $compat);
}

/**
 * mysql_field_len()互換関数
 *
 * @param mysqli_result MySQLi結果オブジェクト
 * @param int フィールド番号
 * @return int
 */
function mysqli_field_len(&$res, $offset){
  return mysqli_field_compat($res, $offset, "length");
}

/**
 * mysql_field_name()互換関数
 *
 * @param mysqli_result MySQLi結果オブジェクト
 * @param int フィールド番号
 * @return string
 */
function mysqli_field_name(&$res, $offset){
  return mysqli_field_compat($res, $offset, "name");
}

/**
 * mysql_field_table()互換関数
 *
 * @param mysqli_result MySQLi結果オブジェクト
 * @param int フィールド番号
 * @return object
 */
function mysqli_field_table(&$res, $offset){
  return mysqli_field_compat($res, $offset, "table");
}

mysql_result()

/**
 * mysql_result()互換関数
 * XXX: mysqli_resultクラスと区別するため、関数名に接尾語として「_compat」を付けている。
 *
 * @param mysqli_result MySQLi結果オブジェクト
 * @param int 行番号
 * @param int フィールド番号
 * @return string
 */
function mysqli_result_compat(&$res, $row, $field = 0){
  mysqli_data_seek($res, $row);
  $fields = (is_int($field)) ? mysqli_fetch_row($res) : mysqli_fetch_assoc($res);
  return (is_null($fields[$field])) ? false : $fields[$field];
}

mysql_unbuffered_query()

/**
 * mysqli_unbuffered_query()互換関数
 * XXX: 他のMySQLi関数と統一性を持たせるため、mysql_unbuffered_query()とは引数の順序が異なる
 *
 * @param mysqli MySQLi接続オブジェクト
 * @param string SQL
 * @return string
 */
function mysqli_unbuffered_query(&$conn, $query){
  mysqli_real_query($conn, $query);
  return mysqli_use_result($conn);
}

追記

[PHP] mysqli_stmt が使いにくかったのでラッパー関数を作った というこの記事の続きを書いた。
MySQLi関数でプリペアドステートメントの利用を考えている方は併せて参考にしていただければ幸いである。

57
63
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
57
63