MDB2→PDO移行の覚えがき。
DB接続、トランザクション、ステートメント実行といった基本的な内容が中心。
公式マニュアル
公式マニュアルのTOPページはそれぞれ以下の通り。
1. DB接続
MDB2 | PDO | |
---|---|---|
接続 | MDB2::factory, MDB2::connent, MDB2::singleton | new PDO |
接続エラー | PEAR::isError | PDOExceptionをスロー |
切断 | MDB2::disconnent | nullをセット |
(1) 接続
いろいろな書き方があるので、とりあえず代表的そうなものを抜粋。
/* 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");
/* 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) 接続エラー
$dbh = MDB2::connect($connection_string);
if (PEAR::isError($dbh)) {
// 接続に失敗した場合の処理を書く
}
try {
$dbh = new PDO($connection_string);
} catch (PDOException $e) {
// 接続に失敗した場合の処理を書く
}
(3) 切断
$dbh->disconnect();
$dbh = null;
参考
-
MDB2
-
PDO
2. トランザクション
内容的にほとんど同じ。
トランザクションサポートの有無の確認の仕方が違うくらい。
MDB2 | PDO | |
---|---|---|
開始 | MDB2::beginTransaction | PDO::beginTransaction |
サポートの確認(※) | MDB2::support('transactions') | 非対応の場合、PDOExceptionをスロー |
コミット | MDB2::commit | PDO::commit |
ロールバック | MDB2::rollBack | PDO::rollBack |
※: サポート確認: 「トランザクションサポートの有無の確認」を指しています。
(1) 開始, (2) サポートの確認
$dbh->beginTransaction();
if(!$dbh->supports('transactions')){
// トランザクションサポートがない場合の処理を記述
}
try {
$dbh->beginTransaction();
} catch (PDOException $e) {
// トランザクションサポートがない場合の処理を記述
}
(3) コミット
$dbh->commit();
$dbh->commit();
(4) ロールバック
$dbh->rollback();
$dbh->rollback();
参考
-
MDB2
-
PDO
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) 変数や値のバインドを伴うプリペアドステートメントの実行
$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);
$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) 入力値の配列を伴うプリペアドステートメントの実行(名前つきパラメータ)
$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()) {
// 取り出す内容を記述
}
$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) 入力値の配列を伴うプリペアドステートメントの実行 (プレースホルダ)
$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()) {
// 取り出す内容を記述
}
$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) 多数の値のバインドを伴う&データ型の指定が必要な場合
$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。
また、こうすれば、パラメータの数が可変の場合も対応できる。
$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文内に入れておくのがよさそうです。
$res = $sth->execute();
if (PEAR::isError($res)) {
// 実行に失敗した場合の処理を記述
}
/* デフォルトの場合 */
$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) {
// 実行に失敗した場合の処理を書く
}
参考
- MDB2
- PDO
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) 接続時に指定
$dbh = MDB2::connect($connection_string);
$dbh->setFetchMode(MDB2_FETCHMODE_ASSOC);
$dbh = new PDO($connection_string);
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
(2) 実行時に指定
$dbh->setFetchMode(MDB2_FETCHMODE_ASSOC);
$sth = $dbh->prepare("SELECT aaa, bbb FROM hoge");
$res = $sth->execute();
while ($row = $res->fetchRow()) {
// 取り出す内容を記述
}
$sth = $dbh->prepare("SELECT aaa, bbb FROM hoge");
// ステートメントに対し、設定
$sth->setFetchMode(PDO::FETCH_ASSOC);
$sth->execute();
foreach ($sth->fetchAll() as $row) {
// 取り出す内容を記述
}
(3) フェッチ時に指定
$sth = $dbh->prepare("SELECT aaa, bbb FROM hoge");
$res = $sth->execute();
while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) {
// 取り出す内容を記述
}
$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
-
PDO
終わりに
MDB2は、
ことから、早めに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)