意味のあることをするつもりでない限り、例外をキャッチするべきではありません。
「意味のあるもの」は次のいずれかです。
例外の処理
最も明白な意味のあるアクションは、たとえばエラーメッセージを表示して操作を中止することにより、例外を処理することです。
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
echo "Error while connecting to database!";
die;
}
ロギングまたは部分的なクリーンアップ
特定のコンテキスト内で例外を適切に処理する方法がわからない場合があります。「全体像」についての情報が不足している可能性がありますが、障害が発生した時点のできるだけ近くにログを記録する必要があります。この場合、キャッチ、ログ、および再スローする必要があります。
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
logException($e); // does something
throw $e;
}
関連するシナリオは、失敗した操作のクリーンアップを実行するのに適切な場所にいるが、最上位レベルでの失敗の処理方法を決定しない場合です。以前のPHPバージョンでは、これは次のように実装されていました
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
catch (Exception $e) {
$connect->disconnect(); // we don't want to keep the connection open anymore
throw $e; // but we also don't know how to respond to the failure
}
PHP 5.5でfinally
キーワードが導入されたため、クリーンアップシナリオでこれに取り組む別の方法があります。クリーンアップコードが何が起こっても(つまり、エラーと成功の両方で)実行する必要がある場合、スローされた例外を透過的に許可しながら、これを実行できるようになりました。
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
finally {
$connect->disconnect(); // no matter what
}
エラーの抽象化(例外の連鎖あり)
3番目のケースは、考えられる多くの障害をより大きな傘の下で論理的にグループ化する場合です。論理グループの例:
class ComponentInitException extends Exception {
// public constructors etc as in Exception
}
class Component {
public function __construct() {
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
throw new ComponentInitException($e->getMessage(), $e->getCode(), $e);
}
}
}
この場合、Component
データベース接続を使用して実装されていることをユーザーに知られたくない(おそらく、オプションを開いたままにし、将来的にファイルベースのストレージを使用したい)。したがって、の仕様でComponent
は、「初期化に失敗した場合はComponentInitException
スローされます」と記述されています。これにより、のコンシューマはComponent
、予期されるタイプの例外をキャッチできると同時に、デバッグコードがすべての(実装に依存する)詳細にアクセスできるようになります。
より豊富なコンテキストの提供(例外チェーンを使用)
最後に、例外のコンテキストを増やしたい場合があります。この場合、エラーが発生したときに何をしようとしていたかについてより多くの情報を保持する別の例外に例外をラップすることは理にかなっています。例えば:
class FileOperation {
public static function copyFiles() {
try {
$copier = new FileCopier(); // the constructor may throw
// this may throw if the files do no not exist
$copier->ensureSourceFilesExist();
// this may throw if the directory cannot be created
$copier->createTargetDirectory();
// this may throw if copying a file fails
$copier->performCopy();
}
catch (Exception $e) {
throw new Exception("Could not perform copy operation.", 0, $e);
}
}
}
このケースは上記に似ています(そしておそらくこの例は思いつく最善のものではないでしょう)が、より多くのコンテキストを提供するポイントを示しています。例外がスローされた場合、ファイルのコピーが失敗したことを示しています。しかし、なぜ失敗したのですか?この情報は、ラップされた例外で提供されます(例がはるかに複雑な場合は、複数のレベルが存在する可能性があります)。
これを行うことの価値はUserProfile
、ユーザープロファイルがファイルに保存され、トランザクションセマンティクスをサポートするためにオブジェクトを作成するとファイルがコピーされるシナリオを考える場合に示されます。コミットするまでプロファイルのコピー。
この場合、あなたがした場合
try {
$profile = UserProfile::getInstance();
}
その結果、「ターゲットディレクトリを作成できませんでした」という例外エラーが発生したため、混乱する権利があります。この「コア」例外をコンテキストを提供する他の例外のレイヤーにラップすると、エラーの処理がはるかに簡単になります(「プロファイルのコピーの作成に失敗しました」->「ファイルのコピー操作に失敗しました」->「ターゲットディレクトリを作成できませんでした」)。