カスタム例外階層
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 67/91。
PHPの組み込み例外クラスは一般的なシナリオをカバーしますが、実際のアプリケーションでは、より具体的なエラー型が必要になることがよくあります。カスタム例外階層を作成すると、ドメインに合った例外を定義し、さまざまな具体性のレベルでそれらを捕捉できます。
カスタム例外を作成するには、Exception クラスまたはそのサブクラスのいずれかを単純に拡張します。
<?php
class PaymentException extends Exception {}
class InsufficientFundsException extends PaymentException {
public function __construct(
private float $required,
private float $available
) {
parent::__construct("Need $required but only $available available");
}
public function getShortfall(): float {
return $this->required - $this->available;
}
}
InsufficientFundsException が Exception を直接ではなく、PaymentException を extends していることに注目してください。これにより、1つの catch ブロックですべての支払い関連の例外を捕捉したり、特定の例外を個別に処理したりできる階層が作成されます。
カスタム例外には、エラーに関連する追加のプロパティやメソッドを含めることができます。ここでは、getShortfall() によって、一般的な例外では提供できない有用な情報が得られます。
<?php
class Wallet {
public function __construct(private float $balance) {}
public function pay(float $amount): void {
if ($amount > $this->balance) {
throw new InsufficientFundsException($amount, $this->balance);
}
$this->balance -= $amount;
}
}
適切に設計されたexception階層によって、コードの保守性が高まります。exceptionsを捕捉するときは、必要に応じて広範囲にも具体的にも対応でき、exceptionオブジェクト自体が、何が問題だったのかについて意味のあるコンテキストを保持しています。
チャレンジ
簡単さまざまな種類の注文失敗を処理する、カスタム例外階層を備えた注文処理システムを構築しましょう。何が問題だったのかについて意味のあるコンテキストを持つ例外を作成し、より正確で有益なエラー処理を実現します。
コードを4つのファイルに分けて整理します。
OrderException.php:ExceptionをextendsするbaseのOrderExceptionclassをCreateします。これはすべての注文関連例外のparentとして機能し、必要に応じて1つのcatch blockで任意の注文問題をcatchできるようにします。OutOfStockException.php:OrderExceptionをextendsするOutOfStockExceptionclassをCreateします。このexceptionは、constructorでproduct nameとrequested quantityを受け取る必要があります。これらのprivate propertiesにはconstructor promotionを使用します。"[productName] is out of stock (requested: [quantity])"という形式のmessageを指定してparent constructorをCallします。product nameを返すgetProductName(): stringmethodをAddします。InvalidQuantityException.php:OrderExceptionをextendsするInvalidQuantityExceptionclassをCreateします。このexceptionは、private propertyとしてconstructor promotionを使用し、constructorでquantity valueを受け取る必要があります。"Invalid quantity: [quantity]"というmessageを指定してparent constructorをCallします。quantity valueを返すgetQuantity(): intmethodをAddします。main.php:3つすべてのexception filesをIncludeします。2つの入力、つまりproduct nameとquantity(intに変換する必要があるstring)を受け取ります。quantity valueに基づいて、適切なexceptionをthrowします。
- quantityが1未満の場合は、
InvalidQuantityExceptionをthrowします - quantityが100より大きい場合は、
OutOfStockExceptionをthrowします(在庫不足をシミュレート) - Otherwise、
"Order placed: [quantity]x [productName]"をprintします
hierarchyを使用してexceptionsをCatchします。まず
InvalidQuantityExceptionをcatchし、exceptionのmessageとgetQuantity()methodを使用して"Quantity error: [message] (value: [quantity])"をprintします。次にOutOfStockExceptionをcatchし、exceptionのmessageとgetProductName()methodを使用して"Stock error: [message] (product: [productName])"をprintします。- quantityが1未満の場合は、
このhierarchyにより、特定のexceptionsを個別にhandleしながら、baseのOrderException classを通じてすべての注文関連の問題をcatchすることもできます。各exceptionに用意されたcustom methodsによって、genericなexceptionでは提供できないコンテキストが得られます。
自分で試してみよう
<?php
// すべての例外ファイルをインクルードする
require_once 'OrderException.php';
require_once 'OutOfStockException.php';
require_once 'InvalidQuantityException.php';
// 入力を読み取る
$productName = trim(fgets(STDIN));
$quantity = intval(trim(fgets(STDIN)));
// TODO: try-catchを使用して注文処理を扱う
// tryブロック内:
// - quantity < 1 の場合、InvalidQuantityExceptionをスローする
// - quantity > 100 の場合、OutOfStockExceptionをスローする
// - それ以外の場合、"Order placed: [quantity]x [productName]"を出力する
//
// 最初にInvalidQuantityExceptionをキャッチする:
// - getMessage()とgetQuantity()を使用して"Quantity error: [message] (value: [quantity])"を出力する
//
// OutOfStockExceptionをキャッチする:
// - getMessage()とgetProductName()を使用して"Stock error: [message] (product: [productName])"を出力する
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: PHPオンラインコンパイラ