- カテゴリ: helper
- 掲載バージョン: Laravel 12・PHP 8.4
- 名前空間 / FQCN / コマンド:
throw_if()(グローバル関数) - 関連: throw_unless / abort_if / abort_unless / rescue / report
- 変更履歴: Laravel 5.5 で追加(以降継続)
要点(TL;DR)
- 条件が true のときに例外を投げる簡潔記法。
throw_if($cond, MyException::class, 'msg')- 罠:HTTP用の中断は
abort_if、例外の生成コストはクロージャで遅延させる、戻り値は基本null。
概要
throw_if は if ($cond) { throw ... } の糖衣構文です。業務ロジックの前提チェック(早期リターン)で可読性を上げます。例外クラス名・インスタンス・あるいは例外を返すクロージャを渡せます。
構文 / シグネチャ
throw_if(
bool $condition,
\Throwable|string|callable $exception = RuntimeException::class,
mixed ...$parameters
): void
- 引数(表)
| 引数 | 型 | 必須 | 既定値 | 説明 |
|---|---|---|---|---|
$condition | bool | ✓ | — | true のとき例外を投げる条件 |
$exception | Throwable | string | callable | RuntimeException::class | 例外インスタンス/例外クラス名/例外を返すクロージャ | |
...$parameters | mixed | — | 例外クラス名を渡した場合のコンストラクタ引数 |
- 戻り値:
void(例外を投げなければ実質null) - 例外/副作用:条件が真なら
$exceptionに基づく例外を throw。他の副作用はなし。
使用例
最小例
<?php
use RuntimeException;
$user = null;
// 条件が真(ユーザー不在)なら例外
throw_if(is_null($user), RuntimeException::class, 'User not found');
// ここには到達しない(上で例外が投げられる)
echo "OK";
実務例(サービス層での前提チェック)
<?php
use App\Models\Order;
use App\Exceptions\PaymentNotAuthorized;
use Illuminate\Support\Facades\DB;
function capturePayment(int $orderId): void
{
$order = Order::query()->with('payment')->find($orderId);
// 例外の生成コストを遅延(必要時のみ new)
throw_if(
! $order || ! $order->payment?->isAuthorized(),
fn () => new PaymentNotAuthorized("Order #{$orderId} is not authorized.")
);
DB::transaction(function () use ($order) {
$order->payment->capture();
$order->markAsPaid();
});
}
よくある落とし穴・注意
- HTTP中断と混同しない:コントローラで 404/403 を返したい場合は
abort_ifを使う(HTTP Exception によるレスポンス)。 - 例外生成のコスト:重いメッセージ整形や依存取得を伴う場合は クロージャで渡して遅延評価。
- 戻り値に依存しない:式の値として使わない(例外を投げない限り
null)。 - 検証との役割分担:入力検証はバリデーション(FormRequest 等)。
throw_ifはアプリ内部の状態不整合の検出に。
代替・関連APIとの比較
if (...) { throw ... }(純PHP):最も汎用。throw_ifは短く書けて意図が明確。throw_unless($cond, ...):条件が false のときに投げる対称 API。読みやすい方を選択。abort_if($cond, 403):HTTP レイヤの中断(ステータス+レスポンス)に最適。API/コントローラで使用。rescue():例外を握りつぶしてデフォルト値を返す用途で逆方向。
テスト例(Pest)
<?php
use App\Exceptions\PaymentNotAuthorized;
it('throws when not authorized', function () {
$fn = fn () => throw_if(true, PaymentNotAuthorized::class, 'NG');
$fn();
})->throws(PaymentNotAuthorized::class, 'NG');
it('does nothing when condition is false', function () {
expect(throw_if(false, RuntimeException::class, 'won\'t throw'))->toBeNull();
});
トラブルシュート(エラー別)
| 症状/エラー | 原因 | 対処 |
|---|---|---|
ArgumentCountError | 例外クラス名を渡したがコンストラクタ引数が足りない | クラスのシグネチャに合わせて ...$parameters を追加 |
| 望まない 500 が返る | コントローラでビジネス条件の失敗を throw_if にしている | API なら abort_if や適切な HTTP 例外へ置換 |
| ログがノイズ化 | 例外で制御フローを多用 | 想定内(バリデーション等)は例外ではなく通常の分岐に寄せる |
参考リンク
- Laravel Docs(Helpers:
throw_if,throw_unless,abort_if): https://laravel.com/docs/12.x/helpers - Laravel Framework ソース(
helpers.php): https://github.com/laravel/framework/blob/12.x/src/Illuminate/Support/helpers.php - PHP Manual(Exceptions): https://www.php.net/manual/en/language.exceptions.php
- HTTP Exceptions(Symfony コンポーネント由来): https://symfony.com/doc/current/controller.html#managing-errors-with-http-exceptions

