例外の命名の参考にするために Laravel の例外すべて漁ってみた
はじめに
クラス名,変数名,パッケージ名など,プログラミングには ”英語での命名” が不可欠ですよね.
でも,
- こういうの,プログラミング的な英語ではなんて言ったらいいか分からない(適切な単語が思い浮かばない)
- そもそも日本語でもうまく命名できない
など,困ったことはありませんか?
僕自身,命名にはいつも悩まされますが,最近よく例外の命名についてとても悩んでしまします.
たとえば
「データベースを検索した結果,該当するレコードが見つからなかった」という場合に投げる例外を考えます.
Laravel の Eloquent Bulder を使う場合,
$user = User::query()->findOrFail(1);
というコードを書いたとき,id
が 1 のユーザーが見つからなかった場合,ModelNotFoundException::class
がスローされます.
でも,もしこのような例外をを自分で考えないといけなかったとき,
CouldNotFindModelException
なんて名前にしたことがある方も,いらっしゃるのではないでしょうか?
GitHub で ModelNotFoundException
と検索すると 58 万件,CouldNotFindModelException
と検索すると 5 件ヒットしました.
文法的に間違いではないし,意味は通るのですが,圧倒的に前者の命名が多いようです.
今回は,例外の命名に頭を悩ます僕が,Laravel 実装内にある例外を列挙し,今後の命名に使えそうな英単語やイディオムをまとめてみようと思います.
例外の例を集めてみた
今回参考にするリポジトリは, laravel/framework
です.Laravel の本体ですね.
Laravel プロジェクトは,src/illuminate/
配下に各パッケージごとにディレクトリが分かれていますので,パッケージごとにみていきます.
また,Laravel プロジェクトは illuminate/
直下に各パッケージの 「実装」があり,illuminate/contracts/
配下に「インタフェース」があります.
例外は実装側にもインタフェース側にも定義されていますので,どちらに置かれているのか分かるように,インタフェース側にあるものに関しては「HogeException(Contract)」という風に表記したいと思います.
なお,いくつかのパッケージには,パッケージ固有の例外が定義されていませんでした.
Auth
- AuthenticationException
- 認証の例外.例えば Guard で認証に失敗するとスローされます.
- AuthorizationException
- 認可の例外.例えば FormRequest 内での認可に失敗するとスローされます.
Broadcast
- BroadcastException
- この手の例外は,パッケージの中でも高いレイヤーの例外です.下から伝播してきた例外を最終的に外に出す時などによく使われ,Broadcast に限った話ではありません.
Cache
- LockTimeoutException(Contract)
-
block()
を使って指定時間待機した後ロックを取得しようとした際,ロックが取得できなかったときにスローされます.
-
Collections
- ItemNotFoundException
-
firstOrFail()
などでアイテムが見つからなかった場合などにスローされます.
-
- MultipleItemsFoundException
-
sole()
で,取得が期待されるアイテム数が 1 つだけのはずなのに,複数取得されてしまった場合などにスローされます.
-
Container
-
EntryNotFoundException
- コンテナから渡された型を解決できなかった場合にスローされます.
-
CircularDependencyException(Contract)
- 依存関係にループが生じたときにスローされます.
-
BindingResolutionException(Contract)
- 指定された型の具象クラスをインスタンス化する際,渡されたクラスが存在しない場合などにスローされます.
Database
-
ClassMorphViolationException
- Polymorphic リレーションにおいて,モデルに Morph マップが定義されていない場合にスローされます.
-
LazyLoadingViolationException
- LazyLoading (遅延ロード)をしようとしたが,遅延ロードが無効になっている場合にスローされます.
-
LostConnectionException
-
reconnect()
でデータベースに再接続できなかった場合にスローされます.
-
-
MultipleColumnsSelectedException
-
scalar()
で 1 レコード目の 1 カラム目を返そうとしたとき,レコードが複数取得されてしまった場合にスローされます.
-
-
MultipleRecordsFoundException
-
sole()
で,取得が期待されるレコード数が 1 つだけのはずなのに,複数取得されてしまった場合などにスローされます.
-
-
RecordsNotFoundException
-
sole()
で,取得が期待されるアイテム数が 1 つだけのはずなのに,何も取得されなかった場合などにスローされます.
-
-
RelationNotFoundException
-
getRelation()
でリレーションを取得しようとしたが,対象のリレーションが定義されていなかったときなどにスローされます.
-
-
ModelNotFoundException
-
findOrFail()
でモデルを取得しようとしたがモデルが見つからなかったときなどにスローされます.
-
-
MassAssignmentException
-
fill()
などでアトリビュートを一括代入しようとした際,fillable
に該当カラムが含まれておらず一括代入ができなかったときなどにスローされます.
-
-
JsonEncodingException
- Model で
toJson()
をした際,json_encode()
でエラーが起きたときなどにスローされます.
- Model で
-
InvalidCastException
- カラムの値をキャストしようとした際,対象のキャストタイプが定義されていなかったときなどにスローされます.
Encryption
-
MissingAppKeyException
- アプリケーションの暗号化キーが指定されていない場合にスローされます.暗号化キーというのは,
.env
にあるAPP_KEY
のことですね.生成するには,すれば良いです.(蛇足)$ php artisan key:generate Application key set successfully.
- アプリケーションの暗号化キーが指定されていない場合にスローされます.暗号化キーというのは,
-
EncryptException(Contract)
- データを暗号化できなかったときなどにスローされます.
-
DecryptException(Contract)
- データを復号化できなかったときなどにスローされます.
Filesystem
-
LockTimeoutException(Contract)
- 指定ファイルの共有ロックを取得しようとした際,そのパスで共有ロックを取得できなかったときにスローされます.
-
FileNotFoundException(Contract)
- 指定されたパスにファイルが存在しなかったときにスローされます.
Http
-
ConnectionException
-
send()
で Http リクエストを送った際,通信に問題が生じ正常に処理できなかったときなどにスローされます.
-
-
RequestException
- サーバーやクライアントに問題が起きてリクエストが正常に処理されなかったとき(ステータスが 400 番など)などにスローされます.
-
HttpClientException
-
ConnectionException
やRequestException
はこの例外を継承しています.
-
-
HttpResponseException
-
abort()
にレスポンスが渡されたときなどにスローされます.これは一番外側の ExceptionHandler でハンドルされて,最終的にレスポンスに変換されます.
-
-
PostTooLargeException
- リクエストに載せたデータが,POSTデータに許可される最大サイズを超えた場合(デフォルトで8M)にスローされます.
-
ThrottleRequestsException
- Throttle機能は決められた時間範囲内のアクセス可能数を制限できるもので,受け取ったリクエスト数が制限に達したときにスローされます.
Queue
-
MaxAttemptsExceededException
- Job が何度も失敗して再試行されたり,実行時間が長くてタイムアウトになったりしたときにスローされます.
-
ManuallyFailedException
- Job の実行に失敗したときにスローされます.
-
InvalidPayloadException
- Payload を作成する際,Payload を Json エンコードできないときにスローされます.
-
EntityNotFoundException(Contract)
- キューイング可能なエンティティが,指定 ID で見つからなかったときにスローされます.
Redis
- LimiterTimeoutException(Contract)
-
block()
を使って指定時間待機した後ロックを取得しようとした際,指定したタイムアウト時間以内にロックを取得できなかったときにスローされます.
-
Routing
-
BackedEnumCaseNotFoundException
- Backed Enum (値に依存した列挙型) が見つからなかったときにスローされます.
-
InvalidSignatureException
- メール認証を実装した際,https 環境で弾かれるアレです.
-
StreamedResponseException
- ファイルダウンロード時,レスポンス取得時に発生した例外をラップしています.
-
UrlGenerationException
-
route()
ヘルパなどで URL を生成する際,必要なパラメータを渡していない時などにスローされます.
-
Session
- TokenMismatchException
- CSRF トークンが一致しなかったときなどにスローされます
Validation
-
UnauthorizedException
- FormRequest 内での認可が通らなかったときなどにスローされます.
-
ValidationException
- バリデーションに失敗したとき(不正な値を受け取った時)にスローされます.
結果,43 種類もの例外が Laravel で定義されていることがわかりました!
パターンから学ぶ例外の命名
上記の 43 種類の例外を眺めていると,なんとなく今後の例外で使えそうなパターンや英単語がいくつか見つかったので,まとめます.
認証・認可系
認証には
AuthenticationException
UnauthenticatedException
認可には
AuthorizationException
UnauthorizedException
あたりが使えそうです.それぞれ表現が2種類ありますが,使い方としてはどちらも同じで良さそうですね.
「リソースが見つからない」パターン
モデルが見つからない,エンティティが見つからないなど,検索の結果リソースが見つからない場合は「〇〇NotFoundException」が使えそうです.
「一致しない」パターン
パスフレーズやトークンなど,値の照合の結果一致しない場合は「〇〇MismatchException」が使えそうです.
「〇〇過ぎる」パターン
なにかのデータが「長過ぎる」,「多すぎる」場合には 「too」が使えそうですね.語順としては,
名詞 + too + 形容詞
が良さそうです.例えば,
- TextTooLongException
- ModelTooManyException
などです.
また,今回挙げた Laravel の例外には出てきませんでしたが,先に Too + 形容詞
を持ってくるパターンもよく見かけますね.
上記の例を使うと,
- TooLongTextException
- TooManyModelException
という風になります
「複数見つかってしまった」パターン
データの取得において,1つだけ取得したいときに複数取得してしまった(取得できた)場合には「Multiple〇〇FoundException」が使えそうです.
ここで,似た意味の「Many」を使って,「Many〇〇FoundException」としたくなるかもしれません.
Many は,とにかく量が沢山であるイメージなのに対し,Multiple は同じようなもの(同じ属性のもの),並列のものというイメージがあります.
似たような特徴を持つデータ群の中から1つだけ取得したかったが複数取れてしまった,みたいな場合には Multiple を使うのが良いかなと思いました.
また,Multiple と意味が近い「Duplicate,Duplicated」もよく見る単語ですね.
これは「重複している」という意味になります.こちらは動詞ですので,使い方としては「Duplicated〇〇Exception」という風になります.
「設定と違うことが起きた」パターン
とある動作を無効にする設定なのに動作してしまったり,その逆だったり,なにかの制約に違反してしまった場合には「Violation」という単語が使いやすそうです.
今回の例でいうと,LazyLoadingViolationException
は,遅延ロードをしようとしたが無効になっていた場合にスローされます.
他には,例えばユニーク制約のついたデータが重複して存在してしまった場合に UniqueConstraintViolationException
みたいな風に使えそうです.
「無効なものを扱う」パターン
無効なシグネチャ,無効なペイロード,無効な型変換...などなど,Laravel にある例外でも「Invalid」という単語は多く見受けられました.
Invalid は「無効な」という意味があります.
- InvalidOperation(無効な操作)
- InvalidAction (無効な動作)
- InvalidSymbol(無効なシンボル)
- InvalidValue(無効な値)
などなど,Invalid はとても使い所の多い単語ですね.
「値が足りない」パターン
値が設定してあることを期待しているのにそれが無い(足りない)場合,「Missing」が使えるかもしれません.
- MissingParameter(パラメータが足りない)
- MissingConfiguration(設定が足りない)
- MissingValue(値が足りない)
などです.
今回 Laravel で取り上げた MissingAppKeyException
も,本来設定してあるべき APP_KEY
に値が無い場合にスローされるものですよね.
まとめ
今回は Laravel の本体である「Illuminate」にある沢山のパッケージで定義される例外を見ていきました.
様々な場面でスローされる例外を集める中で,よく使われる単語,パターンがいくつか見えてきました.
意外と命名に困る例外ですが,Laravel のように大きなプロジェクトのソースコードを覗いてみると,案外参考になる例外が見つかるかもしれません.
本記事で扱った例外,そして命名の参考になりそうな特徴のまとめが,なにかのお役に立てれば幸いです.
Discussion