PHPの例外処理は「検知・捕捉・記録・復旧」で考える
例外は、後続処理を安全に続けるのが難しい状況を通知する仕組みです。失敗する可能性がある処理をtryで囲み、例外が発生した場合の対応をcatchに記述します。PHPでは、例外が捕捉されるまで呼び出し元へ処理が戻り、対応するcatchが見つからない場合は処理が終了します。
利用者には安全なエラーメッセージを返し、開発者向けには原因調査に必要な情報をログへ残す、というように役割を分けると障害を追いやすくなります。
try-catchで例外を捕捉する基本
tryには失敗する可能性のある処理、catchには例外発生時の処理を書きます。例外オブジェクトからメッセージや発生箇所などを確認できます。
<?php
function validateUserName(string $name): void
{
if ($name === '') {
throw new InvalidArgumentException('ユーザー名が未入力です');
}
}
try {
validateUserName('');
echo "登録処理を実行しますn";
} catch (InvalidArgumentException $e) {
// 実行例: 入力エラー: ユーザー名が未入力です
echo "入力エラー: " . $e->getMessage() . "n";
}
// catch後に処理を続けるか、中断してエラー応答を返すかは、
// その処理が業務上どれだけ重要かに応じて決めます。throwで意図的に例外を発生させる
必須値がない、許可されない状態であるなど、後続処理を安全に続けられない条件ではthrowを使って意図的に例外を発生させます。例外を発生させる層と、利用者向けの応答へ変換する層を分けると、業務ロジックと表示処理を整理できます。
<?php
function createOrder(?int $userId): string
{
if ($userId === null) {
// 調査に必要な情報だけを含め、パスワードなどは入れない
throw new RuntimeException('注文作成に必要なユーザーIDがありません');
}
return '注文を作成しました';
}
try {
echo createOrder(null);
} catch (RuntimeException $e) {
// 利用者には詳細を見せず、実際にはログへ記録して安全な応答を返す
echo "注文を作成できませんでした。n";
}finallyとログ出力で後処理と調査性を確保する
finallyには、例外の有無にかかわらず実行したい後処理を置きます。たとえば、一時的なリソースの解放や処理終了時の共通記録などです。PHPの公式マニュアルでも、tryやcatchの後にfinallyが実行される仕組みが説明されています。
| 処理 | 主な役割 |
|---|---|
| try-catch | 例外を捕捉し、失敗時の対応を行う |
| finally | 成功・失敗にかかわらず共通の後処理を行う |
| ログ出力 | エラーの概要や識別子などを残し、原因調査に役立てる |
ログには処理名、エラーの概要、必要に応じてリクエストIDや対象IDなどを残します。一方で、パスワード、アクセストークン、個人情報などの機密情報は出力しないように注意してください。
例外処理と通常のエラーハンドリングを使い分ける
入力チェックのように、呼び出し元が通常の分岐として扱える問題は、戻り値や条件分岐で処理する方法もあります。データベース障害や外部サービスの停止など、処理の継続が難しい問題には例外が適しています。
| 状況 | 処理方法の例 |
|---|---|
| 想定内の入力不備 | 戻り値や条件分岐で利用者へ再入力を促す |
| 業務処理の失敗 | 例外を発生させ、呼び出し元で応答へ変換する |
| 予期しない障害 | 共通ログへ記録し、利用者には内部情報を隠した応答を返す |
PHPのエラーをErrorExceptionへ変換する構成も可能ですが、set_error_handler()で対象にするエラーの種類や、変換後の扱いを明確にする必要があります。すべてのエラーが同じ方法で捕捉されるとは限らないため、公式マニュアルを確認しながら導入してください。
実装前後に確認したい例外処理のチェックポイント
- catchした例外を何もせず握りつぶしていないか確認する。
- ログだけで発生箇所や対象処理を追跡できるか確認する。
- 利用者向けメッセージと開発者向けログを分離する。
- パスワードやトークンなどの機密情報をログへ出力しない。
- 成功時、想定内の失敗時、予期しない障害時の3パターンをテストする。
- 本番環境ではスタック情報や内部パスを利用者へ表示しない設定を確認する。