throw_unless
Throws the given exception when the condition is falsy, the positive form of a throw_if guard.
throw_unless(mixed $condition, mixed $exception = 'RuntimeException', mixed ...$parameters): mixedParameters
| Parameter | Type | Description |
|---|---|---|
$$condition | mixed | Evaluated for truthiness; the exception is thrown when it is falsy. |
$$exceptionoptional | \Throwable|\Closure|class-string|string= 'RuntimeException' | An exception instance, a closure building one, a class name to construct, or a message string. |
$...$parametersoptional | mixed | Forwarded 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 falsy; a string that does not name an existing class becomes the message of a RuntimeException.
throw_unless() is literally throw_if(! $condition, $exception, ...$parameters) followed by return $condition. Everything about how the exception argument is resolved is identical to throw_if(): a Closure is invoked with the trailing parameters, an existing class name is constructed with them, any other string becomes a RuntimeException message, and an object is thrown untouched.
When to reach for it
Pick it when the guard reads better stated positively — throw_unless($user->isAdmin(), ...) instead of throw_if(! $user->isAdmin(), ...). There is no behavioural reason to prefer one over the other; it is purely about removing the negation from the reader's path.
What trips people up
The negation is a plain !, so it throws on every falsy value, not just false. 0, '0', '', [] and null all throw. A method that returns an empty collection to mean "nothing matched" will therefore raise here even though the call succeeded — an empty Collection is falsy in a boolean test only via count(), so pass ->isNotEmpty() or another real boolean.
On the non-throwing path it returns the condition, so it doubles as a validate-and-assign: $id = throw_unless($request->input('id'), ...).
Example
When to use it
- Require an authenticated administrator before running a destructive action.
- Assert that a lookup returned something before using the result.
- Validate a positively-phrased business rule at the top of a service method.
Frequently asked questions
What is the difference between throw_if() and throw_unless()?
throw_unless($cond, ...) is exactly throw_if(! $cond, ...) — same exception resolution, same return value, same behaviour. Choose whichever removes the negation from the line you are writing.
Does throw_unless() throw when the condition is 0 or an empty array?
Yes. It negates with PHP's ! operator, so every falsy value throws: false, 0, 0.0, '', '0', [] and null. Pass an explicit boolean when a falsy value is a legitimate result.
What does throw_unless() return when it does not throw?
It returns the condition itself, unchanged, which lets you use it as a validate-and-assign expression such as $id = throw_unless($request->input('id'), 'id is required.').
More examples
Validate and assign in one expression
Because throw_unless() returns the condition untouched when it does not throw, the same expression both guards and assigns. Note that this only works when the condition is the value you want — a boolean comparison would assign true, not the data.
<?php
$payload = ['reference' => 'INV-2026-0042'];
// Returns the condition, so it can stand in for the assignment.
$reference = throw_unless(
$payload['reference'] ?? null,
InvalidArgumentException::class,
'A reference is required.'
);
echo $reference, "\n"; // INV-2026-0042An authorization guard in a controller
Reading the guard as "unless the user can view it, throw" is closer to how the rule is stated in a specification than the negated throw_if form. Laravel's handler turns AuthorizationException into a 403 response.
<?php
namespace App\Http\Controllers;
use App\Models\Invoice;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\Auth;
class InvoiceController extends Controller
{
public function download(Invoice $invoice)
{
throw_unless(
Auth::user()->can('view', $invoice),
AuthorizationException::class,
'You are not allowed to access this resource.'
);
echo "streaming invoice {$invoice->id}\n";
return response()->download($invoice->path());
}
}The empty-collection trap
The first call throws because an empty Collection evaluates as falsy, even though the lookup itself worked. Whenever the condition is an object, an array or a number rather than a boolean, spell out the test — ->isNotEmpty(), count($x) > 0, $x !== null — instead of relying on truthiness.
<?php
$matches = collect([]);
// Wrong: an empty Collection is falsy, so this throws on a legitimate
// "nothing matched" result.
try {
throw_unless($matches, RuntimeException::class, 'Lookup failed.');
} catch (RuntimeException $e) {
echo 'threw: ', $e->getMessage(), "\n";
}
// Right: decide explicitly what "success" means.
var_dump(throw_unless($matches instanceof \Illuminate\Support\Collection, 'Not a collection.'));
Discussion