Error & Exception Handling
Understand PHP error levels, try-catch blocks, custom exception handling, and debugging strategies for robust backend engineering.
Downloaded from PrepIQ (https://prepiq.online)
Q1. What is the difference between an error and an exception in PHP?
Since PHP 7, both Errors and Exceptions implement the Throwable interface, but they represent different kinds of problems.
| Aspect | Error | Exception |
|---|---|---|
| Represents | Internal engine problems - type mismatches, undefined functions, memory issues | Application-level problems you anticipate and handle - invalid input, failed DB calls |
| Typically thrown by | The PHP engine itself | Your own code or a library |
| Common subclasses | TypeError, DivisionByZeroError, ArgumentCountError | InvalidArgumentException, RuntimeException, PDOException |
| Should you catch it? | Sometimes, for graceful degradation, but often indicates a bug to fix | Yes, this is the expected way to handle recoverable failures |
Interview tip: Mention that both extend the Throwable interface, so a single catch (Throwable $e) block can catch either - useful as a last-resort safety net at the top of an application.
Q2. How does try-catch-finally work in PHP?
The try block contains code that might fail. The catch block handles the exception if one is thrown. The finally block always runs, whether an exception occurred or not - used for cleanup.
function readFile(string $path): string {
$handle = fopen($path, 'r');
try {
if (!$handle) {
throw new RuntimeException('Cannot open file');
}
return fread($handle, filesize($path));
} catch (RuntimeException $e) {
echo 'Error: ' . $e->getMessage();
return '';
} finally {
if ($handle) {
fclose($handle);
}
}
}| Block | Runs when |
|---|---|
| try | Always attempted first |
| catch | Only if a matching exception type is thrown |
| finally | Always, even if there's a return statement or the exception is unhandled |
Interview tip: Emphasize that finally runs even when the try or catch block contains a return statement - a common trick question.
Q3. How do you create and use custom exception classes in PHP?
Create a class that extends Exception (or a more specific built-in subclass) to represent domain-specific error conditions with extra context.
class InsufficientFundsException extends Exception {
public function __construct(
private float $requested,
private float $available
) {
parent::__construct(
"Requested {$requested} but only {$available} available"
);
}
public function getShortfall(): float {
return $this->requested - $this->available;
}
}
function withdraw(float $amount, float $balance): void {
if ($amount > $balance) {
throw new InsufficientFundsException($amount, $balance);
}
}
try {
withdraw(500, 200);
} catch (InsufficientFundsException $e) {
echo $e->getMessage();
echo 'Shortfall: ' . $e->getShortfall();
}Interview tip: Point out that custom exceptions let you catch specific business errors separately from generic ones, and can carry structured data (like getShortfall() here) instead of just a string message.
Q4. What is the difference between catching Exception and catching Throwable?
Throwable is the top-level interface implemented by both Error and Exception. Catching Exception only catches application-level exceptions; catching Throwable catches everything, including engine-level Errors like TypeError.
try {
strlen(); // ArgumentCountError - an Error, not an Exception
} catch (Exception $e) {
echo 'This will NOT catch it';
} catch (Throwable $e) {
echo 'This WILL catch it: ' . $e->getMessage();
}| Catch type | Catches Exception subclasses? | Catches Error subclasses? |
|---|---|---|
| catch (Exception $e) | Yes | No |
| catch (Throwable $e) | Yes | Yes |
Interview tip: Recommend using catch (Throwable $e) only at the outermost boundary of an app (e.g. a global handler) to prevent white-screen crashes, not as a routine replacement for specific exception catching.
Q5. Does the finally block always execute? What are the exceptions to this rule?
The finally block runs in almost all cases - after normal completion, after a caught exception, after an uncaught exception propagates further, and even if try/catch contains a return statement.
| Scenario | Does finally run? |
|---|---|
| Try block completes normally | Yes |
| Exception thrown and caught | Yes |
| Exception thrown and NOT caught (propagates up) | Yes, then the exception continues to propagate |
| return statement inside try or catch | Yes, finally runs before the function actually returns |
| exit() or die() called inside try | No, finally is skipped - the script terminates immediately |
| Fatal PHP error (e.g. out of memory) | No, script terminates immediately |
Interview tip: The exit()/die() case is the detail that separates a strong answer from a memorized one - always mention it.
Q6. How do you handle multiple exception types in a single catch block?
PHP allows catching multiple exception types in one catch block using the pipe | separator, avoiding repeated identical catch bodies.
try {
processPayment();
} catch (InsufficientFundsException | CardDeclinedException $e) {
echo 'Payment failed: ' . $e->getMessage();
logPaymentFailure($e);
} catch (Throwable $e) {
echo 'Unexpected error occurred';
logCriticalError($e);
}| Approach | When to use |
|---|---|
| Single catch with | | Multiple exception types need identical handling |
| Multiple separate catch blocks | Each exception type needs different handling logic |
| Catch order | PHP checks catch blocks top to bottom - put more specific exception types before general ones like Throwable |
Interview tip: Mention the catch order rule explicitly - putting a general catch (Throwable) before a specific one makes the specific one unreachable, which is a real bug PHP will not warn you about at compile time in older versions.
Q7. What is set_error_handler() and set_exception_handler() in PHP?
These functions let you override PHP's default handling of errors and uncaught exceptions with your own logic - commonly used to log issues centrally instead of showing raw output.
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
error_log("Error [$errno]: $errstr in $errfile on line $errline");
return true; // prevents PHP's default handler from also running
});
set_exception_handler(function (Throwable $e) {
error_log('Uncaught: ' . $e->getMessage());
http_response_code(500);
echo 'Something went wrong. Please try again later.';
});| Function | Handles |
|---|---|
| set_error_handler() | Traditional PHP errors/warnings/notices (E_WARNING, E_NOTICE, etc.) |
| set_exception_handler() | Any exception that was thrown but never caught by your code |
Interview tip: Note that frameworks like Laravel already register their own global handlers internally (App\Exceptions\Handler), so in framework code you rarely call these directly - but understanding them is essential for plain PHP and legacy codebases.
Q8. What are common built-in exception classes in PHP (SPL exceptions)?
PHP's Standard Library (SPL) provides a hierarchy of exception classes for common scenarios, so you don't need to write a custom exception for every case.
| Class | Use case |
|---|---|
| InvalidArgumentException | A function received an argument of the wrong type or value |
| OutOfRangeException | An illegal index was requested, e.g. array index out of bounds |
| LengthException | A length value is invalid, e.g. an empty required string |
| RuntimeException | An error that can only be detected at runtime, e.g. a failed API call |
| LogicException | An error in program logic that should be fixed by the developer, e.g. calling a method in the wrong state |
| PDOException | Database errors when using PDO |
function setAge(int $age): void {
if ($age < 0) {
throw new InvalidArgumentException('Age cannot be negative');
}
}Interview tip: Using SPL exceptions instead of a generic Exception makes catch blocks more precise and self-documenting - reach for a custom exception only when SPL doesn't fit.
Q9. How should exceptions be handled differently in development vs production?
| Aspect | Development | Production |
|---|---|---|
| Error display | Show full stack trace to developer | Never show stack traces or internal messages to users |
| Logging | Optional, console is often enough | Mandatory - log to file, or a service like Sentry/CloudWatch |
| User-facing message | Technical detail is fine | Generic, friendly message plus a reference/error ID |
| HTTP status code | Less critical | Must return the correct status (500, 422, 404, etc.) |
try {
chargeCard($order);
} catch (Throwable $e) {
Log::error($e->getMessage(), ['trace' => $e->getTraceAsString()]);
if (app()->environment('production')) {
return response()->json(['message' => 'Payment failed. Please try again.'], 500);
}
throw $e; // rethrow with full detail locally
}Interview tip: This question tests security awareness as much as PHP knowledge - always mention that leaking stack traces in production can expose file paths, database structure, and even credentials.
Q10. What is exception chaining in PHP and why is it useful?
Exception chaining lets you wrap a low-level exception inside a higher-level, more meaningful one while preserving the original cause. PHP's Exception constructor accepts a previous exception as its third argument.
class OrderProcessingException extends Exception {}
try {
try {
$pdo->query('INSERT INTO orders ...');
} catch (PDOException $e) {
throw new OrderProcessingException(
'Failed to save order',
0,
$e // the original exception, preserved as the "previous" one
);
}
} catch (OrderProcessingException $e) {
echo $e->getMessage(); // 'Failed to save order'
echo $e->getPrevious()->getMessage(); // original PDOException message
}| Benefit | Why it matters |
|---|---|
| Preserves root cause | getPrevious() gives you the original low-level error for debugging |
| Clean abstraction | Calling code only needs to catch OrderProcessingException, not know about PDOException internals |
| Full trace in logs | Log::error() with an exception object includes the whole chain automatically in most frameworks |
Interview tip: This shows senior-level thinking - it demonstrates you understand layered architecture (e.g. repository layer errors shouldn't leak database-specific exceptions to the controller layer).
Error & Exception Handling
Understand PHP error levels, try-catch blocks, custom exception handling, and debugging strategies for robust backend engineering.
What is the difference between an error and an exception in PHP?
Since PHP 7, both Errors and Exceptions implement the Throwable interface, but they represent different kinds...
How does try-catch-finally work in PHP?
The try block contains code that might fail. The catch block handles the exception if one is thrown. The final...
How do you create and use custom exception classes in PHP?
Create a class that extends Exception (or a more specific built-in subclass) to represent domain-specific erro...
What is the difference between catching Exception and catching Throwable?
Throwable is the top-level interface implemented by both Error and Exception. Catching Exception only catches...
Does the finally block always execute? What are the exceptions to this rule?
The finally block runs in almost all cases - after normal completion, after a caught exception, after an uncau...
How do you handle multiple exception types in a single catch block?
PHP allows catching multiple exception types in one catch block using the pipe | separator, avoiding repeated...
What is set_error_handler() and set_exception_handler() in PHP?
These functions let you override PHP's default handling of errors and uncaught exceptions with your own logic...
What are common built-in exception classes in PHP (SPL exceptions)?
PHP's Standard Library (SPL) provides a hierarchy of exception classes for common scenarios, so you don't need...
How should exceptions be handled differently in development vs production?
AspectDevelopmentProductionError displayShow full stack trace to developerNever show stack traces or internal...
What is exception chaining in PHP and why is it useful?
Exception chaining lets you wrap a low-level exception inside a higher-level, more meaningful one while preser...