PHPからMySQLに接続するための最適な方法

PHPからMySQLに接続するための最適な方法

こんにちはー!むちょこです。

今回は、「MySQLって何?」というところからPHPに接続するためのベストプラクティスを見つける比較材料をご紹介します。

MySQLとは

MySQLは関係データベース管理システム(RDBMS)の1種で、PHPからデータを扱うときに最もよく選択されるDBの1つです。

MySQLに保存されたデータは、MySQLサーバにアクセスしSQL文を実行することで閲覧・操作することができます。

MEMO

MySQLのSQL文について学びたい方は、よかったらこちらをご覧ください。

https://note.com/muchoco/m/me63e16bd3c3a

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

しかし機能にはいくつか違いあります。公式マニュアルから抜粋してみましたのでご覧ください。

mysqliPDO_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のバージョンによって異なるため、常に明示するようにすると安心です。

PDO::ATTR_EMULATE_PREPARESの設定例
$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()の使用例
$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()で複数クエリを一度に送信することができます。

PDO_MySQLでのmulti query使用例
$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の使い方

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();
MEMO

mysqliクラスについてもっと知りたい方は、こちらの公式マニュアルをご覧ください✨

https://www.php.net/manual/ja/class.mysqli.php

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;
MEMO

PDO_MySQLについてもっと詳しく知りたい方は、こちらの公式マニュアルをご覧ください✨

https://www.php.net/manual/ja/class.pdo.php

非公式ですが、こちらの記事も情報がまとまっていておすすめです✨

https://phpdelusions.net/pdo