Add NoHint exception and improve Unbound error messages
Motivation / モチベーション
従来のUnbound例外メッセージには以下の問題がありました:
-
型もなく名前もない場合のエラーが不明瞭
-
dependency '' with name ''という無意味なメッセージ - 「束縛がない」ではなく「識別できない」という本質が伝わらない
-
-
メッセージ形式が冗長
-
dependency 'FooInterface' with name '' used in file.php:10 ($foo)は長い - 末尾ハイフン
FooInterface-がバグっぽく見える
-
-
依存チェーンの可読性
- クラス名が左揃えでないとスキャンしにくい
- ファイルパスが可変長だと視線移動が多くなる
Design Decisions / 設計判断
NoHint例外の追加
型ヒントも#[Named]も#[Qualifier]もない場合はUnboundとは本質的に異なる:
-
Unbound: 識別子はあるが束縛がない -
NoHint: そもそも識別子がない(Ray.Diが解決しようがない)
セマンティック例外としてクラス名で意味を伝える。
バインディングキー形式の採用
内部のバインディングキー '{type}-{name}' をそのまま使用:
- 一貫性がある
- DIの「型-名前」という概念が明確
-
(name: db)のような括弧は補助情報だけになり不自然
メッセージ形式
クラス名を左端に固定し、依存グラフの構造を一目で把握できるように:
- 'EInterface-' in D.php:7 ($e)
- 'D-' in C.php:7 ($d)
- 'C-' in B.php:7 ($c)
- 'B-' in A.php:7 ($b)
D.php:7の$eのEInterface-が Unbound
それは..(下から)
Aが B- を必要として
Bが C- を必要として
Cが D- を必要として
Dが EInterface-を必要としてそれが解決できなかったから!
$injector->getInstance(A::class);
とAを取得しようとして出たエラーでけど真の問題はAではなく、Eの問題だからEの例外として報告しています。
Summary / 変更内容
- Add
NoHintexception for parameters without type hint or binding name - Improve
Unboundexception message format using binding key notation - Update demo chain-error files with distinct variable names
Message Formats
Unbound: '{type}-{name}' in file:line ($var)
-
'FooInterface-'for type only -
'FooInterface-db'for type with name -
'-db'for name only
NoHint: $var (file:line)
Test Plan
- [x] All existing tests pass
- [x] New
NoHintTestadded - [x] Static analysis passes
Summary by CodeRabbit
-
New Features
- Added a NoHint exception to distinguish DI errors for parameters without type hints or binding names.
-
Bug Fixes
- Improved error handling and diagnostic messages for unbound injection targets; clearer messages in edge cases.
- Adjusted constructor parameter naming across demo classes (no behavioral change).
-
Documentation
- Added docblock describing Unbound exception message formats.
-
Tests
- Added tests and supporting test fixtures to cover NoHint behavior.
✏️ Tip: You can customize this high-level summary in your review settings.
[!NOTE]
Other AI code review bot(s) detected
CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.
Walkthrough
Parameter names in demo classes were renamed. A new NoHint exception was added and used when a parameter has neither a type hint nor a binding name; container/arguments code now detects that case and throws NoHint with a constructed message. Argument meta string formatting was adjusted. New tests and fixtures were added.
Changes
| Cohort / File(s) | Summary |
|---|---|
Demo parameter renaming demo/chain-error/A.php, demo/chain-error/B.php, demo/chain-error/C.php, demo/chain-error/D.php |
Constructors renamed parameter names from generic $dep to $b, $c, $d, $e respectively; type hints unchanged |
New NoHint exception src/di/Exception/NoHint.php |
New final NoHint class extending Unbound to represent parameters with neither a type hint nor a binding name |
Exception documentation src/di/Exception/Unbound.php |
Added PHPDoc describing purpose and message format of Unbound |
DI resolution changes src/di/Container.php, src/di/Arguments.php |
When index is empty (no class/name), NoHint is returned/thrown; Arguments::getParameter rethrows Unbound as NoHint for NoHint cases and a helper builds the NoHint message |
Argument metadata formatting src/di/Argument.php |
Meta sprintf format changed from "dependency '%s' with name '%s' used in %s:%d ($%s)" to "'%s-%s' in %s:%d ($%s)" |
Test fixtures tests/di/Fake/FakeUnNamedClass.php, tests/di/Fake/FakeUnNamedModule.php |
Added test class and module to reproduce a no-type-no-name injection scenario |
NoHint tests tests/di/NoHintTest.php |
New tests asserting NoHint is an Unbound and that injection without type/name throws NoHint |
Sequence Diagram(s)
sequenceDiagram
participant Test as NoHintTest
participant Injector as Injector
participant Container as Container
participant Arguments as Arguments
participant NoHint as NoHint (Exception)
Test->>Injector: request injection for target with unnamed param
Injector->>Container: resolve dependency
Container->>Arguments: getParameter(argument)
Arguments-->>Container: detect Unbound (NoHint case)
Container->>NoHint: construct message and throw NoHint
NoHint-->>Injector: exception propagates
Injector-->>Test: NoHint observed by test
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
- Pay attention to error-path changes in
src/di/Arguments.phpandsrc/di/Container.phpto ensure NoHint is constructed and propagated correctly. - Verify tests in
tests/di/NoHintTest.phpand fixtures (tests/di/Fake/*) align with exception hierarchy and behavior. - Check consumers of
Argument::metafor compatibility with the new meta format.
Possibly related PRs
- ray-di/Ray.Di#294 — Modifies DI core classes (Argument/Unbound docs); likely related to metadata/exception handling changes.
Poem
🐇 I hopped from A down to D, renaming each small paw,
When hints go missing, I shout — "NoHint!" — and point to the flaw.
I nibble on messages, bind tests in a row,
A little rabbit cheering when errors grow. 🥕
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 14.29% 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 'Add NoHint exception and improve Unbound error messages' accurately captures the main changes: introducing a new NoHint exception class and enhancing error message formatting for Unbound exceptions. |
✨ Finishing touches
- [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
- [ ] Commit unit tests in branch
improve-unbound-error-messages
📜 Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between d941f5c324dea575b6daa0562f98c8f7e4b4af6a and 429359dc16a617d3f2a8a751b615a58056aaefe0.
📒 Files selected for processing (1)
-
src/di/Arguments.php(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/di/Arguments.php
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: ci / PHPUnit - PHP 8.5 (windows-latest, highest)
- GitHub Check: Sourcery review
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.
Comment @coderabbitai help to get the list of available commands and usage tips.
Reviewer's Guide
Introduces a dedicated NoHint exception for parameters lacking both type hints and binding names, refines Unbound exception messages to use a unified '{type}-{name}' binding-key format with richer context, updates demo chain-error constructors for clearer variable naming, and adds tests and fakes to cover the new behavior.
Sequence diagram for resolving a parameter and raising NoHint or Unbound
sequenceDiagram
actor Developer
participant Container
participant Arguments
participant Argument
participant BindingResolver as BindingStorage_or_Resolver
participant NoHint
participant Unbound
Developer->>Container: resolve(parameter)
Container->>BindingResolver: lookup(class, name)
BindingResolver-->>Container: missing binding
Container->>Container: unbound(index)
alt class == '' and name == ''
Container-->>Arguments: NoHint instance
else binding key identifiable
Container-->>Arguments: Unbound("'{type}-{name}'")
end
Arguments->>Argument: get()
Arguments->>Container: request dependency for Argument
Container-->>Arguments: throws NoHint or Unbound
alt caught NoHint
Arguments->>Argument: get()
Arguments->>Arguments: getNoHintMsg(argument)
Arguments-->>Developer: throws NoHint("$var (file:line)")
else caught Unbound
Arguments->>Argument: getMeta()
Arguments-->>Developer: throws Unbound(meta)
end
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Add dedicated NoHint exception type and integrate it into the resolution flow when no type hint and no binding name are present. |
|
src/di/Exception/NoHint.phpsrc/di/Container.phpsrc/di/Arguments.php |
| Standardize Unbound exception message format around the '{type}-{name}' binding key and improve metadata used in error-chain output. |
|
src/di/Exception/Unbound.phpsrc/di/Argument.phpsrc/di/Container.php |
| Adjust demo chain-error classes and add tests/fakes to validate NoHint behavior and maintain documentation quality of error chains. |
|
demo/chain-error/A.phpdemo/chain-error/B.phpdemo/chain-error/C.phpdemo/chain-error/D.phptests/di/NoHintTest.phptests/di/Fake/FakeUnNamedModule.phptests/di/Fake/FakeUnNamedClass.php |
Tips and commands
Interacting with Sourcery
-
Trigger a new review: Comment
@sourcery-ai reviewon the pull request. - Continue discussions: Reply directly to Sourcery's review comments.
-
Generate a GitHub issue from a review comment: Ask Sourcery to create an
issue from a review comment by replying to it. You can also reply to a
review comment with
@sourcery-ai issueto create an issue from it. -
Generate a pull request title: Write
@sourcery-aianywhere in the pull request title to generate a title at any time. You can also comment@sourcery-ai titleon the pull request to (re-)generate the title at any time. -
Generate a pull request summary: Write
@sourcery-ai summaryanywhere in the pull request body to generate a PR summary at any time exactly where you want it. You can also comment@sourcery-ai summaryon the pull request to (re-)generate the summary at any time. -
Generate reviewer's guide: Comment
@sourcery-ai guideon the pull request to (re-)generate the reviewer's guide at any time. -
Resolve all Sourcery comments: Comment
@sourcery-ai resolveon the pull request to resolve all Sourcery comments. Useful if you've already addressed all the comments and don't want to see them anymore. -
Dismiss all Sourcery reviews: Comment
@sourcery-ai dismisson the pull request to dismiss all existing Sourcery reviews. Especially useful if you want to start fresh with a new review - don't forget to comment@sourcery-ai reviewto trigger a new review!
Customizing Your Experience
Access your dashboard to:
- Enable or disable review features such as the Sourcery-generated pull request summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.
Getting Help
- Contact our support team for questions or feedback.
- Visit our documentation for detailed guides and information.
- Keep in touch with the Sourcery team by following us on X/Twitter, LinkedIn or GitHub.
Codecov Report
:white_check_mark: All modified and coverable lines are covered by tests.
:white_check_mark: Project coverage is 100.00%. Comparing base (55cabee) to head (429359d).
Additional details and impacted files
@@ Coverage Diff @@
## 2.x #310 +/- ##
===========================================
Coverage 100.00% 100.00%
- Complexity 326 331 +5
===========================================
Files 47 47
Lines 839 852 +13
===========================================
+ Hits 839 852 +13
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
@coderabbitai FakeUnNamedClass and FakeUnNamedModule are in the same namespace (Ray\Di) as the test, so no imports are needed. Tests pass without the suggested use statements.
[!TIP] For best results, initiate chat on the files or code changes.
@koriym You're absolutely right! Since all three classes are in the same Ray\Di namespace, PHP will resolve them without needing explicit use statements. I apologize for the unnecessary suggestion. Thank you for the clarification!
Quality Gate passed
Issues
0 New issues
0 Accepted issues
Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code