throw raises an exception. You use it when your code finds a situation it cannot continue from.
<?php
function setAge(int $age): int
{
if ($age < 0 || $age > 150) {
throw new InvalidArgumentException("Age must be between 0 and 150, got $age.");
}
return $age;
}
try {
setAge(-5);
} catch (InvalidArgumentException $e) {
echo $e->getMessage();
}
Age must be between 0 and 150, got -5.
What a good message looks like
- Says what is wrong and, if possible, what was received.
- Is written for the programmer, not the visitor.
- Contains no secrets (passwords, tokens).
throw new InvalidArgumentException("Email is required"); // short, fine
throw new RuntimeException("Could not open $file for writing"); // includes the cause
throw new Exception("Error"); // too vague
throw is an expression (PHP 8.0+)
Since PHP 8, throw can be used inside an expression, which makes short checks possible.
<?php
function findUser(array $users, int $id): string
{
return $users[$id] ?? throw new OutOfBoundsException("No user with id $id");
}
$users = [1 => "Ana", 2 => "Ben"];
echo findUser($users, 2) . "\n";
try {
findUser($users, 9);
} catch (OutOfBoundsException $e) {
echo $e->getMessage();
}
Ben
No user with id 9
It also works in arrow functions and ternaries.
<?php
$check = fn($x) => $x > 0 ? $x : throw new RangeException("Must be positive");
echo $check(5);
5
Rethrowing
Inside a catch, you can throw the same exception again after you have done something with it (for example, logged it).
<?php
try {
try {
throw new RuntimeException("inner problem");
} catch (RuntimeException $e) {
echo "Logging: " . $e->getMessage() . "\n";
throw $e; // pass it on
}
} catch (RuntimeException $e) {
echo "Handled at the top: " . $e->getMessage();
}
Logging: inner problem
Handled at the top: inner problem
Throw early, catch late
Check your inputs at the top of a function and throw straight away. Handle the exception at a higher level where you know how to respond (show a message, return an HTTP error).
Exceptions are for exceptional cases
Do not use exceptions for normal control flow, such as “the loop is finished”. Use them for things that should not happen in a correct run: invalid input, missing resources, broken assumptions.
throw needs a Throwable
You can only throw objects that implement Throwable. throw "error"; is itself an error.
Exercise: validate an email
Write requireEmail(string $email): string that throws an InvalidArgumentException when the address is not valid. Test it with a valid and an invalid address.
Show a possible solution
<?php
function requireEmail(string $email): string
{
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
throw new InvalidArgumentException("Invalid email: $email");
}
return $email;
}
echo requireEmail("ana@example.com") . "\n";
try {
requireEmail("nope");
} catch (InvalidArgumentException $e) {
echo $e->getMessage();
}
ana@example.com
Invalid email: nope