Skip to content

Conversation

@VadymHrechukha
Copy link
Contributor

@VadymHrechukha VadymHrechukha commented Dec 5, 2025

Summary by CodeRabbit

  • New Features

    • Added formatted exception context output enabling human-readable debug information display.
    • Support for displaying chained exceptions and contextual details alongside primary exception information.
  • Tests

    • Added comprehensive tests for exception context formatting functionality.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 5, 2025

Walkthrough

The PR introduces exception formatting capabilities through a new ExceptionDebugFormatter class in the Infrastructure layer and extends the HasContext trait with a getFormattedContext() method for human-readable output. The formatter recursively processes exception chains with optional context inclusion. Interface contract updated to reflect new public method, with unit tests added for new functionality.

Changes

Cohort / File(s) Summary
Exception Formatting Infrastructure
src/Infrastructure/Exception/ExceptionDebugFormatter.php
New final class with format(\Throwable $e, bool $skipContext = false): array method that assembles debug arrays containing exception class, message, throw location, and recursively chains parent exceptions. Conditionally includes context when exception implements HasContextInterface.
Context Trait & Interface Updates
src/exception/HasContext.php, src/exception/HasContextInterface.php
Added getFormattedContext(): string public method to trait and interface. Trait includes private helpers: getExceptionDebugInfo(?Throwable $throwable): array for exception details and jsonEncode($value): string for complex values. Imports ExceptionDebugFormatter for formatting throwables.
Unit Tests
tests/unit/exception/HasContextTest.php
Added four test cases: empty context handling, simple context formatting, array-to-JSON serialization, and previous exception inclusion in formatted output.

Sequence Diagram

sequenceDiagram
    actor Client
    participant ExceptionDebugFormatter
    participant HasContext
    participant HasContextInterface

    Client->>ExceptionDebugFormatter: format(exception, skipContext)
    ExceptionDebugFormatter->>ExceptionDebugFormatter: Extract class, message, location
    
    alt Exception implements HasContextInterface
        alt skipContext is false
            ExceptionDebugFormatter->>HasContextInterface: getContext()
            HasContextInterface-->>ExceptionDebugFormatter: context array
            ExceptionDebugFormatter->>HasContext: getExceptionDebugInfo() [helper]
            HasContext-->>ExceptionDebugFormatter: formatted exception data
        end
    end
    
    alt Has previous exception
        ExceptionDebugFormatter->>ExceptionDebugFormatter: format(previousException, skipContext)<br/>(recursive)
        ExceptionDebugFormatter->>ExceptionDebugFormatter: Nest as parentException
    end
    
    ExceptionDebugFormatter-->>Client: array with debug info & chain
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • ExceptionDebugFormatter: New recursive formatting logic with conditional context inclusion and exception chaining requires verification of logic correctness
  • HasContext trait helpers: JSON encoding and exception debug info extraction introduce new patterns; interaction with ExceptionDebugFormatter needs tracing
  • Test coverage: Four test cases cover main scenarios but verify that recursion handling and context serialization edge cases are properly addressed

Possibly related PRs

Suggested reviewers

  • SilverFire

Poem

🐰 Exceptions now format so fine,
Context displayed in lines divine,
Debug arrays chain parent to child,
No more throwables running wild!
JSON-encoded, recursive and bright,

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title refers to modifying the HasContext Exception class to return a formatted message with context, which aligns with the main changes (adding getFormattedContext() method and supporting formatting logic). However, the phrasing is awkward ('returns formatter message') and doesn't capture the full scope that also includes the new ExceptionDebugFormatter class.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
tests/unit/exception/HasContextTest.php (1)

99-100: Consider verifying JSON structure instead of string matching.

The current assertions check for specific JSON formatting (e.g., "a": 1 with specific spacing), which tests implementation details rather than behavior. This could break if JSON formatting options change.

Consider parsing the JSON and verifying the structure:

-// Pretty-printed JSON
-$this->assertStringContainsString('"a": 1', $output);
-$this->assertStringContainsString('"b": 2', $output);
+// Verify JSON structure by parsing
+$this->assertStringContainsString('data', $output);
+// Extract and parse the JSON portion if needed for stricter validation
src/Infrastructure/Exception/ExceptionDebugFormatter.php (1)

25-27: Clarify variable naming for the previous exception.

The variable $root is misleading as it typically refers to the root/oldest exception in a chain, but getPrevious() returns the immediate previous/parent exception.

Apply this diff for clarity:

-if ($root = $e->getPrevious()) {
-    $debugInfo['parentException'] = $this->format($root, $skipContext);
+if ($previous = $e->getPrevious()) {
+    $debugInfo['parentException'] = $this->format($previous, $skipContext);
 }
src/exception/HasContext.php (2)

70-73: Add defensive error handling for JSON encoding.

Using JSON_THROW_ON_ERROR during exception formatting (error handling) can throw a JsonException and mask the original problem. This can happen with recursive data structures, resources, or non-serializable objects.

Apply this diff to handle encoding errors gracefully:

 private function jsonEncode($value): string
 {
-    return \json_encode($value, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);
+    try {
+        return \json_encode($value, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);
+    } catch (\JsonException $e) {
+        // Fallback for non-serializable values
+        return \print_r($value, true);
+    }
 }

65-68: Consider reusing the formatter instance.

Creating a new ExceptionDebugFormatter instance on every call is unnecessary. While the performance impact is minimal, a static instance would be more efficient.

Consider this approach:

 private function getExceptionDebugInfo(?Throwable $throwable): array
 {
-    return (new ExceptionDebugFormatter())->format($throwable);
+    static $formatter = null;
+    if ($formatter === null) {
+        $formatter = new ExceptionDebugFormatter();
+    }
+    return $formatter->format($throwable);
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8ed3d0 and 6e3a5e8.

📒 Files selected for processing (4)
  • src/Infrastructure/Exception/ExceptionDebugFormatter.php (1 hunks)
  • src/exception/HasContext.php (2 hunks)
  • src/exception/HasContextInterface.php (1 hunks)
  • tests/unit/exception/HasContextTest.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/exception/HasContextInterface.php (1)
src/exception/HasContext.php (1)
  • getFormattedContext (42-63)
src/Infrastructure/Exception/ExceptionDebugFormatter.php (2)
src/exception/HasContext.php (2)
  • getContext (24-27)
  • getPrevious (15-15)
src/exception/HasContextInterface.php (1)
  • getContext (9-9)
src/exception/HasContext.php (2)
src/Infrastructure/Exception/ExceptionDebugFormatter.php (2)
  • ExceptionDebugFormatter (9-30)
  • format (11-29)
src/exception/HasContextInterface.php (2)
  • getFormattedContext (11-11)
  • getContext (9-9)
tests/unit/exception/HasContextTest.php (3)
tests/unit/exception/stub/TestException.php (1)
  • TestException (11-14)
src/exception/HasContext.php (2)
  • getFormattedContext (42-63)
  • addContext (17-22)
src/exception/HasContextInterface.php (2)
  • getFormattedContext (11-11)
  • addContext (7-7)
🔇 Additional comments (4)
src/exception/HasContextInterface.php (1)

11-11: LGTM!

The interface extension is clean and the return type is appropriate for formatted output.

tests/unit/exception/HasContextTest.php (1)

67-116: Good test coverage for the new formatting functionality.

The tests cover empty context, simple values, complex values, and exception chaining appropriately.

src/Infrastructure/Exception/ExceptionDebugFormatter.php (1)

11-29: The formatting logic is correct and handles exception chaining well.

The recursive processing of previous exceptions and conditional context inclusion work as intended.

src/exception/HasContext.php (1)

42-63: The formatting implementation is well-structured.

The method correctly builds human-readable output by iterating over context and including previous exception details.

@SilverFire SilverFire merged commit 5fdd73a into hiqdev:master Dec 10, 2025
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants