throw_if

Throws the given exception when the condition is truthy, collapsing a guard clause to one line.

Signature
throw_if(mixed $condition, mixed $exception = 'RuntimeException', mixed ...$parameters): mixed

Parameters

ParameterTypeDescription
$$conditionmixedEvaluated for truthiness; the exception is thrown when it is truthy.
$$exceptionoptional\Throwable|\Closure|class-string|string= 'RuntimeException'An exception instance, a closure building one, a class name to construct, or a message string.
$...$parametersoptionalmixedForwarded to the exception constructor, or to the closure when one is given.

Return value

mixedThe condition itself when nothing is thrown, so the call can be used inline.

Errors

Throws the resolved exception whenever the condition is truthy; a string that does not name an existing class becomes the message of a RuntimeException.

throw_if() replaces the four-line if (...) { throw new ...; } block with one expression. The second argument can be an exception instance, a class-string, a plain message string, or a Closure that builds the exception. Everything after it is forwarded to the exception constructor, or to the closure.

How the second argument is resolved

  • A Closure is called with the trailing parameters and its return value is treated as the exception.
  • A string naming an existing class is instantiated with the trailing parameters.
  • Any other string becomes the message of a RuntimeException.
  • An object is thrown as-is; trailing parameters are ignored.

What trips people up

The condition is tested for truthiness, not identity. throw_if('0') does not throw, because '0' is falsy in PHP, while throw_if('false') does, because a non-empty string is truthy. Cast to bool when the source might return something odd.

The default exception is RuntimeException with no message, so throw_if($cond) on its own throws something a reader cannot diagnose. Always give it a class or a message.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Reject an unauthorized action at the top of a controller method.
  • Guard against an impossible state in a service before doing any work.
  • Fail fast on a malformed payload inside a queued job's handle().

Frequently asked questions

How do I pass a custom message to throw_if()?

Pass the exception class as the second argument and the message as the third: throw_if($cond, AuthorizationException::class, 'Not allowed.'). Every argument after the second is forwarded to the exception's constructor, so you can also pass a code and a previous exception.

What does throw_if() return when it does not throw?

It returns the condition unchanged, which lets you use it as an expression — for example $ok = throw_if($problem, ...) or chaining it inside a larger statement. On the throwing path nothing is returned.

Why did throw_if() throw a RuntimeException instead of my exception class?

Because the string you passed does not name a class that exists, so throw_if() treated it as a message and wrapped it in a RuntimeException. This is almost always a typo or a missing import — use MyException::class instead of a quoted string so the mistake becomes a compile-time error.

More examples

Building the exception with a closure

The closure form is the one to use when the message needs interpolation, because the closure is only invoked when the condition is truthy — the string concatenation costs nothing on the happy path. The trailing arguments are passed to the closure, not to a constructor.

Example · php
<?php

class QuotaExceeded extends RuntimeException {}

$used = 120;
$limit = 100;

try {
    throw_if(
        $used > $limit,
        fn (int $used, int $limit) => new QuotaExceeded("Used {$used} of {$limit} credits."),
        $used,
        $limit
    );
} catch (QuotaExceeded $e) {
    echo $e->getMessage(), "\n";   // Used 120 of 100 credits.
}

An authorization guard in a controller

AuthorizationException is rendered as a 403 by Laravel's exception handler, so this one line produces the correct HTTP response without an if block or a manual abort(). The guard sits at the top of the method where a reader looks for preconditions.

Example · php
<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\Auth;

class PostController extends Controller
{
    public function destroy(Post $post)
    {
        throw_if(
            $post->user_id !== Auth::id(),
            AuthorizationException::class,
            'You are not allowed to delete this post.'
        );

        $post->delete();

        echo "deleted post {$post->id}\n";

        return response()->noContent();
    }
}

The class-name typo trap

Because class_exists() returns false for the misspelled name, the string falls through to the message branch and you get a RuntimeException whose message is the class name you meant to throw. Nothing warns you, so a catch block written for the real class silently stops matching. Always write ExceptionClass::class rather than a quoted string.

Example · php
<?php

try {
    throw_if(true, 'App\Exceptions\QuotaExceded');   // note the missing 'e'
} catch (Throwable $e) {
    echo get_class($e), "\n";      // RuntimeException
    echo $e->getMessage(), "\n";   // App\Exceptions\QuotaExceded
}

Discussion

  • Be the first to comment on this lesson.