In PHP, what separates SPL's LogicException family from its RuntimeException family, and which would you throw for a bad argument versus a missing record?
answer
- both extend Exception
- logic: fix the calling code
- runtime: only detectable while running
- InvalidArgumentException is a LogicException
- PDOException extends RuntimeException
basics
~20 sLogicException subclasses signal a programming mistake that should lead to a code fix, such as InvalidArgumentException for a bad argument. RuntimeException subclasses signal conditions only detectable while running, such as OutOfBoundsException or UnexpectedValueException for a missing record or bad data.
solid answer
~40 sBoth families extend `Exception`. The manual defines `LogicException` as an error in program logic that "should lead directly to a fix in your code"; its subclasses are `InvalidArgumentException`, `DomainException`, `LengthException`, `OutOfRangeException` and `BadFunctionCallException` (with `BadMethodCallException`). `RuntimeException` is for errors "which can only be found on runtime": `OutOfBoundsException`, `OverflowException`, `UnderflowException`, `RangeException` and `UnexpectedValueException`; `PDOException` and `mysqli_sql_exception` extend it too. So a caller passing a negative page size to your method gets `InvalidArgumentException` — their code is wrong. A lookup for an ID that does not exist is a runtime condition: `OutOfBoundsException` or, better, a domain exception extending `RuntimeException`. The distinction tells callers whether catching and handling makes sense or whether the stack trace is a bug report.
code
php · 22 lines<?php
declare(strict_types=1);
final class OrderNotFound extends RuntimeException {}
final class OrderRepository
{
public function __construct(private array $rows) {}
public function page(int $size): array
{
if ($size < 1) {
throw new InvalidArgumentException("Page size must be >= 1, got $size"); // caller bug
}
return array_slice($this->rows, 0, $size);
}
public function get(int $id): array
{
return $this->rows[$id] ?? throw new OrderNotFound("Order $id not found"); // runtime fact
}
}go deeper
Know that InvalidArgumentException and RuntimeException are standard SPL exceptions, both extending Exception.
Sort the SPL subclasses into logic and runtime families and choose correctly between a bad argument and a missing record.
Design catch sites that handle runtime failures while letting logic exceptions surface, and base domain exceptions on the right SPL parent.
Define an organisation-wide exception taxonomy on top of SPL so libraries signal caller bugs and operational failures consistently.
## Two families under Exception The SPL extension ships a small set of exception classes so that code does not have to invent a class for every common failure. They split into two families, both extending `Exception` (not `Error`): | Family | Manual definition | Subclasses | |---|---|---| | `LogicException` | an error in the program logic; should lead directly to a fix in your code | `InvalidArgumentException`, `DomainException`, `LengthException`, `OutOfRangeException`, `BadFunctionCallException` > `BadMethodCallException` | | `RuntimeException` | an error that can only be found at run time | `OutOfBoundsException`, `OverflowException`, `UnderflowException`, `RangeException`, `UnexpectedValueException` | Several extensions build on the runtime side: `PDOException` and `mysqli_sql_exception` extend `RuntimeException`, because a lost connection or a failed query is a runtime condition. ## What each subclass means **Logic side** — the caller made a mistake the code could have prevented: - `InvalidArgumentException` — an argument is not of the expected type or form; - `DomainException` — a value is outside a defined valid domain; - `LengthException` — a length is invalid; - `OutOfRangeException` — an illegal index was requested, an error that should be detectable before running; - `BadFunctionCallException` / `BadMethodCallException` — a callback refers to an undefined function or method, or arguments are missing. **Runtime side** — the program is correct but the world is not as expected: - `OutOfBoundsException` — a value is not a valid key, which cannot be known in advance; - `OverflowException` — adding to a full container; - `UnderflowException` — removing from an empty container; - `RangeException` — a range error during execution, the runtime counterpart of `DomainException`; - `UnexpectedValueException` — a value, often a return value from another call, is not one of the expected ones. ## Choosing in practice 1. **Bad argument from calling code** → `InvalidArgumentException`. A `Paginator` given a page size of `-5` has been misused; the fix is in the caller. 2. **Record not found by ID** → a runtime-side exception. The ID may have been valid a moment ago; nobody's code is wrong. `OutOfBoundsException` fits literally, but most applications define `OrderNotFound extends RuntimeException` so the name carries the meaning. 3. **Third-party data malformed** → `UnexpectedValueException`, for example an API returning a status your code does not know. 4. **Invalid user input** → validation errors that the UI shows, usually a dedicated exception or a result object, not `InvalidArgumentException`, because the user did nothing wrong in your code's terms. ## Why the distinction pays off - **Handling strategy.** Runtime exceptions are candidates for catch-and-recover: show a 404, retry, fall back. Logic exceptions should surface loudly in logs and tests, because retrying the same call reproduces the same bug. - **Readable catch sites.** `catch (RuntimeException $e)` around a repository call says "I expect operational failures here", without swallowing programming errors. - **Library contracts.** Documenting that a method throws `InvalidArgumentException` for bad input and a `RuntimeException` subclass for I/O failures tells callers what to handle. ## Common misuses - **Throwing the bare parent.** `throw new RuntimeException('failed')` everywhere gives callers nothing to catch selectively; extend it with a named class. - **Using `InvalidArgumentException` for everything.** Missing records, network failures and bad upstream data are not argument errors. - **Catching `LogicException` to recover.** A logic exception means the calling code is wrong; catching it to carry on hides a bug that will recur. - **Confusing the pairs.** `DomainException` versus `RangeException`, and `OutOfRangeException` versus `OutOfBoundsException`, differ exactly by the logic-versus-runtime axis. ## Relation to the Error branch The engine's `TypeError` and `ValueError` are close cousins of `InvalidArgumentException`, but they live on the `Error` branch and are thrown by PHP itself. Libraries validating their own arguments conventionally throw `InvalidArgumentException`, so callers' existing `catch (Exception)` handlers still see the failure.
- What is the difference between OutOfRangeException and OutOfBoundsException in PHP's SPL?`OutOfRangeException` is a `LogicException`: an illegal index that should have been caught before running, such as a hard-coded position beyond a fixed-size structure. `OutOfBoundsException` is a `RuntimeException`: a key that is invalid only given run-time data, such as an ID absent from the loaded set.
- Which SPL parent does PDOException have, and why does it matter for catch blocks?`PDOException` extends `RuntimeException`, so `catch (RuntimeException $e)` around data access catches database failures along with other runtime conditions. It does not catch `InvalidArgumentException` or any `Error`, so programming mistakes still surface.
saying these in an interview costs you the question
- InvalidArgumentException extends RuntimeException.
- LogicException and RuntimeException are on the Error branch.
- A missing database row is best reported as InvalidArgumentException.
- PDOException extends LogicException because queries are code.
- The SPL families differ only in name and carry no meaning.