axy/errors
axy/errors is a PHP 8.1+ helper for defining and organizing exception classes. Provides common exception structures, basic error classes, default messages, backtrace truncation, and global options to standardize error handling across projects.
Installation:
composer require axy/errors
Requires PHP 8.1+.
Basic Usage: Throw a predefined exception in your code:
use axy\errors\InvalidFormat;
throw new InvalidFormat('127.0.0.256', 'IPv4');
Catching Exceptions: Catch exceptions by their type or interface:
try {
// Your code
} catch (\axy\errors\Logic $e) {
// Handle logic errors
} catch (\axy\errors\Runtime $e) {
// Handle runtime errors
}
Custom Exceptions: Extend the base classes to create domain-specific exceptions:
namespace App\Errors;
use axy\errors\Logic;
class InvalidUserInput extends Logic {}
Replace generic InvalidArgumentException with domain-specific exceptions for better error handling:
throw new \axy\errors\TypingError('user_id', 'int|string');
Domain-Specific Exception Hierarchy:
Organize exceptions under a dedicated namespace (e.g., App\Errors) and extend axy\errors\Logic or axy\errors\Runtime:
namespace App\Errors;
use axy\errors\Logic;
class InvalidPaymentMethod extends Logic {}
Consistent Error Messages:
Use the $value and $type parameters in InvalidFormat to provide context:
throw new InvalidFormat($email, 'email');
State-Based Validation:
Use ActionNotAllowed for state-dependent operations:
if (!$user->isSaved()) {
throw new ActionNotAllowed('delete', $user, 'User not saved');
}
Truncated Backtraces: Leverage automatic backtrace truncation to hide library internals:
try {
$service->execute();
} catch (\axy\errors\Error $e) {
// Backtrace points to caller, not library internals
}
Validation Workflow:
use axy\errors\{InvalidFormat, TypingError};
$ip = '192.168.1.256';
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
throw new InvalidFormat($ip, 'IPv4');
}
$ids = ['user1', 'user2'];
if (!is_array($ids)) {
throw new TypingError('ids', 'array');
}
Container Operations:
use axy\errors\{ContainerReadOnly, FieldNotExists};
$config = new ReadOnlyConfig();
$config->timeout = 30; // Throws ContainerReadOnly
$config->invalidKey = 'value'; // Throws FieldNotExists
Initialization Checks:
use axy\errors\{AlreadyInited, NotInited};
if ($service->isInitialized()) {
throw new AlreadyInited($service);
}
$service->initialize();
Global Exception Handler:
Centralize error handling in app/Exceptions/Handler.php:
public function render($request, Throwable $exception) {
if ($exception instanceof \axy\errors\Error) {
return response()->json([
'error' => $exception->getMessage(),
'code' => $exception->getCode(),
], $exception->getCode());
}
return parent::render($request, $exception);
}
Logging: Log exceptions with context using Monolog:
use axy\errors\Error;
try {
// Risky operation
} catch (Error $e) {
\Log::error($e->getMessage(), [
'exception' => $e,
'backtrace' => $e->getTruncatedTrace(),
]);
}
API Responses: Standardize API error responses:
return response()->json([
'success' => false,
'errors' => [
'code' => $e->getCode(),
'message' => $e->getMessage(),
'details' => method_exists($e, 'getVarName') ? $e->getVarName() : null,
],
], $e->getCode());
Backtrace Truncation:
getTrace() is discouraged. Use getTruncatedTrace() for custom logic.$howTruncateTrace = false in your exception class.Constructor Parameters:
$previous and $thrower as the last arguments to maintain compatibility:
throw new InvalidFormat($value, $type, null, new stdClass());
Namespace Sensitivity:
App\Errors under App).PHP 7 Compatibility:
truncateNativeTrace is unsupported in PHP 7+. Avoid relying on it.Overriding Methods:
getFile(), getLine(), or getTrace() unless absolutely necessary. Use getOriginFile()/getOriginLine() for original values.Inspect Truncated Trace:
$e = new \axy\errors\InvalidFormat('test');
dump($e->getTruncatedTrace()->getFrames());
Check Backtrace Settings:
var_dump(property_exists($e, 'howTruncateTrace') ? $e->howTruncateTrace : 'default');
Verify Exception Hierarchy:
Use instanceof to debug inheritance:
var_dump($e instanceof \axy\errors\Logic); // true
var_dump($e instanceof \axy\errors\Error); // true
Custom Message Templates: Use array messages for dynamic content:
throw new \axy\errors\NotValid('email', [
'The :varName is invalid: :message',
'varName' => 'email',
'message' => 'must be a valid email',
]);
Extend Base Classes: Create a base exception for your application:
namespace App\Errors;
use axy\errors\Logic;
abstract class AppError extends Logic {}
Leverage Interfaces: Catch exceptions by interface for broad handling:
try {
// Code
} catch (\axy\errors\NotFound $e) {
// Handle missing items, fields, etc.
}
Disable Truncation for Tests:
class TestError extends \axy\errors\Runtime {
protected $howTruncateTrace = false;
}
Use RequiresOverride for Optional Methods:
Mark non-critical abstract methods in base classes:
public function optionalFeature() {
throw new \axy\errors\RequiresOverride();
}
Global Options:
Configure default behavior via axy\errors\Opts:
\axy\errors\Opts::setDefaultMessageTemplate('[:message]');
How can I help you explore Laravel packages today?