LoginSignup
1
1

More than 3 years have passed since last update.

HTML・PHP・MySQLだけで作る間違い探しゲーム【④DB接続設定ファイルを作る】

Last updated at Posted at 2020-10-26

4. DB接続設定


<< 前の記事 【③問題表示ページを作る】
3. 問題表示ページの実装
  3-1. 無効なアクセスの拒否
  3-2. リセット回数の計測
  3-3. 問題用文字配列の設定
   3-3-1. 文字ペア配列の選択
   3-3-2. 文字ペア配列及び文字ペアのシャッフル
   3-3-3. 正解文字と不正解文字配列の設定
    3-3-3-1. 正解文字の設定
    3-3-3-2. 不正解文字配列の設定
   3-3-4. 問題用文字配列の生成
  3-4. 開始時刻の記録
  3-5. 選択肢の描写


次の記事 >> 【⑤結果表示ページを作る】
5. 結果表示ページ
  5-1. 無効なアクセスの拒否
  5-2. 回答時間の算出
  5-3. 各変数への格納
  5-4. 保存する回答時間とカウント数の上限設定
  5-5. 正解/不正解による表示メッセージの分岐
  5-6. ランキングへの登録
   5-6-1. ランキングテーブルの構成
   5-6-2. ランキングへの登録

この記事ではDB接続設定ファイルdb_connect.php」を作成します。

必要となったところでこのファイルを呼び出すことで、いちいちPDOオブジェクトの属性などを記述しなくても済みます。

db_connect.php
<?php

// 定数定義
const PDO_DSN = 'mysql:host=localhosts;dbname=[FILTERED];charset=utf8mb4';
const USERNAME = '[FILTERED]';
const PASSWORD = '[FILTERED]';

// DB接続
try {
    $dbh = new PDO(PDO_DSN, USERNAME, PASSWORD, [
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => false,
    ]);
} catch (PDOException $e) {
    header('Content-Type: text/plain; charset=UTF-8', true, 500);
    exit('DB接続に失敗しました' . '<br>' . PHP_EOL . $e->getMessage());
}

冒頭でも述べましたが、DB構築については本記事では扱いません。
接続にはPDOオブジェクトを使用しています。

PDOのインスタンスを作成する為にはPDO()を用いて

  • 第1引数・・・DSN(必須)
  • 第2引数・・・ユーザーネーム(任意)
  • 第3引数・・・パスワード(任意)
  • 第4引数・・・PDOオブジェクトの属性(任意)

を渡す必要があります。

今回のコードでは第1〜第3引数を定数としてを定義してますが、変数定義や引数に直接渡す書き方でも問題ありません。

また、DBにはプレイヤーが入力した名前を保存します。このため絵文字を取り扱う可能性があるので、文字コードはutf8ではなくマルチバイト対応のuft8mb4を指定しています。

コピペする際は、[FILTERED]を適切な値に置き換えて下さい。

PHPマニュアル「PHP: 接続、および接続の管理 - Manual
PHPマニュアル「PHP: PDO::__construct - Manual

4-1. PDOオブジェクトの属性

属性に関する公式リファレンスは以下を参照願います。
PHPマニュアル「PHP: PDO::setAttribute - Manual

また下記Qiita記事が体系的にまとめられており、とても参考になります。ぜひ一読を!

Qiita「PHPでデータベースに接続するときのまとめ - Qiita」 by @mpyw さん
Qiita「【PHP超入門】クラス~例外処理~PDOの基礎 - Qiita」 by @7968 さん

4-1-1. フェッチ形式の指定

db_connect.php
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC

PDO::FETCH_ASSOC: は、結果セットに 返された際のカラム名で添字を付けた配列を返します。
引用元:PHPマニュアル「PHP: PDOStatement::fetch - Manual

SQL文で得られた結果をフェッチする際の形式を指定しています。
カラム名がキーとなった配列で返ってくるので直感的に操作しやすいです。
この形式がもっともスタンダードな気がします。

4-1-2. エラーモードの設定

db_connect.php
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION

エラーモードは3種類あり、PDO::ERRMODE_EXCEPTIONはエラー発生時にPDOException例外を発生させます。

また、エラーコードだけでなくその関連情報も返してくれるようにもなります。

どれに設定するかは開発する物や状況によると思いますが、今回はPDO::ERRMODE_EXCEPTIONを選択しました。

4-1-2-1. エラーモードの違いによるエラー文の違い

以下は本ゲームの「result.php」の56行目において、テーブルのカラムとは異なるカラム名を指定したINSERT文を実行した際のエラー文の比較です。

  • PDO::ERRMODE_SILENT
Fatal error: Uncaught Error: Call to a member function execute() on boolean in /path/to/result.php:58 Stack trace: #0 {main} thrown in /path/to/result.php on line 58
  • PDO::ERRMODE_WARNING
Warning: PDO::prepare(): SQLSTATE[42S22]: Column not found: 1054 Unknown column 'namae' in 'field list' in /path/to/result.php on line 57

Fatal error: Uncaught Error: Call to a member function execute() on boolean in /path/to/result.php:58 Stack trace: #0 {main} thrown in /path/to/result.php on line 58
  • PDO::ERRMODE_EXCEPTION
Fatal error: Uncaught PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'namae' in 'field list' in /path/to/result.php:57 Stack trace: #0 /path/to/result.php(57): PDO->prepare('INSERT INTO ran...') #1 {main} thrown in /path/to/result.php on line 57

デフォルトのPDO::ERRMODE_SILENTでは単に「SQL文を実行するexecute()でエラーが起こった」と表示されるだけであるのに対し、PDO::ERRMODE_WARNINGPDO::ERRMODE_EXCEPTIONでは「指定されたカラムが見つからない」とより具体的なエラー文となっています。

また、PDO::ERRMODE_WARNINGではE_WARNINGメッセージが追加されており、PDO::ERRMODE_EXCEPTIONでは例外がPDOExceptionクラスになっているという違いもあります。

PHPマニュアル「エラーおよびエラー処理 - Manual

4-1-3. エミュレーションの設定

db_connect.php
        PDO::ATTR_EMULATE_PREPARES => false

プリペアドステートメントエミュレーションに関する設定です。

4-1-3-1. プリペアドステートメント

直訳すると「準備された記述」になりますが、一文で言うと
後から値を入れる部分を別の文字・単語などで仮置きしたSQL文
かなと思います。

プリペアドステートメントを用いる利点は以下の2つです。

  • 後から入れる値のみを変更しながら、何度も使用できる
  • SQLインジェクション対策になる

下記記事もご参照ください。

Qiita「【PHP超入門】クラス~例外処理~PDOの基礎 - プリペアドステートメント - Qiita」 by @7968 さん

4-1-3-2. エミュレーション

truefalse(ONかOFF)かによってプリペアドステートメントの挙動が変化します。

上述のプリペアドステートメントにおいて、仮置きする文字・単語のことをプレースホルダーといい、

  • 疑問符プレースホルダー
  • 名前つきプレースホルダー

の2種類があり、今回の記事では前者に統一しています。

さらにプレースホルダー自体にも

  • 静的プレースホルダー
  • 動的プレースホルダー

の2種類のタイプがあり、エミュレーションをfalseとすることで静的プレースホルダーが用いられるようになります。

静的プレースホルダーを選択する理由としては、よりセキュアである為です。

こちらに関しては下記記事が参考になります。

Qiita「【PHP超入門】クラス~例外処理~PDOの基礎 - 静的プレースホルダと動的プレースホルダ - Qiita」 by @7968 さん

また、エミュレーションのON/OFFによる挙動の違いに関しては下記記事が参考になります。

Qiita「PHPでデータベースに接続するときのまとめ - エミュレーションに関するまとめ - Qiita」 by @mpyw さん

更に、下記質問もご参考までに。

Teratail「`PDO::ATTR_EMULATE_PREPARES => false`は必要か?

4-2. 例外発生時の処理

db_connect.php
} catch (PDOException $e) {
    header('Content-Type: text/plain; charset=UTF-8', true, 500);
    exit('DB接続に失敗しました' . PHP_EOL . $e->getMessage() . PHP_EOL);
}

catch()を用いることで、発生した例外を捕捉することができます。

PHPマニュアル「PHP: 例外(exceptions) - Manual

PDOException発生した例外のクラス名で、$e発生した例外のクラスから作成したインスタンスを代入する変数となっています。

4-2-1. HTTPヘッダの送信

エラーメッセージを表示する際、webブラウザにエラーメッセージを「単なるテキストである」と解釈してもらうため、header()を用いてMINEタイプを設定しています。

また、第3引数にはHTTPレスポンスステータスコードを指定し、
サーバー側のエラーであることを明示しています。

MDN web docs「MIME タイプ (IANA メディアタイプ) - HTTP | MDN
MDN web docs「HTTP レスポンスステータスコード - HTTP | MDN

4-2-2. 処理の中断とエラーメッセージの表示

続くexit()によって、

  • 自作のエラー文
  • 発生した例外に関するエラーメッセージ

を出力させ、後続の処理を中断させています。

このファイルを呼び出しているということは、DBのアクセスを必要とする処理を行うはずなので、接続に失敗した場合には後続の処理も失敗する可能性が高いためです。

PHPマニュアル「PHP: exit - Manual

例外に関するエラーメッセージは、$e->getMessage()によってインスタンスのgetMessage()メソッドにアクセスすることで取得しています。

PHPマニュアル「PHP: Error::getMessage - Manual

PHP_EOLPHPの定義済み定数で、プラットフォームの行末文字を意味します(EOLは"End Of Line"の略です)。

この定数は、OSを自動判定して行末文字を選定してくれますので、サーバーのOSを気にすることなく改行して表示させることができます。

PHPマニュアル「PHP: 定義済みの定数 - Manual

4-3. DBの切断について

PDOインスタンスを格納した変数にNULLを代入することで、DBから切断させることができます(今回の場合なら$dbh = NULL)。

接続を閉じるには、他から 参照されていないことを保障することでオブジェクトを破棄する 必要があります。それには、オブジェクトを保持している変数に対して NULL を代入します。
引用元:PHPマニュアル「PHP: 接続、および接続の管理 - Manual

しかし、

明示的にこれを行わなかった場合は、スクリプトの終了時に自動的に 接続が閉じられます。
引用元:PHPマニュアル「PHP: 接続、および接続の管理 - Manual

とも書かれており、あえて記述する必要はないかと思われます。

下記もご参照下さい。

Qiita「PHPでデータベースに接続するときのまとめ - データベース接続の切断 - Qiita」 by @mpyw さん

これでDBの接続設定が完了したので、次は結果ページの実装に入ります。


<< 前の記事 【③問題表示ページを作る】
3. 問題表示ページの実装
  3-1. 無効なアクセスの拒否
  3-2. リセット回数の計測
  3-3. 問題用文字配列の設定
   3-3-1. 文字ペア配列の選択
   3-3-2. 文字ペア配列及び文字ペアのシャッフル
   3-3-3. 正解文字と不正解文字配列の設定
    3-3-3-1. 正解文字の設定
    3-3-3-2. 不正解文字配列の設定
   3-3-4. 問題用文字配列の生成
  3-4. 開始時刻の記録
  3-5. 選択肢の描写


次の記事 >> 【⑤結果表示ページを作る】
5. 結果表示ページ
  5-1. 無効なアクセスの拒否
  5-2. 回答時間の算出
  5-3. 各変数への格納
  5-4. 保存する回答時間とカウント数の上限設定
  5-5. 正解/不正解による表示メッセージの分岐
  5-6. ランキングへの登録
   5-6-1. ランキングテーブルの構成
   5-6-2. ランキングへの登録

1
1
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
1
1