PHPでのエラーチェック方法を解説【try-catchは不要です】

Programming


こんにちは。あっきーです。
21歳でニートしながら、英語とプログラミングの勉強をしています。

今回は「PHPでPDOを使ってデータベースを接続するときのエラーチェック方法」ということでお話します。

PHPでPDOを使ってデータベースに接続する方法でも少し例外処理に関して触れましたが、これを詳しく解説していきます。

特に強調したいのが、ほとんどのサイトでも紹介されているtry-catchを使った例外処理は使ってはいけないということです。

では、行きましょう。

環境
開発環境:XAMPP 7.4.11
PHP 7.4.11

参考:https://phpdelusions.net/articles/error_reporting

PHPのエラー表示設定

PHPには標準でエラーを表示する設定があります。そして個人でどのエラーを表示するかを変えることができます。
設定ファイルは主に3種類あって

  • php.ini:設定ファイル→このファイルが設定に影響する
  • php.ini.development:開発用→開発中なのでエラー表示される
  • php.ini.production:実装用→実際に公開される用なのでエラーがあっても表示されない

xamppをインストールすると初めからphp.iniという設定ファイルがあります。

これを開いて、error_reportingdisplay_errorsを見てください。
以下のように書いてあるかと思います。

error_reporting=E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE
display_errors=On

error_reporting(エラーの種類設定)

error_reportingにはいろいろ書いてありますが、これはどのエラーを表示するかを示しています。
エラータイプは主に4つあります。

  • Fatal Error:致命的なエラー→必ず対処が必要
  • Parse Error:構文エラー→カンマ(;)のつけ忘れなど、文法等にミスがある
  • Warning : 警告→致命的ではないが、対処した方が良いエラー(実行時)
  • Notice:注意→変数の未定義などの注意、致命的ではない

これらを設定をiniファイルで当てはめると

  • E_ERROR:Fatal Error
  • E_PARSE : Parse Error
  • E_WARNING : Warning
  • E_NOTICE:Notice
  • E_ALL:すべてのエラー
  • E_DEPERCATED:将来的にエラーとなり得る(警告)

これらを使って実際に表示するエラーを設定できます。開発時はE_ALLとしておけば間違いないです。

~E_NOTICEのように「~」をつけると「そのエラーは除く」という意味になります。例えば”E_ALL & ~E_NOTICE”は「すべてのエラー、ただし”注意”は除く」ということになります。

display_errors(エラー表示設定)
display_errorsOnOffで設定でき、前者はエラーを表示、後者は表示しないというように設定できます。

開発中は、これもOnにしておけば問題ないです。

ということでまとめると

  • error_reporting=E_ALL
  • display_errors=On

このように最初は設定しておきましょう。

PHPの例外処理について

PHPには例外処理というのがあって、エラーが起こる場合の処理を作ることができます。
よく使うのがTry-Catch構文なのですが、これは使いどころを注意しないといけません。
特にデータベースを使った処理に関して間違いが多いので、これに関して解説していきます。

データベース関連でTry-Catchは使っていけない

多くのサイトでPHPの例外処理を調べると、ほとんどがデータベース接続時にTry-Catchを使っているのがわかります。
具体的にはこんな感じ。


try {
    // MySQLへの接続
    $dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);

} catch (PDOException $e) {//PDOException(例外処理)をキャッチ
    $e->getMessage()
    exit;
}

Try-CatchはTryの部分で処理を行い、その中でエラーが出たら、Catch内で別の処理を行うというものです。
今回はデータベース接続をやってみて、ミスったらエラーをだして処理を終了するという形ですね。

ただ、これはやってはいけません。

理由は2つあります。

  • そもそもtry-catchを使わなくても標準でPHPがエラーを出してくれる
  • try-catchを使うと何がエラーなのかが見えにくくなる→修正しにくい

先ほどエラー設定したように、PHPは設定を元にエラーを返してくれます。特に致命的エラーの場合は処理をストップしてくれるので、わざわざ例外処理でエラー出力などしなくていいんですよ。

それだけならいいのですが、例外処理で勝手に処理を書いてしまうと、かえってエラーの原因がわかりにくくなります。

例えばこんなファイルを用意しました。



<?php
$host = 'localhost';
$db = '自分のデータベース名';
$user = '自分のユーザー名';
$pass = '自分のパスワード';
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, $user, $pass, $options);

try {
  $sql = 'ミス';
  $stmt = $pdo->query($sql);
} catch (PDOException $e) {
  die($e->getMessage());
}

「データベース接続→データベースとPHP間で情報のやり取り」というサンプルです。
$sqlにはMySQlの構文(SELECTなど)を書かないといけないのですが、今回は’ミス’と間違った構文を書いてしまっているので、当然エラーがでます。

これを実行してみるとこんなエラーが返ってきます。

SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘ミス’ at line 1

要は「’ミス’ていう構文は無いから直してね」というエラーです。

次に、try-catchを使わず素直に書いて見ます。


<?php

$host = 'dbname=phplogin;host=localhost;charset=utf8mb4';
$db = 'phplogin';
$user = 'root';
$pass = 'akiai1806';
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, $user, $pass, $options);

  $sql = 'ミス';
  $stmt = $pdo->query($sql);

結果はこんな感じ

Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘ミス’ at line 1 in C:\xampp\htdocs\test\error_sample.php:17 Stack trace: #0 C:\xampp\htdocs\test\error_sample.php(17): PDO->query(‘\xE3\x83\x9F\xE3\x82\xB9’) #1 {main} thrown in C:\xampp\htdocs\test\error_sample.php on line 17

try-catchをしなくてもエラーが出てますね。
さらに、前のものには無かった分がここでは表示されています。以下の部分です。

in C:\xampp\htdocs\test\error_sample.php:17 Stack trace: #0 C:\xampp\htdocs\test\error_sample.php(17): PDO->query(‘\xE3\x83\x9F\xE3\x82\xB9’) #1 {main} thrown in C:\xampp\htdocs\test\error_sample.php on line 17

これは「17行目がおかししですよ」という情報です。17行目はまさに$sql = 'ミス';の部分です。

つまり、どこがミスってたかが分かるようになっているんです。

try-catchを使うとどこが間違っているかが分からなくなってしまうので、エラー処理に関しては何もせず素直にほっとくのがベストです。

自作エラー処理ファイルを作り、開発者とユーザーともに適した表示をする

というわけで、基本的に何もしなければPHPが勝手にエラー表示して処理を終了させることがわかりました。

ただ、正確にはそのままでOkということでは、無くていくつか問題があります。

  • 1.errorsとErrorExceptionsの対応をしないといけない
  • 2.ユーザーにエラーを見せるのはよくない

1については、どうやらerrorsというものがあって、これが予期せぬエラー(なんだけど処理が終わらない)らしいんですね。一方で、ErrorExceptionsは予期せぬエラーだけど処理を止めるものらしいんです。

「これらに対する処理を作ってあげようね」というのが1つ目。

2.については、エラーは開発者にとっては問題ないんですが、ユーザーにとっては邪魔でしかありません。
エラーが起こって処理が止まってしまったときに、ユーザー側には「ごめん!ちょっとエラーが出た!また後で来てね!」というような表示をした方が良心的ですよね。

つまり

  • errorsは何らかの形で処理を停止させる
  • ユーザー用と開発者用にエラー表示を切り替える

というエラー処理を作っていこうということです。

これは参考サイトのプログラムをそのままお借りします。


<?php

error_reporting(E_ALL);

function myExceptionHandler ($e)
{
    error_log($e);
    http_response_code(500);
    if (ini_get('display_errors')) {
        echo $e;
    } else {
        echo "

500 Internal Server Error

An internal server error has been occurred.
Please try again later."; } } set_exception_handler('myExceptionHandler'); set_error_handler(function ($level, $message, $file = '', $line = 0) { throw new ErrorException($message, 0, $level, $file, $line); }); register_shutdown_function(function () { $error = error_get_last(); if ($error !== null) { $e = new ErrorException( $error['message'], 0, $error['type'], $error['file'], $error['line'] ); myExceptionHandler($e); } });

errorをerrorExceptionとして処理をする

前半のコードはいったん置いといて、set_error_handlerというところを見ます。
これはphpの関数の一つで、「errorsについてどうするか」というものです。

その中でthrow new ErrorException()と書いています。これは「ErrorException(例外エラー)を投げるよ」というものです。

errorsは先ほどの通り、処理が止まらない厄介なエラーなんですが、ErrorExceptionsは処理は止まるし、このように投げて別の処理を書くことができるので(try-catchのような)、便利なんです。

こうすることで、errorsの対処は完了です。

後は、このErrorExceptionsが出たときの対処なんですが、何度も言うように放置でもOKです。でも、ユーザーへの配慮が必要といいましたので、その処理を書いてあげればOKです。
それがプログラムの前半の部分です。

ErrorExceptionsの処理はユーザー用と開発者用に分ける

myExceptionHandlerは自作の関数で、ユーザー用と開発者用に表示するものを分けています。

display_errorsがOnのときは、開発モードなのでエラーをそのまま表示し、Offの場合はユーザー用に500.htmlページを表示し、「また来てね」という表示をさせています。

これをset_exception_handler関数に入れることで、「ErrorException(例外エラー)が起こったときに処理を行う」ということが可能なりました。
これで、問題はクリアです。

Fatal Errors(致命的なエラー)に関しても表示を分ける

最後にFatal Errorに関しても処理を書いておきましょう。
このエラーは処理はとまるのですが、何も設定していないとユーザー側にもエラーが見えてしまうので、先ほどと同じように表示をユーザー側と開発者側で分けます。

register_shutdown_functionは「処理が終了した際に呼ばれる」関数で、つまりFatal Errorが出たときに行われます。

後は、エラーがあれば、先ほど作ったmyExceptionHandlerに渡して、開発者にはそのままエラーを、ユーザーには500.htmlを表示させます。

このファイルを読み込めばOK

これでエラー処理ファイルは完成です。後は、別ファイルで何か処理をする際にこれを読み込めばOKです。


<?php

require('エラー処理を書いたファイル名');

//以降に様々な処理

まとめ

というわけで、今回はPHPのエラー処理についての解説をしました。
僕も理解するのに苦労しましたが、かなり大切なところなので頑張って説明しました。

後、英語の情報は有能ですね。みなさんも英語の記事を参考にプログラミングができると理解が深まると思います。

ということで、いつもありがとうございます。それでは、また。

スポンサードサーチ

オススメ英語学習用SNS "Our Dictionary"

人気記事英語学習用SNSをLaravelで作ってみた【システム解説あり】