PHP から MySQL に接続する方法を解説します。
PDO
と mysqli
どっちがいいの?とか、query
と prepare
どっちを使うべき?ということも、わかりやすく説明しちゃいます。
PDO と mysqli どちらを使うべきか
PHP から MySQL に接続するためのAPIは PDO
と mysqli
の2種類あります。
結論からいうと、これから PHP を始めるならば PDO
を使いましょう。
PDO
と mysqli
はどちらもほぼ同じことができます。主な違いは次のとおりです。
PDO | mysqli | |
---|---|---|
インターフェイス | オブジェクト指向 | 手続き型、オブジェクト指向 |
mysqlnd によるノンブロッキングな非同期クエリ | 非対応 | 対応 |
クライアントサイドのプリペアドステートメント | 対応 | 非対応 |
複数ステートメント | ほとんど対応 | 対応 |
MySQL 5.1+ の全機能への対応 | ほとんど対応 | 対応 |
PDO を使うメリット
- 汎用性が高い。MySQL 以外にもいろいろなデータベースに対応しているので、これさえ覚えておけば他のデータベースにも対応できる。サポートされるデータベースはこちら。
- プリペアドステートメントが使えるので、SQLインジェクション対策がしやすい(あとでちゃんと説明します)。
mysqli を使うメリット
- MySQL 専用のAPIなので、MySQL 独自の高度な機能にも対応している。
- 手続き型で書くことができる。
というわけで、ほとんどの方は PDO
を使ったほうがいいと思います。以降、PDO
の使い方しか説明しません(強硬姿勢)。
無料で利用できるプログラミング学習サービスをお探しならば Code Lesson はいかがでしょうか。プロのエンジニアが監修した学習ロードマップで効率的に学習、AIに質問、最後にクイズで理解度をチェックできます。
PDO の使い方
接続、切断、エラー処理
MySQL に接続するためのコードをシンプルに書くと次のようになります。
//データベース接続情報
$dbuser = 'データベースユーザ名';
$dbpass = 'データベースパスワード';
$server = 'データベースサーバ名';
$db = 'データベース名';
$dsn = "mysql:host=$server;dbname=$db;";
try{
//インスタンスの生成
$dbh = new PDO($dsn, $dbuser, $dbpass);
//接続を閉じる(省略可)
$dbh = null;
//エラー処理
}catch (PDOException $e) {
exit('データベース接続失敗: ' . $e->getMessage());
}
データベース接続情報
MySQL に接続するためのユーザ名、パスワード、サーバ名、データベース名を用意しておきます。そして、DSN(Data Source Name)を組み立てておきます。
インスタンスの生成
第一引数にDSN、第二引数にユーザ名、第三引数にパスワードを入れてインスタンスを生成します。
インスタンス(オブジェクト)名は $dbh
としました。Database Handler の略だそうです。名前は何でもいいです。$pdo
を使う人も多いですよね。
接続を閉じる
ここではとにかくシンプルなコードを書いているので、何もせずに接続を閉じます。
接続を閉じるには $dbh
に null
を代入します。
これをしなければ、スクリプトの終了時に自動的に接続が閉じられます。なので、省略してもいいのですが、ちゃんと書いたほうが律儀で真面目な感じがするので私は書く派です。
エラー処理
エラーが発生した場合の処理をするために、try-catch
文を使っています。try
ブロック内の処理を実行した結果、例外が投げられるとcatch
ブロックの処理が実行されます。
PDO
はエラーが発生したときに例外( PDOException
オブジェクト)を投げます。
SELECT する方法
では、SQL を実行してみましょう。query
メソッドを使う方法とprepare
、execute
メソッドを使う方法があります。それぞれの使い方を解説した後に、どちらを使うべきか説明します。
query メソッド
//データベース接続情報
$dbuser = 'データベースユーザ名';
$dbpass = 'データベースパスワード';
$server = 'データベースサーバ名';
$db = 'データベース名';
$dsn = "mysql:host=$server;dbname=$db;";
$sql = 'SELECT * FROM table_name;';
try{
//インスタンスの生成
$dbh = new PDO($dsn, $dbuser, $dbpass);
//SQLの実行
$sth = $dbh->query($sql);
//SQLの実行結果を格納
$result = $sth->fetchAll(PDO::FETCH_ASSOC);
//接続を閉じる(省略可)
$dbh = null;
//SQLの実行結果の表示
echo '<pre>';
print_r($result);
echo '</pre>';
//エラー処理
}catch (PDOException $e) {
exit('データベース接続失敗: ' . $e->getMessage());
}
SQLの実行
query
メソッドにSQL文を渡して実行します。
オブジェクト名は $sth
にしました。Statement Handler の略です。好きな名前をつけてください。
SQLの実行結果を格納
fetchAll
メソッドでSQLの実行結果を取得して $result
に格納します。fetchAll
はSQLの実行結果のすべてを配列で返します。
fetchAll
の代わりに fetch
メソッドを使うこともできます。fetch
はSQLの実行結果を1行だけ返します。
フェッチモードの設定
fetch
, fetchAll
の引数として PDO::FETCH_ASSOC
を渡しています。これによって返す結果の形式を制御しています。
設定するモードによってprint_r($result)
の出力は次のように変わります。
//PDO::FETCH_ASSOCの場合
Array
(
[0] => Array
(
[id] => 1
[name] => まさお
[age] => 33
[created] => 2022-12-06 20:17:13
)
[1] => Array
(
[id] => 2
[name] => まさこ
[age] => 28
[created] => 2022-12-06 20:17:25
)
)
//PDO::FETCH_BOTH (デフォルト)の場合
Array
(
[0] => Array
(
[id] => 1
[0] => 1
[name] => まさお
[1] => まさお
[age] => 33
[2] => 33
[created] => 2022-12-06 20:17:13
[3] => 2022-12-06 20:17:13
)
[1] => Array
(
[id] => 2
[0] => 2
[name] => まさこ
[1] => まさこ
[age] => 28
[2] => 28
[created] => 2022-12-06 20:17:25
[3] => 2022-12-06 20:17:25
)
)
//PDO::FETCH_NUMの場合
Array
(
[0] => Array
(
[0] => 1
[1] => まさお
[2] => 33
[3] => 2022-12-06 20:17:13
)
[1] => Array
(
[0] => 2
[1] => まさこ
[2] => 28
[3] => 2022-12-06 20:17:25
)
)
prepare、execute メソッド
//データベース接続情報
$dbuser = 'データベースユーザ名';
$dbpass = 'データベースパスワード';
$server = 'データベースサーバ名';
$db = 'データベース名';
$dsn = "mysql:host=$server;dbname=$db;";
$sql = 'SELECT * FROM table_name WHERE name = :name AND age = :age;';
$name = 'まさお';
$age = 32;
try{
//インスタンスの生成
$dbh = new PDO($dsn, $dbuser, $dbpass);
//SQLの準備
$sth = $dbh->prepare($sql);
//値をバインドする
$sth->bindValue(':name', $name);
$sth->bindValue(':age', $age, PDO::PARAM_INT);
//SQLの実行
$sth->execute();
//SQLの実行結果を格納
$result = $sth->fetchAll(PDO::FETCH_ASSOC);
//接続を閉じる(省略可)
$dbh = null;
//SQLの実行結果の表示
echo '<pre>';
print_r($result);
echo '</pre>';
//エラー処理
}catch (PDOException $e) {
exit('データベース接続失敗: ' . $e->getMessage());
}
SQLの準備
prepare
メソッドを使って、SQL を実行する準備をします。この段階ではまだ実行されません。
prepare
に渡された変数$sql
の内容を見てみると、コロンで始まる文字列 :name
や:age
が確認できます。これらはプレースホルダと呼ばれるもので、この後に正式な値にバインドされます。バインドとは結びつけるとか関連付けるという意味です。
値をバインドする
bindValue
メソッドで、:name
に$name
、:age
に$age
を結びつけています。
第三引数でデータ型を指定します。指定しない場合の初期値はPDO::PARAM_STR
です。
PDO::PARAM_STR
: 文字列PDO::PARAM_INT
: 数値PDO::PARAM_BOOL
: ブール値PDO::PARAM_NULL
: NULL
SQL の実行
execute
メソッドで、SQLを実行します。
query と prepare どちらを使うべきか
query
がたった1行でSQLを実行しているのに対して、prepare
+ execute
は「準備→バインド→実行」の3段階になっていて面倒くさそうです。
しかし、面倒くさいのには理由がありまして。prepare
+ execute
を使うということはプリペアドステートメントを使うということなんです。プリペアドステートメントを使うメリットは次のとおりです。
- 同じSQL文を値を変えて使い回すことができる。
- SQLインジェクション対策になる。
特に2つめのSQLインジェクション対策が大事です。SQLインジェクションとは想定外のSQL文を実行させることによって、データベースを不正に操作する攻撃方法のことです。外部から受け取った値をSQL文に含めて実行する場合に、SQLインジェクションが発生する可能性があります。
なので、外部から受け取る値がSQL文に含まれない場合は query
、含まれる場合は prepare
+ execute
を使うという使い分けをしましょう。それが面倒ならば全部 prepare
+ execute
でやればいいと思います。全部 query
でやるのはダメ。ゼッタイ。
以降、prepare
+execute
でやる方法しか説明しません(強硬姿勢)。
INSERT する方法
//データベース接続情報
$sql = 'INSERT INTO table_name (name, age, created) VALUES(:name, :age, NOW());';
$name = 'ひろみ';
$age = 98;
//SQLの準備
$sth = $dbh->prepare($sql);
//値をバインドする
$sth->bindValue(':name', $name);
$sth->bindValue(':age', $age, PDO::PARAM_INT);
//SQLの実行
$sth->execute();
//INSERTしたidを取得
$id = $dbh->lastInsertId();
//INSERTしたレコード数を取得
$count = $sth->rowCount();
//接続を閉じる(省略可)
$dbh = null;
「準備→バインド→実行」の3段階です。SELECT
と同じですね。
INSERT した id を取得
今INSERT
したレコードのid
を取得したいときはlastInsertId
メソッドを使います。$sth
ではなく$dbh
から取得するので注意してください。
lastInsertId
を使うにはAUTO_INCREMENT
が設定されたPRIMARY KEY
のカラムが存在する必要があります。カラム名はid
だったりすることが多いですが、何でもOKです。
INSERT したレコード数を取得
挿入したレコード数を取得するにはrowCount
メソッドを使います。
UPDATE する方法
//データベース接続情報
$sql = 'UPDATE table_name SET age = :age WHERE name = :name;';
$name = 'まさお';
$age = 33;
//SQLの準備
$sth = $dbh->prepare($sql);
//値をバインドする
$sth->bindValue(':name', $name);
$sth->bindValue(':age', $age, PDO::PARAM_INT);
//SQLの実行
$sth->execute();
//UPDATEしたレコード数を取得
$count = $sth->rowCount();
//接続を閉じる(省略可)
$dbh = null;
UPDATE したレコード数を取得
変更したレコード数を取得するにはrowCount
メソッドを使います。
DELETE する方法
//データベース接続情報
$sql = 'DELETE FROM table_name WHERE id = :id;';
$id = 4;
//SQLの準備
$sth = $dbh->prepare($sql);
//値をバインドする
$sth->bindValue(':id', $id, PDO::PARAM_INT);
//SQLの実行
$sth->execute();
//DELETEしたレコード数を取得
$count = $sth->rowCount();
//接続を閉じる(省略可)
$dbh = null;
DELETE したレコード数を取得
削除したレコード数を取得するにはrowCount
メソッドを使います。
オプションを設定する
データベースハンドル$dbh
の属性を設定することによってPDOの挙動を制御することができます。方法は2つ、インスタンスの生成時に設定する方法と、インスタンスの生成後にsetAttribute
メソッドで設定する方法です。
インスタンス生成時に設定する方法
//データベース接続情報
$dbuser = 'データベースユーザ名';
$dbpass = 'データベースパスワード';
$server = 'データベースサーバ名';
$db = 'データベース名';
$dsn = "mysql:host=$server;dbname=$db;";
$options = array(
属性 => 値,
属性 => 値
);
//インスタンスの生成
$dbh = new PDO($dsn, $dbuser, $dbpass, $options);
インスタンスを生成するときの第四引数にオプションを連想配列で渡します。
利用できるオプションは下記で確認してください。
setAttribute で設定する方法
//データベース接続情報
$dbuser = 'データベースユーザ名';
$dbpass = 'データベースパスワード';
$server = 'データベースサーバ名';
$db = 'データベース名';
$dsn = "mysql:host=$server;dbname=$db;";
//インスタンスの生成
$dbh = new PDO($dsn, $dbuser, $dbpass);
//オプションの設定
$dbh->setAttribute(属性, 値);
$dbh->setAttribute(属性, 値);
設定例
//エラーが発生したときに例外を投げるようにする
//PHP7以下の場合に設定する必要があります。PHP8以上では不要です。
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
//フェッチモードを設定する
//fetch, fetchAllの引数で設定することもできます
$options = array(
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
まとめ
以上を踏まえたPDO_MySQLの使い方テンプレートがこちらです。
//データベース接続情報
$dbuser = 'データベースユーザ名';
$dbpass = 'データベースパスワード';
$server = 'データベースサーバ名';
$db = 'データベース名';
$dsn = "mysql:host=$server;dbname=$db;";
$sql = 'SELECT * FROM table_name WHERE id = :id AND name = :name;';
$options = array(
属性 => 値,
属性 => 値
);
try {
//インスタンスの生成
$dbh = new PDO($dsn, $dbuser, $dbpass, $options);
//SQLの準備
$sth = $dbh->prepare($sql);
//値をバインドする
$sth->bindValue(':id', 1, PDO::PARAM_INT);
$sth->bindValue(':name', 'まさお', PDO::PARAM_STR);
//SQLの実行
$sth->execute();
//SQLの実行結果を格納
$result = $sth->fetchAll(PDO::FETCH_ASSOC);
//INSERTしたidを取得
$id = $dbh->lastInsertId();
//レコード数を取得
$count = $sth->rowCount();
//接続を閉じる(省略可)
$dbh = null;
//エラー処理
} catch (PDOException $e) {
exit('データベース接続失敗: ' . $e->getMessage());
}
でわでわ
無料で利用できるプログラミング学習サービスをお探しならば Code Lesson はいかがでしょうか。プロのエンジニアが監修した学習ロードマップで効率的に学習、AIに質問、最後にクイズで理解度をチェックできます。
コメント