Installation:
composer require phrity/util-errorhandler
Basic Usage:
use Phrity\Util\ErrorHandler;
$handler = new ErrorHandler();
$result = $handler->with(function () {
// Your code here
return "Success";
});
First Use Case: Wrap a risky operation (e.g., third-party API call) to catch errors and convert them to exceptions:
try {
$result = $handler->with(function () {
$response = Http::get('https://api.example.com/unsafe-endpoint');
return $response->json();
});
} catch (ErrorException $e) {
// Handle the error (e.g., log, retry, or return a fallback)
Log::error("API call failed: " . $e->getMessage());
return response()->json(['error' => 'Service unavailable'], 503);
}
API Endpoints:
Use with() to convert PHP errors into HTTP responses:
public function riskyOperation(Request $request)
{
$handler = new ErrorHandler();
$result = $handler->with(function () use ($request) {
// Risky logic (e.g., parsing user input)
return $this->processInput($request->input());
}, function (ErrorException $error) {
// Convert to HTTP response
return response()->json([
'error' => $error->getMessage(),
'code' => $error->getCode()
], 422);
});
return response()->json($result);
}
Background Jobs:
Use withAll() to collect all errors in a batch job:
public function handle()
{
$handler = new ErrorHandler();
$results = $handler->withAll(function () {
// Process multiple items
return array_map(function ($item) {
return $this->processItem($item);
}, $this->items);
}, function (array $errors, $successResult) {
// Log all errors and return partial results
Log::error('Batch job errors:', $errors);
return $successResult; // Return what succeeded
});
$this->dispatchAfterCompletion(new LogResultsJob($results));
}
Global Error Handling: Temporarily override Laravel’s error handler for CLI commands:
$handler = new ErrorHandler();
$handler->set(function (ErrorException $error) {
// Custom CLI error handling
fwrite(STDERR, "Error: " . $error->getMessage() . "\n");
exit(1);
});
// CLI logic here
$handler->restore(); // Restore Laravel's default handler
Filtering Errors:
Ignore E_NOTICE but throw on E_USER_ERROR:
$result = $handler->with(function () {
// Code that might trigger notices or errors
}, null, E_USER_ERROR);
Error-to-Exception Conversion:
Replace @ suppression with structured error handling:
// Before (risky)
$data = @json_decode($response->body());
// After (structured)
$data = $handler->with(function () use ($response) {
return json_decode($response->body());
});
Custom Exception Wrapping: Convert errors into domain-specific exceptions:
$result = $handler->with(function () {
// Risky logic
}, new PaymentGatewayException('Failed to process payment'));
Fallback Logic: Use callbacks to implement retries or defaults:
$result = $handler->with(function () {
return $this->fetchFromPrimarySource();
}, function (ErrorException $error) {
return $this->fetchFromBackupSource();
});
Laravel Service Container: Bind the handler for dependency injection:
$this->app->singleton(ErrorHandler::class, function ($app) {
return new ErrorHandler();
});
Then inject it into controllers/jobs:
public function __construct(private ErrorHandler $handler) {}
Middleware: Wrap API routes to handle errors globally:
public function handle($request, Closure $next)
{
$handler = new ErrorHandler();
return $handler->with(function () use ($request, $next) {
return $next($request);
}, function (ErrorException $error) {
return response()->json([
'error' => 'Server error',
'message' => env('APP_DEBUG') ? $error->getMessage() : null
], 500);
});
}
Testing: Simulate errors in tests:
public function test_error_handling()
{
$handler = new ErrorHandler();
$this->expectException(ErrorException::class);
$handler->with(function () {
trigger_error("Test error", E_USER_ERROR);
});
}
Global Handler Conflicts:
set() can interfere with Laravel’s debug bar, logging, or exception handling.restore() after use, or limit set() to CLI/command contexts.Error Level Mismatches:
App\Exceptions\Handler may not process ErrorExceptions thrown by this package if they’re not rethrown.// Avoid swallowing exceptions unless intentional
try {
$handler->with(...);
} catch (ErrorException $e) {
// Only catch if you need custom logic
throw $e; // Re-throw to let Laravel handle it
}
Performance in Loops:
with() adds overhead.withAll() for batch operations or limit usage to critical sections.Callback Return Values:
with() method returns the result of the callback or the error callback. If both return values, the error callback’s result takes precedence.null or a default value in error cases).Error Context Loss:
ErrorExceptions may lose context (e.g., file/line) if not thrown immediately.with() over withAll() when you need precise error locations.Silent Failures:
E_ALL vs. E_USER_ERROR).restore()).error_reporting isn’t suppressing errors.Unexpected Exceptions:
RuntimeException is thrown instead of an ErrorException, check the second parameter of with()/withAll():
// This will throw a RuntimeException, not ErrorException
$handler->with(fn() => trigger_error("Test"), new RuntimeException());
Logging:
$handler->with(function () {
Log::debug("Executing risky block");
// ...
}, function (ErrorException $error) {
Log::error("Error caught", ['error' => $error]);
});
Combine with Laravel Features:
report() in error callbacks to leverage Laravel’s exception reporting:
$handler->with(function () { /* ... */ }, function (ErrorException $error) {
report($error); // Logs to Laravel's channels
return response()->json(['error' => 'Internal server error'], 500);
});
Custom Error Levels:
const CRITICAL_ERRORS = E_USER_ERROR | E_USER_WARNING;
$handler->with(fn() => ..., null, CRITICAL_ERRORS);
Immutable Handler:
$handler = app(ErrorHandler::class);
Testing Edge Cases:
public function test_error_levels()
{
$handler = new ErrorHandler();
$this->expectException(ErrorException::class);
$handler->with(function () {
trigger_error("Warning", E_USER_WARNING);
}, null, E_USER_WARNING);
}
Extension Points:
class LaravelErrorHandlerDecorator
{
public function __construct(private Error
How can I help you explore Laravel packages today?