こんにちはー!むちょこです。
今回は、「MySQLって何?」というところからPHPに接続するためのベストプラクティスを見つける比較材料をご紹介します。
MySQLとは
MySQLは関係データベース管理システム(RDBMS)の1種で、PHPからデータを扱うときに最もよく選択されるDBの1つです。
MySQLに保存されたデータは、MySQLサーバにアクセスしSQL文を実行することで閲覧・操作することができます。
MySQLへのアクセスやSQL文の実行は、コマンドラインから手動で行うことも可能ですし、PHP等のプログラミング言語から行うことも可能です。
ここではもちろんPHPからの接続・操作方法をご紹介します。
PHPからMySQLへ接続するための2つの方法
最新のPHPでは、MySQLへ接続するための選択肢が2つあります。
mysqliとPDO_MySQLです。
mysqli
mysqliは、PHPからMySQLを操作するための多くの機能を備えた拡張モジュールです。
また、手続き型かオブジェクト指向を選択することができるのが大きな特徴の1つです。基本的にはオブジェクト指向を選択するのがモダンなやり方ですが、古いmyql関数から移行する場合など、状況によっては手続き型の方が便利なときもあります。
PDO_MySQL
PDOは、PHPからDBを操作するためのインターフェイスです。PDO_MySQLは、そのPDOインターフェイスを実装したMySQL用のドライバです。
MySQL以外のDBドライバもあるため、DBに囚われずシステムを設計・実装することができます。
mysqliとPDO_MySQLの違い
PHPの公式マニュアルでは、mysqliとPDO_MySQLには全体的なパフォーマンスの違いはほとんどないと記されています。
全体的なパフォーマンスは、どれもほぼ同じです。 拡張モジュール自体のパフォーマンスが PHP のウェブリクエストの実行時間に及ぼす影響はごくわずかで、 たいていは 0.1% 程度に過ぎません。
https://www.php.net/manual/ja/mysqlinfo.api.choosing.php
しかし機能にはいくつか違いあります。公式マニュアルから抜粋してみましたのでご覧ください。
mysqli | PDO_MySQL | |
利用可能なPHPのバージョン | 5.0以上 | 5.1以上 |
手続き型のインターフェイス | 対応 | 非対応 |
mysqlnd によるノンブロッキングな非同期クエリ | 対応 | 非対応 |
クライアントサイドのプリペアドステートメント | 非対応 | 対応 |
複数ステートメント | 対応 | ほとんど対応 |
MySQL 5.1以降のすべて機能のサポート | 対応 | ほとんど対応 |
この表だけだとわかりづらいかもしれないので、少しずつ補足していきます。
手続き型のインターフェイス
mysqliの項目でも少しご紹介しましたが、mysqliではオブジェクト指向だけでなく手続き型のインターフェイスも利用することができます。
$mysqli = new mysqli("example.com", "user", "password", "database");
$result = $mysqli->query("SELECT 'Bonjour, cher utilisateur de MySQL !' AS _message FROM DUAL");
$row = $result->fetch_assoc();
echo htmlentities($row['_message']);
※ 上記のコードは、公式マニュアルから引用しています。
$link = mysqli_connect("example.com", "user", "password", "database");
$result = mysqli_query($link, "SELECT 'Bonjour, cher utilisateur de MySQL !' AS _message FROM DUAL");
$row = mysqli_fetch_assoc($result);
echo htmlentities($row['_message']);
mysqlnd によるノンブロッキングな非同期クエリ
ノンブロッキングとは、入出力処理がすぐできない状態のときは待たずにreturnされる仕組みです。
非同期とは、他の処理のタイミングに関わらず行われる処理のことです。
mysqliにはMYSQLI_ASYNCオプションやmysqli_poll()関数など、mysqlnd上で利用できるノンブロッキング・非同期機能が備わっています。
クライアントサイドのプリペアドステートメント
PDO_MySQLでは、サーバサイドのプリペアドステートメント(静的プレースホルダ)だけでなく、クライアントサイドのプリペアドステートメント(動的プレースホルダ)も利用可能です。
PDO::ATTR_EMULATE_PREPARES属性の値がtrueならクライアントサイド、falseならサーバサイドのプリペアドステートメントが有効になります。
どちらがデフォルトになっているかはPHPのバージョンによって異なるため、常に明示するようにすると安心です。
$dbh = new PDO($dsn, $username, $passwd);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
複数ステートメント
mysqliでは、mysqli::multi_query()またはmysqli_multi_query()を使うことで複数のステートメントを一度に送信することができます。
$mysqli->multi_query("
SELECT * FROM users;
INSERT INTO users(id, email) VALUES(3, '[email protected]');
SELECT COUNT(*) FROM users;
");
PDO_MySQLの場合は、PDO::ATTR_EMULATE_PREPARES属性の値をtrueにするとquery()やprepare()、execute()で複数クエリを一度に送信することができます。
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$dbh->query("SELECT * FROM users;
INSERT INTO users(id, email) VALUES(3, '[email protected]');
SELECT COUNT(*) FROM users;");
mysqliの使い方
$host = 'localhost';
$username = 'my_user';
$passwd = 'my_password';
$dbname = 'my_db';
// 接続
$mysqli = new mysqli($host, $username, $passwd, $dbname);
// 接続エラーの確認
if ($mysqli->connect_errno) {
printf("接続に失敗しました: %s\n", $mysqli->connect_error);
exit();
}
// 結果を返さないクエリの実行(テーブルの作成)
$sql = "CREATE TABLE users (
id INT(11) AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(256) UNIQUE)";
if ($mysqli->query($sql) === TRUE) {
printf("usersテーブルは正常に作成されました\n");
}
// 結果セットを返すクエリの実行(SELECTクエリ)
if ($result = $mysqli->query("SELECT name FROM events LIMIT 10")) {
// 結果の出力
while ($event = $result->fetch_object()) {
printf("%s\n", $event->name);
}
// 結果セットの開放
$result->close();
}
// 接続を閉じる
$mysqli->close();
PDO_MySQLの使い方
$dsn = 'mysql:dbname=my_db;host=localhost';
$username = 'my_user';
$passwd = 'my_password';
try {
// 接続
$dbh = new PDO($dsn, $username, $passwd);
// 結果を返さないクエリの実行(テーブルの作成)
try {
$sql = "CREATE TABLE users (
id INT(11) AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(256) UNIQUE)";
if ($dbh->query($sql) !== FALSE) {
printf("usersテーブルは正常に作成されました\n");
}
} catch (PDOException $e) {
echo 'エラーが発生しました: ' . $e->getMessage();
}
// 結果セットを返すクエリの実行(SELECTクエリ)
if ($stmt = $dbh->query("SELECT name FROM events LIMIT 10")) {
// 結果の出力
while ($event = $stmt->fetch()) {
printf("%s\n", $event['name']);
}
}
} catch (PDOException $e) {
echo 'エラーが発生しました: ' . $e->getMessage();
}
// 接続を閉じる
$dbh = null;
PDO_MySQLについてもっと詳しく知りたい方は、こちらの公式マニュアルをご覧ください✨
https://www.php.net/manual/ja/class.pdo.php
非公式ですが、こちらの記事も情報がまとまっていておすすめです✨