LoginSignup
5
5

More than 1 year has passed since last update.

MDB2→PDOに移行する

Last updated at Posted at 2021-04-29

MDB2→PDO移行の覚えがき。
DB接続、トランザクション、ステートメント実行といった基本的な内容が中心。

公式マニュアル

公式マニュアルのTOPページはそれぞれ以下の通り。

1. DB接続

MDB2 PDO
接続 MDB2::factory, MDB2::connent, MDB2::singleton new PDO
接続エラー PEAR::isError PDOExceptionをスロー
切断 MDB2::disconnent nullをセット

(1) 接続

いろいろな書き方があるので、とりあえず代表的そうなものを抜粋。

MDB2
/* MySQL */
$dbh = MDB2::connect("mysql://$user:$password@$host:$port/$dbname");
/* PostgreSQL */
$dbh = MDB2::connect("pgsql://$user:$password@$host:$port/$dbname");
/* Oracle */
$dbh = MDB2::connect("oci8://$user:$password@$host:$port/?service=$dbname");
PDO
/* MySQL */
$dbh = new PDO("mysql:host=$host;port=$port;dbname=$dbname", $user, $password);
/* PostgreSQL */
$dbh = new PDO("pgsql:host=$host;port=$port;dbname=$dbname;user=$user;password=$password");
/* Oracle */
$dbh = new PDO("oci:dbname=".$dbinfo, $user, $password);

(2) 接続エラー

MDB2
$dbh = MDB2::connect($connection_string);
if (PEAR::isError($dbh)) {
    // 接続に失敗した場合の処理を書く
}
PDO
try {
    $dbh = new PDO($connection_string);
} catch (PDOException $e) {
    // 接続に失敗した場合の処理を書く
}

(3) 切断

MDB2
$dbh->disconnect();
PDO
$dbh = null;

参考

2. トランザクション

内容的にほとんど同じ。
トランザクションサポートの有無の確認の仕方が違うくらい。

MDB2 PDO
開始 MDB2::beginTransaction PDO::beginTransaction
サポートの確認(※) MDB2::support('transactions') 非対応の場合、PDOExceptionをスロー
コミット MDB2::commit PDO::commit
ロールバック MDB2::rollBack PDO::rollBack

※: サポート確認: 「トランザクションサポートの有無の確認」を指しています。

(1) 開始, (2) サポートの確認

MDB2
$dbh->beginTransaction();
if(!$dbh->supports('transactions')){
    // トランザクションサポートがない場合の処理を記述
}
PDO
try {
    $dbh->beginTransaction();
} catch (PDOException $e) {
    // トランザクションサポートがない場合の処理を記述
}

(3) コミット

MDB2
$dbh->commit();
PDO
$dbh->commit();

(4) ロールバック

MDB2
$dbh->rollback();
PDO
$dbh->rollback();

参考

3. ステートメントの準備と実行

MDB2→PDOの移行で最もエネルギーを使いそうなところ。
ステートメントを実行した後のデータの取り出し方も異なっているので、そこも注意すべきポイントだったり。

MDB2 PDO
値のバインド MDB2::quote PDOStatement::bindParam, PDOStatement::bindValue
値のデータ型(SQL INTEGER) integer PDO::PARAM_INT
値のデータ型(SQL CHAR, VARCHAR, 他文字列データ型) text PDO::PARAM_STR
値のデータ型(FLOAT型) float PDO::PARAM_STR もしくは PDO::PARAM_INT
値のデータ型(ブールデータ型) boolean PDO::PARAM_BOOL
値のデータ型(タイムスタンプ型) timestamp PDO::PARAM_STR(※2)
値のデータ型(日付型) date PDO::PARAM_STR(※)
実行に失敗した場合 MDB2 Errorオブジェクトを返す falseを返す
※PDO::ATTR_ERRMODEにPDO::ERRMODE_EXCEPTIONをセットすると、PDOExceptionとなる

※1: FLOAT型に関し、PDO::PARAM_INTを使う場合は、PDO::ATTR_EMULATE_PREPARESをtrueにすること
※2: PDOには、タイムスタンプ型、日付型が存在しないので、代わりに文字列型のPDO::PARAM_STRを使用する

(1) 変数や値のバインドを伴うプリペアドステートメントの実行

MDB2
$stmt = 'INSERT INTO tablename (id, itemname, saved_time) VALUES ('
    . $dbh->quote($id,   'integer')   .', '
    . $dbh->quote($name, 'text')      .', '
    . $dbh->quote($time, 'timestamp') .')';
$res = $dbh->exec($stmt);
PDO
$stmt = 'INSERT INTO tablename (id, itemname, saved_time) VALUES (:id, :name, :saved_time)';
$sth = $dbh->prepare($stmt);
$sth->bindValue(':id', $id, PDO::PARAM_INT);
$sth->bindValue(':name', $name, PDO::PARAM_STR);
$sth->bindValue(':saved_time', $saved_time, PDO::PARAM_STR);
$res = $sth->execute();

(2) 入力値の配列を伴うプリペアドステートメントの実行(名前つきパラメータ)

MDB2
$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit
    WHERE calories < :calories AND colour = :colour');
$res = $sth->execute(array(':calories' => $calories, ':colour' => $colour));
while ($row = $res->fetchRow()) {
    // 取り出す内容を記述
}
PDO
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->execute(array(':calories' => $calories, ':colour' => $colour));

foreach ($sth->fetchAll() as $row) {
    // 取り出す内容を記述
}

(3) 入力値の配列を伴うプリペアドステートメントの実行 (プレースホルダ)

MDB2
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit
    WHERE calories < ? AND colour = ?');
$res = $sth->execute(array($calories, $colour));
while ($row = $res->fetchRow()) {
    // 取り出す内容を記述
}
PDO
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit
    WHERE calories < ? AND colour = ?');
$sth->execute(array($calories, $colour));
foreach ($sth->fetchAll() as $row) {
    // 取り出す内容を記述
}

(4) 多数の値のバインドを伴う&データ型の指定が必要な場合

MDB2
$types = array('integer', 'text', 'text', 'date', 'date');
$sth = $dbh->prepare('INSERT INTO numbers VALUES (?, ?, ?, ?, ?)', $types, MDB2_PREPARE_MANIP);
$params = array(1, 'one', 'en', date("Y-m-d H:i:s"), date("Y-m-d H:i:s"));
$res = $sth->execute($params);
while ($row = $res->fetchRow()) {
    // 取り出す内容を記述
}

MDB2だとprepareの第2引き数にデータ型を詰めた配列をセットしてあげればいいけれど、PDOだとそうはいかない。bindParamやbindValueを何回も書くのもな、という場合は関数化してしまえばOK。
また、こうすれば、パラメータの数が可変の場合も対応できる。

PDO
$sth = $dbh->prepare('INSERT INTO numbers VALUES (?, ?, ?, ?, ?)');
$params = array(1, 'one', 'en', date("Y-m-d H:i:s"), date("Y-m-d H:i:s"));
$types = array('integer', 'text', 'text', 'date', 'date');
$sth = bindValues($stmt, $params, $types);
$sth->execute();
foreach ($sth->fetchAll() as $row) {
    // 取り出す内容を記述
}

function bindValues($sth, $params, $types){
    $i = 0;
    while($i < count($params)){
        switch($types[$i]){
            case 'boolean':
                $bindType = PDO::PARAM_BOOL;
                break;
            case 'integer':
                $bindType = PDO::PARAM_INT;
                break;
            case ('text' || 'timestamp' || 'date'):
                $bindType = PDO::PARAM_STR;
                break;
            default:
                return false;
        }
        $sth->bindValue($i+1, $params[$i], $bindType);
        $i++;
    }
    return $sth;
}

※1: 上記はあくまで例なので、使用しているDBにおけるデータ型を確認して、適切な形に書き換えるようにしてください。
※2: おかしな値が指定された時用にfalseを返してますが、上記コード中ではその際の処理は書いてません。

(5) 実行に失敗した場合

MDB2では、MDB2 Errorオブジェクトを返すのに対し、PDOではデフォルトでは、falseを返します。

そのままだと、try catch文では、失敗を検知できませんが、PDO::ATTR_ERRMODEにPDO::ERRMODE_EXCEPTIONをセットすると、PDOExceptionとすることができます。
書き換えをラク&処理を煩雑化させないためにも、後者としておくのが良いかと思います。
DB接続時(というより直後?)に、設定しておきましょう。
その他、DB処理をしている間は、try catch文内に入れておくのがよさそうです。

MDB2
$res = $sth->execute();
if (PEAR::isError($res)) {
    // 実行に失敗した場合の処理を記述
}
PDO
/* デフォルトの場合 */
$res = $sth->execute();
if (!res) {
    // 実行に失敗した場合の処理を記述
}

/* 以下の記述により、PDOExceptionにできる */
try {
    // DB接続処理
    $dbh = new PDO($connection_string);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    /* ・・ 省略 ・・*/
    $res = $sth->execute();
} catch (PDOException $e) {
    // 実行に失敗した場合の処理を書く
}

参考

4. フェッチモード

それぞれ接続時、SQL実行前など、いろいろなタイミングで設定が可能。
PDOはステートメントに対して指定することもできる。

キーor プロパティ MDB2 PDO
規則配列 キー: カラム番号 MDB2_FETCHMODE_ORDERED PDO::FETCH_NUM
連想配列 キー: カラム名 MDB2_FETCHMODE_ASSOC PDO::FETCH_ASSOC
オブジェクト プロパティ:カラム名 MDB2_FETCHMODE_OBJECT PDO::FETCH_OBJ
デフォルト -(デフォルトの設定値) MDB2_FETCHMODE_ORDERED PDO::FETCH_BOTH

(1) 接続時に指定

MDB2
$dbh = MDB2::connect($connection_string);
$dbh->setFetchMode(MDB2_FETCHMODE_ASSOC);
PDO
$dbh = new PDO($connection_string);
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

(2) 実行時に指定

MDB2
$dbh->setFetchMode(MDB2_FETCHMODE_ASSOC);
$sth = $dbh->prepare("SELECT aaa, bbb FROM hoge");
$res = $sth->execute();
while ($row = $res->fetchRow()) {
    // 取り出す内容を記述
}
PDO
$sth = $dbh->prepare("SELECT aaa, bbb FROM hoge");
// ステートメントに対し、設定
$sth->setFetchMode(PDO::FETCH_ASSOC);
$sth->execute();
foreach ($sth->fetchAll() as $row) {
    // 取り出す内容を記述
}

(3) フェッチ時に指定

MDB2
$sth = $dbh->prepare("SELECT aaa, bbb FROM hoge");
$res = $sth->execute();
while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) {
    // 取り出す内容を記述
}
PDO
$sth = $dbh->prepare("SELECT aaa, bbb FROM hoge");
$sth->execute();

foreach ($sth->fetchAll(PDO::FETCH_ASSOC) as $row) {
    print $row;
}

(4) 結果セット

①規則配列

MDB2でMDB2_FETCHMODE_ORDERED, PDOでPDO::FETCH_NUMを指定した場合の結果セット

Array
(
    [0] => Taro
    [1] => 12
    [2] => male
)

②連想配列

MDB2でMDB2_FETCHMODE_ASSOC, PDOでPDO::FETCH_ASSOCを指定した場合の結果セット

Array
(
    [name] => Taro
    [age] => 12
    [sex] => male
)

③オブジェクト

MDB2でMDB2_FETCHMODE_OBJECT, PDOでPDO::FETCH_OBJを指定した場合の結果セット

stdClass Object
(
    [name] => Taro
    [age] => 12
    [sex] => male
)

参考

終わりに

MDB2は、

  • 最新のリリースが2012年10月とかなり前(詳しくはコチラ
  • 使用に必要なPEARがPHP7.4からデフォルトでは使えず非推奨になり、将来的に削除される見通し(詳しくはコチラ)

ことから、早めにPHPの標準クラスであるPDOに移行していきたいところ。

変更履歴

  • 2021/07/29: 実行失敗時の書き換えを追加(3-(5))、記述ミスの修正(3のMDB2のforeachあたり)
  • 2021/07/30: フェッチモードにデフォルトのモードを追記(4)、フェッチモード指定のタイミングを追記(4-(3))
  • 2021/08/20: MDB2のフェッチモードの指定に誤りがあったため、修正(4-(1), (2))
  • 2021/11/25: PDOのSQL実行後のデータの取り出し方に誤りがあったため、修正(3-(3), (4))
  • 2022/07/15: PDOのプリペアドステートメントの実行の仕方に誤りがあったため、修正(3-(1))
  • 2022/07/15: PDOのSQL実行後のデータの取り出し方に誤りがあったため、修正(3-(2), 4-(2))
  • 2022/09/08: ステートメントの準備と実行に関し、FLOAT型についての言及を追記(3)
5
5
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
5
5