PHP-DB - 10. PDO例外

続いて PDO ライブラリの例外クラスである PDOException クラスについて学習します。PDOライブラリとのやりとりの中で、予期せぬ自体が起こりうる可能性があります。たとえば接続先のデータベースがダウンしていたり、データベースの認証に失敗したりするケースです。他にも実行するSQLの構文に誤りが存在する、というケースも考えられるでしょう。 PDO ライブラリは このような予期せぬ自体に対して PDOException をスローできます。

SQL構文の不具合

ここではまずSQL構文に誤りがある場合の PDO ライブラリの挙動について確認しておきましょう。簡単なSQLを実行する次のプログラム( pdo8.php )を作成します。

<?php
$dsn = "sqlite:eldb.sqlite3";
$username = null;
$passwd = null;
$pdo = new PDO($dsn, $username, $passwd);

$sql = "select id, title from categories";
// $sql = "select id, title from categorie";
$st = $pdo->query($sql);
var_dump($st);

// $row = $st->fetch();
// var_dump($row);

このプログラムではいくつかのコードをコメントアウトしています。のちほど、これらのコメントアウトを解除(コメントイン)することで、実行結果にどのような変化が起こるかを確認します。

このプログラムでは select 文を定義して $pdo->query($sql) を実行し、戻り値である PDOStatement インスタンスを var_dump 関数で出力しています。現時点のプログラムには特に誤りはないので、データベースに正常に接続できた場合は、次のような実行結果が表示されるでしょう。

$ php pdo8.php
object(PDOStatement)#2 (1) {
  ["queryString"]=>
  string(32) "select id, title from categories"
}

実行結果から var_dump 関数によって PDOStatement インスタンスが表示されているのがわかります。

次にコメントアウトしている部分を解除して、次のようにプログラム( pdo8.php )のSQLを修正します。

<?php
$dsn = "sqlite:eldb.sqlite3";
$username = null;
$passwd = null;
$pdo = new PDO($dsn, $username, $passwd);

// $sql = "select id, title from categories";
$sql = "select id, title from categorie";
$st = $pdo->query($sql);
var_dump($st);

// $row = $st->fetch();
// var_dump($row);

ここでは select 文に指定しているテーブル名( categorie )にタイプミスがあります。 PDO インスタンスの query メソッドは不正なSQLを受け取ると戻り値に論理値の false を返します。

このようなケースでプログラムを実行すると次のような結果が表示されるでしょう。

$ php pdo8.php
bool(false)

実行結果から $pdo->query($sql) 呼び出しの結果、 PDOStatement インスタンスではなく論理値の false が返却されていることがわかります。

このような戻り値の変化は気づきにくく、プログラムの不具合の原因になりやすいものです。そこでPDOライブラリは例外( PDOException )をスローするように変更できます。

PDOのエラーレポート設定

PDO インスタンスの PDO::ATTR_ERRMODE 属性を指定することで、さきほどのような異常時の振る舞いを変更できます。

エラーレポート設定 エラー時の動作
PDO::ERRMODE_SILENT エラーコードのみ設定する(デフォルト)
PDO::ERRMODE_WARNING E_WARNINGを発生させる
PDO::ERRMODE_EXCEPTION 例外を投げる

デフォルトの PDO::ERRMODE_SILENT の場合、 PDO インスタンスの errorCode メソッドや errorInfo メソッドによってエラーコードを取得できます。

ここでは PDO クラスの setAttribute メソッドを使って、 PDO::ATTR_ERRMODE 属性に PDO::ERRMODE_EXCEPTION を指定することで、異常時に例外( PDOException )をスローするように変更してみましょう。さきほどのプログラム( pdo8.php )を次のように実装します。

<?php
$dsn = "sqlite:eldb.sqlite3";
$username = null;
$passwd = null;
$pdo = new PDO($dsn, $username, $passwd);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// $sql = "select id, title from categories";
$sql = "select id, title from categorie";
$st = $pdo->query($sql);
var_dump($st);

// $row = $st->fetch();
// var_dump($row);

上記にようにエラーレポート設定を変更しておくことで、 $pdo->query($sql) 呼び出し時に例外( PDOException )をスローできます。それではプログラムを実行してみましょう。

$ php pdo8.php

Fatal error: Uncaught PDOException: SQLSTATE[HY000]: 
General error: 1 no such table: categorie in 
/Users/your_name/Desktop/code-php-db/pdo8_3.php:10
Stack trace:
#0 /Users/your_name/Desktop/code-php-db/pdo8_3.php(10): PDO->query('select id, titl...')
#1 {main}
  thrown in /Users/your_name/Desktop/code-php-db/pdo8_3.php on line 10

ここではメッセージを折り返して表示しています。

実行結果の Fatal error のメッセージに Uncaught PDOException という内容を確認できます。以降はスローされた PDOException を適切に処理する方法を学習します。

まとめ

  • 例外とは、プログラムにおける予期せぬ事態のこと
  • SQLの実行時にはSQLの構文の誤り、データベース接続の失敗、制約の違反など様々な予期せぬ事態が起こりうる
  • PDO インスタンスはエラーレポート設定を変更することで異常時に PDOException をスローできる