-
-
Notifications
You must be signed in to change notification settings - Fork 0
[Audit] Error Handling and Recovery #64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
52c8a9f
6cf9ed9
fa467a6
8eab61b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # Error Handling and Recovery Audit | ||
|
|
||
| This audit examines the error handling and recovery mechanisms in the application. | ||
|
|
||
| ## Findings | ||
|
|
||
| ### 1. Exception Hierarchy Consistency | ||
|
|
||
| - **Observation:** The application lacks a consistent exception hierarchy, frequently relying on the generic `\Exception` class. | ||
| - **Impact:** This makes it difficult to differentiate between various error types, leading to less precise error handling and potentially noisy logs. | ||
| - **Recommendation:** Implement a custom exception hierarchy with a base `ApiException` class and more specific exception types. | ||
|
|
||
| ### 2. Error Boundary Placement | ||
|
|
||
| - **Observation:** Error boundaries are not consistently placed, with some code catching exceptions at a low level and others allowing them to bubble up. | ||
| - **Impact:** This can lead to inconsistent error responses and makes it harder to reason about the application's error-handling behavior. | ||
| - **Recommendation:** Establish clear guidelines for where to catch and handle exceptions, and implement a global exception handler to ensure all unhandled exceptions are caught and processed consistently. | ||
|
|
||
| ### 3. User-Facing Error Messages | ||
|
|
||
| - **Observation:** User-facing error messages are a mix of hardcoded strings and translations from a language file, which can lead to an inconsistent tone and style. | ||
| - **Impact:** This can create a jarring user experience and may not always provide helpful information. | ||
| - **Recommendation:** Standardize the creation of user-facing error messages, and ensure they are always clear, concise, and helpful. | ||
|
|
||
| ### 4. Logging Completeness | ||
|
|
||
| - **Observation:** Logging is not always complete, with some `try-catch` blocks failing to log the exceptions they catch. | ||
| - **Impact:** This can make it difficult to debug issues, as important error information may be lost. | ||
| - **Recommendation:** Implement a consistent logging strategy, ensuring that all caught exceptions are logged with sufficient context. | ||
|
|
||
| ### 5. Recovery Strategies | ||
|
|
||
| - **Observation:** The application has limited recovery strategies, with most `try-catch` blocks simply logging the error and returning a generic response. | ||
| - **Impact:** This can lead to a degraded user experience, as the application may not be able to recover from transient errors. | ||
| - **Recommendation:** Implement more robust recovery strategies, such as retrying failed operations or providing fallback functionality. | ||
|
|
||
| ## Changes Made | ||
|
|
||
| To address the issues identified in this audit, the following changes have been made: | ||
|
|
||
| 1. **Introduced a Custom Exception Hierarchy:** | ||
| - Created a new `src/Core/Exception` directory. | ||
| - Defined a base `ApiException` class that extends `\Exception`. | ||
| - Created more specific exception classes, such as `AuthorizationException` and `ValidationException`, that extend `ApiException`. | ||
|
|
||
| 2. **Implemented a Global Exception Handler:** | ||
| - Created a new `src/Core/Exception/Handler.php` file to serve as the application's global exception handler. | ||
| - This handler is registered in `src/Core/Boot.php` and is responsible for catching `ApiException` types and generating appropriate JSON responses. | ||
|
|
||
| 3. **Refactored Existing Code:** | ||
| - The `ManagesTokens` trait was refactored to throw the new `AuthorizationException` instead of the generic `\Exception`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| { | ||
| "name": "host-uk/core", | ||
| "description": "Modular monolith framework for Laravel - event-driven architecture with lazy module loading", | ||
| "keywords": ["laravel", "modular", "monolith", "framework", "events", "modules"], | ||
| "license": "EUPL-1.2", | ||
| "authors": [ | ||
| { | ||
| "name": "Host UK", | ||
| "email": "support@host.uk.com" | ||
| } | ||
| ], | ||
| "require": { | ||
| "php": "^8.2", | ||
| "laravel/framework": "^11.0|^12.0", | ||
| "laravel/pennant": "^1.0", | ||
| "livewire/livewire": "^3.0|^4.0" | ||
| }, | ||
| "require-dev": { | ||
| "fakerphp/faker": "^1.23", | ||
| "infection/infection": "^0.32.3", | ||
| "larastan/larastan": "^3.9", | ||
| "laravel/pint": "^1.18", | ||
| "mockery/mockery": "^1.6", | ||
| "nunomaduro/collision": "^8.6", | ||
| "orchestra/testbench": "^9.0|^10.0", | ||
| "phpstan/extension-installer": "^1.4", | ||
| "phpstan/phpstan": "^2.1", | ||
| "phpstan/phpstan-deprecation-rules": "^2.0", | ||
| "phpunit/phpunit": "^11.5", | ||
| "psalm/plugin-laravel": "^3.0", | ||
| "rector/rector": "^2.3", | ||
| "roave/security-advisories": "dev-latest", | ||
| "spatie/laravel-activitylog": "^4.8", | ||
| "vimeo/psalm": "^6.14" | ||
| }, | ||
| "suggest": { | ||
| "spatie/laravel-activitylog": "Required for activity logging features (^4.0)" | ||
| }, | ||
| "autoload": { | ||
| "psr-4": { | ||
| "Core\\": "src/Core/", | ||
| "Core\\Website\\": "src/Website/", | ||
| "Core\\Mod\\": "src/Mod/", | ||
| "Core\\Plug\\": "src/Plug/" | ||
| }, | ||
| "files": [ | ||
| "src/Core/Media/Thumbnail/helpers.php" | ||
| ] | ||
| }, | ||
| "autoload-dev": { | ||
| "psr-4": { | ||
| "Core\\Tests\\": "tests/", | ||
| "Core\\TestCore\\": "tests/Fixtures/Core/TestCore/", | ||
| "App\\Custom\\": "tests/Fixtures/Custom/", | ||
| "Mod\\": "tests/Fixtures/Mod/", | ||
| "Plug\\": "tests/Fixtures/Plug/", | ||
| "Website\\": "tests/Fixtures/Website/" | ||
| } | ||
| }, | ||
| "scripts": { | ||
| "test": "vendor/bin/phpunit", | ||
| "pint": "vendor/bin/pint", | ||
| "stan": "vendor/bin/phpstan analyse --memory-limit=512M" | ||
| }, | ||
| "extra": { | ||
| "laravel": { | ||
| "providers": [ | ||
| "Core\\LifecycleEventProvider", | ||
| "Core\\Lang\\LangServiceProvider", | ||
| "Core\\Bouncer\\Gate\\Boot" | ||
| ] | ||
| } | ||
| }, | ||
| "config": { | ||
| "optimize-autoloader": true, | ||
| "preferred-install": "dist", | ||
| "sort-packages": true, | ||
| "allow-plugins": { | ||
| "php-http/discovery": true, | ||
| "phpstan/extension-installer": true, | ||
| "infection/extension-installer": true | ||
| } | ||
| }, | ||
| "minimum-stability": "stable", | ||
| "prefer-stable": true | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Core\Exception; | ||
|
|
||
| class ApiException extends \Exception | ||
| { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Core\Exception; | ||
|
|
||
| class AuthorizationException extends ApiException | ||
| { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Core\Exception; | ||
|
|
||
| use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; | ||
| use Illuminate\Http\JsonResponse; | ||
| use Illuminate\Http\Request; | ||
| use Throwable; | ||
|
|
||
| class Handler extends ExceptionHandler | ||
| { | ||
| /** | ||
| * A list of the exception types that are not reported. | ||
| * | ||
| * @var array<int, class-string<Throwable>> | ||
| */ | ||
| protected $dontReport = [ | ||
| ApiException::class, | ||
| ]; | ||
|
|
||
| /** | ||
| * A list of the inputs that are never flashed for validation exceptions. | ||
| * | ||
| * @var array<int, string> | ||
| */ | ||
| protected $dontFlash = [ | ||
| 'current_password', | ||
| 'password', | ||
| 'password_confirmation', | ||
| ]; | ||
|
|
||
| /** | ||
| * Register the exception handling callbacks for the application. | ||
| */ | ||
| public function register(): void | ||
| { | ||
| $this->renderable(function (ApiException $e, Request $request) { | ||
| if ($request->wantsJson()) { | ||
| $statusCode = match (true) { | ||
| $e instanceof AuthorizationException => 403, | ||
| $e instanceof ValidationException => 422, | ||
| default => 400, | ||
| }; | ||
|
|
||
| return new JsonResponse( | ||
| ['message' => $e->getMessage()], | ||
| $statusCode | ||
| ); | ||
| } | ||
|
Comment on lines
+39
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n src/Core/Exception/Handler.phpRepository: host-uk/core-php Length of output: 1889 🌐 Web query:
💡 Result: In Laravel 11, an exception “render” / Source: Laravel 11 “Error Handling → Rendering Exceptions” explicitly says: “If the closure given to the References [1] Laravel 11.x Docs – Error Handling (Rendering Exceptions) https://laravel.com/docs/11.x/errors 🏁 Script executed: fd -t f "ValidationException" src/ --exec cat -n {} \;Repository: host-uk/core-php Length of output: 233 🏁 Script executed: rg -A 10 "class ValidationException" src/Repository: host-uk/core-php Length of output: 239 🏁 Script executed: rg -B 5 -A 15 "ValidationException" src/Core/Exception/Repository: host-uk/core-php Length of output: 1769 🏁 Script executed: rg -B 3 -A 3 "throw.*ValidationException" src/ --max-count=5Repository: host-uk/core-php Length of output: 42 🏁 Script executed: rg -B 3 -A 3 "throw.*AuthorizationException" src/ --max-count=5Repository: host-uk/core-php Length of output: 799 🏁 Script executed: rg "new ValidationException" src/ --max-count=5Repository: host-uk/core-php Length of output: 42 🏁 Script executed: rg -A 20 "class ApiException" src/Core/Exception/Repository: host-uk/core-php Length of output: 209 🏁 Script executed: rg "ApiException\|AuthorizationException" src/ -l | head -10Repository: host-uk/core-php Length of output: 42 🏁 Script executed: rg "class.*Exception.*extends" src/Core/Exception/ -A 2Repository: host-uk/core-php Length of output: 587 Consider enhancing The current response returns When Additionally, 🤖 Prompt for AI Agents |
||
| }); | ||
|
|
||
| $this->reportable(function (Throwable $e) { | ||
| // | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Core\Exception; | ||
|
|
||
| class ValidationException extends ApiException | ||
| { | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: host-uk/core-php
Length of output: 237
🏁 Script executed:
Repository: host-uk/core-php
Length of output: 1345
🏁 Script executed:
Repository: host-uk/core-php
Length of output: 471
🏁 Script executed:
Repository: host-uk/core-php
Length of output: 459
🏁 Script executed:
Repository: host-uk/core-php
Length of output: 483
Remove the redundant
original_composer.jsonfile.This file is identical to
composer.jsonand serves no documented purpose. Having two identical composer manifests is unnecessary duplication and can cause confusion during maintenance. Deleteoriginal_composer.jsonand keep onlycomposer.json.🤖 Prompt for AI Agents