Skip to content

Fix #106: Improve closure rendering in stack traces#172

Open
WarLikeLaux wants to merge 12 commits into
yiisoft:masterfrom
WarLikeLaux:render-closure-trace
Open

Fix #106: Improve closure rendering in stack traces#172
WarLikeLaux wants to merge 12 commits into
yiisoft:masterfrom
WarLikeLaux:render-closure-trace

Conversation

@WarLikeLaux

@WarLikeLaux WarLikeLaux commented Mar 27, 2026

Copy link
Copy Markdown
Contributor
Q A
Is bugfix? ✔️
New feature?
Docs added?
Tests added? ✔️
Breaks BC?
Fixed issues #106

What does this PR do?

This fixes how closures are shown in the HTML stack trace and stops some trace items from disappearing.

  1. Keep trace items when the source snippet is unavailable. renderCallStackItem() used to return an empty string when the source file could not be read. That removed the whole item from the error page. Now the item still renders with the available file and function data. The source viewer is skipped.

  2. Remove duplicated namespace parts in closure names. On PHP < 8.4, a namespaced closure like Yiisoft\Yii\Gii\{closure} was combined with the class name and ended up as Yiisoft\Yii\Gii\Gii::Yiisoft\Yii\Gii\{closure}. It now renders as Yiisoft\Yii\Gii\Gii::{closure}. Bound closures (class=Closure) also stop showing the extra Closure:: prefix.

  3. Format PHP 8.4+ closure names. PHP 8.4+ uses closure names like {closure:Foo::bar():4} that include the definition location. The HTML template now formats that value as {closure} Foo::bar():4. renderCallStack() also treats both {closure} and {closure:...} as closure frames.

Before / After

Case Before After
Namespaced (PHP < 8.4) Ns\Gii::Ns\{closure} Ns\Gii::{closure}
Bound closure Closure::{closure} {closure}
PHP 8.4+ in method Foo::{closure:Foo::bar():4} {closure} Foo::bar():4
PHP 8.4+ file-level {closure:/app/index.php:12} {closure} /app/index.php:12
Simple (PHP < 8.4) Foo::{closure} Foo::{closure} (unchanged)

Note on missing file/line

Some closures called through internal PHP functions such as array_map do not have file or line in the trace data. In that case the page shows the function name and arguments only, without a source viewer for that frame. This matches Symfony's error handler, which also renders source excerpts only for frames that have a file path. On PHP 8.4+, the formatted closure name still shows where the closure was defined.

BC

No BC break: formatTraceFunctionName() is a new public helper used by the HTML template.

P.S. Existing tests that use custom templates now create temporary template files per test. This avoids random-order interference between tests and does not change runtime behavior.

Summary by CodeRabbit

Release Notes

  • New Features

    • Improved closure rendering in stack traces for better error diagnostics.
  • Bug Fixes

    • Stack-trace items are now retained when source code is unavailable.
  • Tests

    • Added comprehensive test coverage for closure and stack-trace rendering across PHP versions.

@WarLikeLaux WarLikeLaux changed the title Improve closure rendering in stack traces Improve closure rendering in stack traces (#172) Mar 27, 2026
@codecov

codecov Bot commented Mar 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.44%. Comparing base (73428a0) to head (01a2520).

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #172      +/-   ##
============================================
+ Coverage     87.18%   87.44%   +0.26%     
- Complexity      225      233       +8     
============================================
  Files            19       19              
  Lines           710      725      +15     
============================================
+ Hits            619      634      +15     
  Misses           91       91              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes HTML stack trace rendering for closure frames (including PHP 8.4+ {closure:...} format) and ensures stack frames are not dropped when source snippets can’t be read, addressing #106.

Changes:

  • Keep rendering call stack items even when the source file can’t be read or the requested line is out of range (skip only the source excerpt).
  • Introduce HtmlRenderer::formatTraceFunctionName() and use it in the call-stack item template to normalize closure names (PHP < 8.4 and PHP 8.4+).
  • Add/adjust test fixtures and PHPUnit coverage for closure rendering scenarios (method closures, bound closures, internal-function closures, and file-level closures).

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Renderer/HtmlRenderer.php Adds trace function name formatter, recognizes {closure:...} frames, and keeps frames when source can’t be loaded.
templates/_call-stack-item.php Uses the new formatter to display cleaner function/closure names.
tests/Renderer/HtmlRendererTest.php Expands test coverage for closure formatting and for rendering frames without source excerpts.
tests/Support/NamespacedClosureTraceFixture.php Fixture for PHP < 8.4 namespaced closure trace behavior.
tests/Support/FileLevelClosureLoader.php Helper to load a file-level closure fixture for PHP 8.4+ behavior.
tests/Support/file_level_closure_exception.php File-level closure exception generator used by tests.
CHANGELOG.md Records the bugfix in the unreleased section.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Renderer/HtmlRenderer.php Outdated
Comment thread CHANGELOG.md Outdated
Comment thread tests/Renderer/HtmlRendererTest.php Outdated
Comment thread tests/Renderer/HtmlRendererTest.php Outdated
Comment thread tests/Renderer/HtmlRendererTest.php Outdated
Comment thread tests/Renderer/HtmlRendererTest.php Outdated
Comment thread tests/Renderer/HtmlRendererTest.php Outdated
Comment thread tests/Renderer/HtmlRendererTest.php Outdated
Comment thread tests/Support/NamespacedClosureTraceFixture.php Outdated
Comment thread tests/Support/file_level_closure_exception.php Outdated
@vjik vjik requested a review from a team March 28, 2026 17:00
@vjik vjik added the status:code review The pull request needs review. label Mar 28, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/Renderer/HtmlRendererTest.php Outdated
Comment thread tests/Support/file_level_closure_exception.php
Comment thread tests/Renderer/HtmlRendererTest.php

@vjik vjik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The edits are similar to LLM usage without review. Am I right?

Comment thread src/Renderer/HtmlRenderer.php Outdated
WarLikeLaux and others added 2 commits May 31, 2026 15:39
# Conflicts:
#	tests/Renderer/HtmlRendererTest.php
Co-authored-by: Sergei Predvoditelev <sergey.predvoditelev@gmail.com>
@WarLikeLaux WarLikeLaux changed the title Improve closure rendering in stack traces (#172) Fix #106: Improve closure rendering in stack traces May 31, 2026
@WarLikeLaux

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR refactors stack-trace closure rendering and source extraction. HtmlRenderer now provides dedicated formatTraceFunctionName() for normalized trace names, improves closure detection via prefix matching, and explicitly handles invalid source line boundaries. Tests gain dynamic template support and comprehensive closure rendering coverage across PHP versions.

Changes

Closure Rendering and Source Handling

Layer / File(s) Summary
Core closure and source handling improvements
src/Renderer/HtmlRenderer.php, templates/_call-stack-item.php
Closure detection broadens from exact {closure} to {closure prefix match. New formatTraceFunctionName() normalizes PHP 8.4+ {closure:Context:line} and older namespaced closure forms. Source line window calculation separates invalid-line handling from boundary computation.
Test fixtures for closure exceptions
tests/Support/FileLevelClosureLoader.php, tests/Support/NamespacedClosureTraceFixture.php, tests/Support/file_level_closure_exception.php
New support fixtures create closures that throw exceptions at file scope and within namespaced contexts, capturing realistic closure stack traces for cross-version testing.
Test infrastructure and dynamic template generation
tests/Renderer/HtmlRendererTest.php
Test class tracks and cleans temporary template files. New createCustomSetting() helper generates temp template paths and settings. Existing template-driven tests updated to use dynamic temp-file-backed settings.
Boundary and closure rendering tests
tests/Renderer/HtmlRendererTest.php
New tests validate source code rendering at file boundaries and closure formatting across method, bound, internal, namespaced, and file-level forms. Assertions refined for unavailable source scenarios.
Changelog documentation
CHANGELOG.md
Two new entries document closure rendering enhancement and stack-trace preservation when source unavailable.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 Closures now render with grace,
Each trace shows the proper face,
No source? No fear!
Stack traces appear,
With boundaries marked in their place. 🌳

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix #106: Improve closure rendering in stack traces' directly and specifically describes the main objectives of this changeset: fixing closure rendering and preventing trace item loss when source files are unavailable.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Renderer/HtmlRenderer.php`:
- Around line 776-777: The branch that handles invalid snippet lines currently
sets $lines = [] but leaves $line unchanged so templates can render an
impossible non-positive value; modify the branch that checks "if ($line < 0 ||
$lines === false)" to also set $line = null (in addition to $lines = []) so the
frame is preserved but templates receive a null line instead of 0 or negative;
locate the variables $line and $lines in the HtmlRenderer rendering code path
(around the snippet extraction logic) and update that branch to assign $line =
null.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ca807532-a3a2-41b3-9c6c-50a79856dde7

📥 Commits

Reviewing files that changed from the base of the PR and between 73428a0 and 9f87586.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • src/Renderer/HtmlRenderer.php
  • templates/_call-stack-item.php
  • tests/Renderer/HtmlRendererTest.php
  • tests/Support/FileLevelClosureLoader.php
  • tests/Support/NamespacedClosureTraceFixture.php
  • tests/Support/file_level_closure_exception.php

Comment thread src/Renderer/HtmlRenderer.php
@WarLikeLaux

Copy link
Copy Markdown
Contributor Author

The edits are similar to LLM usage without review. Am I right?

Yeah, fair. I do use Claude Code, but I go through the diff and run the tests myself rather than just pasting output. Doesn't mean I catch everything though - I didn't here and that's on me. I'll keep a closer eye on it.

@WarLikeLaux WarLikeLaux requested a review from vjik May 31, 2026 11:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:code review The pull request needs review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants