You can define your own exception classes by extending Exception, giving each error condition a specific, self-documenting type:
class InsufficientFundsException extends Exception
{
public function __construct(
public readonly float $requested,
public readonly float $available,
) {
parent::__construct(
"Requested $requested but only $available available."
);
}
}
function withdraw(float $amount, float $balance): float
{
if ($amount > $balance) {
throw new InsufficientFundsException($amount, $balance);
}
return $balance - $amount;
}
try {
withdraw(500, 200);
} catch (InsufficientFundsException $e) {
echo $e->getMessage();
echo "Short by: " . ($e->requested - $e->available);
}
Error reporting settings control what gets shown or logged. In development, show everything:
ini_set('display_errors', 1);
error_reporting(E_ALL);
In production, never display raw errors to users (they can leak sensitive info) — log them instead:
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/path/to/error.log');
A custom top-level error handler (set_error_handler()) and exception handler (set_exception_handler()) let you centralize logging, alerting, or converting PHP errors into exceptions for consistent handling across an app — this is exactly what frameworks like Laravel and Symfony do under the hood.