Writing clean exception handling code doesn't have to be verbose. Instead of cluttering your code with if-statements followed by throw statements, you can use throw_if()
and throw_unless()
functions to make your code more readable and concise.
Table Of Contents
- The Problem with Traditional Exception Handling
- The Solution: Conditional Exception Functions
- Real-World Usage Patterns
- Implementation for Frameworks Without Built-in Support
- Quick Tips
- Before vs After
The Problem with Traditional Exception Handling
Traditional exception throwing often looks like this:
if ($user->is_banned) {
throw new ValidationException('User is banned');
}
if (!$user->isAuthenticated()) {
throw new UnauthorizedException('Authentication required');
}
if (empty($data['email'])) {
throw new InvalidArgumentException('Email is required');
}
This approach works but creates repetitive, verbose code that's harder to scan and maintain.
The Solution: Conditional Exception Functions
throw_if()
- Throw When Condition is True
throw_if(condition, ExceptionClass::class, 'Error message');
Examples:
// Throw if user is banned
throw_if($user->is_banned, ValidationException::class, 'User is banned');
// Throw if file is too large
throw_if(filesize($file) > 5242880, FileSizeException::class, 'File exceeds 5MB limit');
// Throw if insufficient balance
throw_if($account->balance < $amount, InsufficientFundsException::class, 'Insufficient balance');
throw_unless()
- Throw When Condition is False
throw_unless(condition, ExceptionClass::class, 'Error message');
Examples:
// Throw unless user is authenticated
throw_unless($user->isAuthenticated(), UnauthorizedException::class, 'Authentication required');
// Throw unless file exists
throw_unless(file_exists($filePath), FileNotFoundException::class, 'File not found');
// Throw unless user has permission
throw_unless($user->can('edit', $post), ForbiddenException::class, 'Permission denied');
Real-World Usage Patterns
Input Validation
function createUser($data) {
throw_unless(isset($data['email']), ValidationException::class, 'Email required');
throw_unless(isset($data['password']), ValidationException::class, 'Password required');
throw_if(strlen($data['password']) < 8, ValidationException::class, 'Password too short');
return User::create($data);
}
API Endpoint Guards
function updatePost($id, $data) {
$post = Post::find($id);
throw_unless($post, ModelNotFoundException::class, 'Post not found');
throw_unless(auth()->user()->can('update', $post), ForbiddenException::class, 'Unauthorized');
throw_if($post->is_locked, ValidationException::class, 'Post is locked');
return $post->update($data);
}
Business Logic Validation
function processPayment($order, $paymentMethod) {
throw_unless($order->isPending(), InvalidStateException::class, 'Order already processed');
throw_if($paymentMethod->isExpired(), PaymentException::class, 'Payment method expired');
throw_unless($order->total > 0, ValidationException::class, 'Invalid order total');
// Process payment logic
}
Implementation for Frameworks Without Built-in Support
If your framework doesn't provide these functions, create them:
function throw_if($condition, $exception, $message = null) {
if ($condition) {
throw new $exception($message ?? 'Condition was true');
}
}
function throw_unless($condition, $exception, $message = null) {
if (!$condition) {
throw new $exception($message ?? 'Condition was false');
}
}
Quick Tips
- Use descriptive messages: Make error messages actionable
- Choose appropriate exceptions: Use specific exception types for different error categories
- Keep conditions simple: Avoid complex logic in the condition parameter
- Chain validations: Group related conditional throws together
Before vs After
Before:
if (!$user->isActive()) {
throw new UserException('User account is inactive');
}
if ($user->loginAttempts >= 3) {
throw new SecurityException('Too many login attempts');
}
if (!$user->hasVerifiedEmail()) {
throw new ValidationException('Email not verified');
}
After:
throw_unless($user->isActive(), UserException::class, 'User account is inactive');
throw_if($user->loginAttempts >= 3, SecurityException::class, 'Too many login attempts');
throw_unless($user->hasVerifiedEmail(), ValidationException::class, 'Email not verified');
The result is cleaner, more readable code that clearly communicates intent while reducing boilerplate. Start using throw_if()
and throw_unless()
in your next project to write more expressive exception handling code!
Share this article
Add Comment
No comments yet. Be the first to comment!