2016/04/11 14:14:15

PDOはデフォルトではエラー出力をしないです

目次(クリックするとジャンプします)
  • 1:ハマりにハマる
  • 2:ちょっとした説明
  • 3:PDO::ERRMODE_SILENT
  • 4:PDO::ERRMODE_WARNING
  • 5:PDO::ERRMODE_EXCEPTION
  • 6:まとめ

ハマりにハマる

PDOでMySQLを扱っていたのですが、あるSQLがいっこうに受け付けてもらえません。

エラーも出ないのでSQL文が間違っているわけでもなさそうなのですが…。

不具合かなと思い、いろいろ調べているとPDOはデフォルトでエラーを吐かないという衝撃の事実を知りました。

なんだってーーーー!!!

PHP.iniの設定でdisplay_Erroryesにしていたので出力されるものだと思っていました。

セキュリティを考えるとエラーを吐かない方がいいのはわかっているつもりでしたが、設定は別なんですね。

PDOでは属性を設定することで、エラーの挙動や出力を変更することが可能です。

ちょっとした説明

PDOの属性設定はPDO::setAttribute()で行います。エラー以外にもさまざまな属性が存在しています。

属性の設定は「どの属性の設定か」の指定に続き、「その属性の設定」を行います。ですのでPDO::setAttribute()の引数は2つになります。

エラーの属性はPDO::ATTR_ERRMODEです。

以下の説明はエラー属性の設定についてです。

PDO::ERRMODE_SILENT

デフォルトのエラー設定。エラーメッセージを出力しない。エラー内容は保持している。 エラ−を取得するにはPDO::errorCode()PDO::errorInfo()を使います。

PDO::ERRMODE_SILENTについてはデフォルトなのでsetAttribute()しなくてもいいのですが、今回は例を示すために明示的に記述しています。

<?php
$DB_DSN      = "mysql:host=172.17.0.2;dbname=db";
$DB_USER     = "hoge";
$DB_PASSWD   = "hogepass";

//データベースハンドルの取得
$dbHandle    = new \PDO($DB_DSN, $DB_USER, $DB_PASSWD);
//属性の設定
$dbHandle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
//  エラーが出るSQLをわざと実行
$dbHandle->query("INSERT INTO zzdb.p_string (site_id, ps) VALUES ('sss', 'shiosndo');");

var_dump($dbHandle->errorCode());
var_dump($dbHandle->errorInfo());

こんな感じの出力が得られます。

//PDO::errorCode()の出力
string(5) "HY000" 

//PDO::errorInfo()の出力
array(3) { 
  [0]=>
  string(5) "HY000"
  [1]=>
  int(1366)
  [2]=>
  string(60) "Incorrect integer value: 'sss' for column 'site_id' at row 1"
} 

PDO::errorInfo()ではエラーコード、エラー番号、エラーメッセージが配列で得られます。

PDO::ERRMODE_WARNING

いつものブラウザに表示されるエラーを出力させるにはこの設定がいいと思います。この設定が返すのはE_WARNINGなので、スクリプトは停止されません。

さきほどのPDO::ERRMODE_SILENTと同じくPDO::errorCode()およびPDO::errorInfo()でエラーコード等を取得することも可能です。

開発中はこの設定が便利でいいかもしれません。

<?php
$DB_DSN      = "mysql:host=172.17.0.2;dbname=db";
$DB_USER     = "hoge";
$DB_PASSWD   = "hogepass"

//データベースハンドルの取得
$dbHandle    = new \PDO($DB_DSN, $DB_USER, $DB_PASSWD);
//属性の設定
$dbHandle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
//  エラーが出るSQLをわざと実行
$dbHandle->query("INSERT INTO zzdb.p_string (site_id, ps) VALUES ('sss', 'shiosndo');");

var_dump($dbHandle->errorCode());
var_dump($dbHandle->errorInfo());

こんな感じの出力になります。

<br />
<b>Warning</b>:  PDO::query(): SQLSTATE[HY000]: General error: 1366 Incorrect integer value: 'sss' for column 'site_id' at row 1 in <b>/var/www/html/run.php</b> on line <b>58</b><br />
string(5) "HY000"
array(3) {
  [0]=>
  string(5) "HY000"
  [1]=>
  int(1366)
  [2]=>
  string(60) "Incorrect integer value: 'sss' for column 'site_id' at row 1"
}

PDO::ERRMODE_EXCEPTION

最後の設定は例外を投げます。もっともエラーハンドリングに向いている設定かもしてれません。

この設定をしているとPDOExceptionがスローされます。ですからtrycatch構文でキャッチすれば処理を分岐させることが可能です。

例外オブジェクトからも取得できますが、PDO::errorCode()およびPDO::errorInfo()でエラーコード等を取得することも可能です。

<?php
$DB_DSN      = "mysql:host=172.17.0.2;dbname=db";
$DB_USER     = "hoge";
$DB_PASSWD   = "hogepass"

//データベースハンドルの取得
$dbHandle    = new \PDO($DB_DSN, $DB_USER, $DB_PASSWD);
//属性の設定
$dbHandle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
 //  エラーが出るSQLをわざと実行
    $dbHandle->query("INSERT INTO zzdb.p_string (site_id, ps) VALUES ('sss', 'shiosndo');");
} catch (PDOException $e) {
    echo "ErrorMes : " . $e->getMessage() . "\n";
    echo "ErrorCode : " . $e->getCode() . "\n";
    echo "ErrorFile : " . $e->getFile() . "\n";
    echo "ErrorLine : " . $e->getLine() . "\n";
}

var_dump($dbHandle->errorCode());
var_dump($dbHandle->errorInfo());

こんな出力になります。

ErrorMes : SQLSTATE[HY000]: General error: 1366 Incorrect integer value: 'sss' for column 'site_id' at row 1
ErrorCode : HY000
ErrorFile : /var/www/html/run.php
ErrorLine : 60
string(5) "HY000"
array(3) {
  [0]=>
  string(5) "HY000"
  [1]=>
  int(1366)
  [2]=>
  string(60) "Incorrect integer value: 'sss' for column 'site_id' at row 1"
}

まとめ

エラーが出ないので、エラー箇所に気づくのにすごく時間がかかってしまいました。特にSQLってまだ経験が浅いので結構間違えちゃうんですよね…。

でもこれでエラーがちゃんと確認できるようになったのできっとプログラミングが捗るはずです。

PDOでは…

  • デフォルトでエラー出力がされない設定になっている
  • PHP.inidisplay_errorを設定していても出力はされない。
  • エラーを出力するには属性を設定する必要がある。
  • 設定はPDO::ERRMODE_SILENTPDO::ERRMODE_WARNINGPDO::ERRMODE_EXCEPTIONの3つ