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

More than 3 years have passed since last update.


はじめに

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関数でプリペアドステートメントの利用を考えている方は併せて参考にしていただければ幸いである。