Skip to content

feat: Enhance DI container type inference and testing. #37

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

Merged
merged 3 commits into from
Jun 13, 2025

Conversation

terabytesoftw
Copy link
Member

@terabytesoftw terabytesoftw commented Jun 13, 2025

Q A
Is bugfix?
New feature? ✔️
Breaks BC?

Summary by CodeRabbit

  • New Features

    • Enhanced type inference for Yii DI Container's get() method, improving accuracy when retrieving services or classes.
    • Added new configuration options for dependency management, supporting development versions and expanded Yii2 version constraints.
  • Bug Fixes

    • Improved error handling and validation for malformed or unsupported service and component configurations.
  • Documentation

    • Updated and expanded documentation for service mapping and type inference logic.
  • Refactor

    • Simplified and optimized internal service and component lookup mechanisms for better performance and clarity.
  • Tests

    • Introduced comprehensive new test suites for service and component resolution, dynamic return type inference, and configuration scenarios.
    • Updated test configurations to streamline and standardize extension testing.

@terabytesoftw terabytesoftw added the enhancement New feature or request label Jun 13, 2025
Copy link

coderabbitai bot commented Jun 13, 2025

Walkthrough

This change refactors the ServiceMap class for improved efficiency and clarity, enhances type inference in the DI container extension, and introduces comprehensive tests for service and component resolution. It also updates dependency configurations and test setups, adding new tests and configuration files for better static analysis and validation.

Changes

File(s) Change Summary
src/ServiceMap.php Refactored: Renamed properties, added reverse class-to-ID map, simplified service/component lookup, improved PHPDoc/type safety, and removed unused imports.
src/type/ContainerDynamicMethodReturnTypeExtension.php Enhanced: Improved type inference logic, clarified fallback behavior, updated PHPDoc, and refined method checks.
composer.json Updated: Added "minimum-stability": "dev" and expanded Yii2 version constraints.
CHANGELOG.md Updated: Added entry for DI container type inference and testing improvements.
tests/ServiceMapComponentTest.php Added: New PHPUnit tests for component resolution and definition retrieval in ServiceMap.
tests/ServiceMapServiceTest.php (was ServiceMapTest.php) Refactored/Renamed: Focused on service resolution, updated assertions, removed component tests, expanded error case coverage.
tests/fixture/data/types/ContainerDynamicMethodReturnType.php Added: New tests for DI container dynamic return type inference covering various service retrieval scenarios.
tests/type/ContainerDynamicMethodReturnTypeExtensionTest.php Added: New test class for PHPStan type inference on DI container methods, with data provider and config loading.
tests/type/ActiveQueryDynamicReturnTypeExtensionTest.php
tests/type/ActiveRecordDynamicStaticMethodReturnTypeExtensionTest.php
tests/type/ActiveRecordDynamicMethodReturnTypeExtensionTest.php
Updated: Changed config file path for extension tests, imported dirname explicitly.
tests/extension-tests.neon Added: New NEON config for extension tests, sets bootstrap and Yii2 config paths.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test Suite
    participant ServiceMap as ServiceMap
    participant Config as Config File

    Test->>ServiceMap: new ServiceMap(configPath)
    ServiceMap->>Config: Load config
    ServiceMap->>ServiceMap: Process services/components
    Test->>ServiceMap: getServiceById(serviceId)
    ServiceMap->>ServiceMap: Lookup service class/definition
    ServiceMap-->>Test: Return class name or null
    Test->>ServiceMap: getComponentDefinitionByClassName(className)
    ServiceMap->>ServiceMap: Reverse lookup component ID/definition
    ServiceMap-->>Test: Return definition or null
Loading

Possibly related PRs

Poem

In the warren of code, we mapped every name,
With lookups so swift, no search is the same.
Components and services, now easy to find,
With tests that ensure we don’t lag behind.
The container’s return types are clever and bright—
A rabbit’s delight in the static code light!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Jun 13, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (4a19ff1) to head (1036002).
Report is 1 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##                main       #37   +/-   ##
===========================================
  Coverage     100.00%   100.00%           
+ Complexity        60        54    -6     
===========================================
  Files              1         1           
  Lines             91        91           
===========================================
  Hits              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.

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: 5

♻️ Duplicate comments (2)
tests/type/ActiveRecordDynamicStaticMethodReturnTypeExtensionTest.php (2)

10-10: Same note on use function dirname; – align with the convention decided in the previous file.


49-50: Config-file path duplication – see earlier remark.

🧹 Nitpick comments (18)
tests/extension-tests.neon (1)

5-6: Bootstrap file path is ambiguous

bootstrapFiles entries are resolved relative to the current NEON file, not to %rootDir%.
If tests/bootstrap.php is intended, make the path explicit to avoid surprises when the file is moved or the NEON file is relocated.

-        - bootstrap.php
+        - %rootDir%/tests/bootstrap.php
CHANGELOG.md (1)

5-8: Minor markdown formatting – missing back-tick and closing parenthesis

- - Enh #37: Enhance `DI container type inference and testing (@terabytesoftw)
+ - Enh #37: Enhance `DI` container type inference and testing (@terabytesoftw)

Correcting this prevents broken inline-code formatting in rendered docs.

composer.json (1)

10-11: minimum-stability: dev without prefer-stable may pull unstable packages

Adding "prefer-stable": true balances flexibility with safety.

     "minimum-stability": "dev",
+    "prefer-stable": true,
tests/type/ActiveRecordDynamicMethodReturnTypeExtensionTest.php (2)

10-10: Import of global dirname() is fine but inconsistent across codebase

If you want to optimise for opcode caching, keep the use function import everywhere; otherwise drop it for brevity.
Stick to one convention to avoid churn.


47-48: Hard-coded config path duplicated in several test classes

Consider extracting the value to a constant or helper to avoid future drift across tests.

tests/type/ActiveQueryDynamicReturnTypeExtensionTest.php (2)

11-12: Unnecessary import

dirname() is already in the global namespace; the explicit use function dirname; adds no value and will be flagged by most linters as an unused import.

-use function dirname;

47-47: Hard-coded path separator may break on non-POSIX tooling

Using a literal '/' inside the path string is fine on runtime PHP, but some static tools running on Windows treat it as a literal. For full portability you can rely on DIRECTORY_SEPARATOR or sprintf:

-        return [dirname(__DIR__) . '/extension-tests.neon'];
+        return [dirname(__DIR__) . DIRECTORY_SEPARATOR . 'extension-tests.neon'];
tests/type/ContainerDynamicMethodReturnTypeExtensionTest.php (2)

7-10: Remove unused import

yii\di\Container isn’t referenced in this class. Keeping only the necessary imports keeps the test surface lean.

-use yii\di\Container;

38-54: Duplicated helper logic across test classes

dataFileAsserts() / getAdditionalConfigFiles() blocks are identical in several classes. Consider extracting them into a shared trait to DRY the test suite.

tests/ServiceMapComponentTest.php (2)

108-111: Assertion message typo

The message contains stray back-ticks and back-quote:
'ServiceMap should return \'' f`or a non-existent component class.'`

-            'ServiceMap should return \'`\' f`or a non-existent component class.',
+            'ServiceMap should return null for a non-existent component class.',

38-65: Reduce boilerplate with setUp()

Every test duplicates the ServiceMap construction. Initialising it in setUp() (or using a data provider) will shorten the file and run marginally faster.

tests/fixture/data/types/ContainerDynamicMethodReturnType.php (2)

70-78: random_int() call is unnecessary

The fixture is analysed, not executed; however, some static analysers still warn on unused pseudo-random logic. A ternary on a literal true/*false*/ would express the intent without side noise.


189-196: Consider registering services to avoid runtime surprises

Although fixture files are parsed only by PHPStan, newcomers may attempt to run them. Registering the service IDs before calling $container->get() would make the examples executable and self-documenting.

src/ServiceMap.php (3)

285-293: Prefer instanceof for Closure detection

is_object($definition) && $definition::class === Closure::class works but is unnecessarily indirect;
$definition instanceof Closure is the idiomatic, slightly faster, and more readable PHP way.

-if (is_object($definition) && $definition::class === Closure::class) {
+if ($definition instanceof Closure) {

303-305: Avoid autoload side-effects in is_subclass_of guard

Calling is_subclass_of($id, BaseObject::class) on an arbitrary service ID may trigger an unnecessary
autoload attempt for non-class strings (e.g. "db").
Guard the check with class_exists($id, /*autoload*/ false) first to prevent accidental autoloading
and the associated I/O cost.

-if (is_subclass_of($id, BaseObject::class)) {
+if (class_exists($id, false) && is_subclass_of($id, BaseObject::class)) {

258-279: Docblock / signature drift

normalizeDefinition()’s PHP type allows int, yet the accompanying @phpstan-param DefinitionType
excludes it. Either drop int from the signature or extend DefinitionType to keep static analysis
in sync with runtime behaviour.

tests/ServiceMapServiceTest.php (2)

330-353: Duplicate test block

testThrowExceptionWhenDefinitionNotArray() and testThrowExceptionWhenDefinitionTypeInteger() use the
same fixture and expect the same exception message, providing no additional coverage. Consider
removing one or merging them to keep the suite lean.


39-52: Strengthen “no-assertion” smoke tests

Using expectNotToPerformAssertions() verifies absence of exceptions, but asserting the created object
improves intent without extra cost:

$serviceMap = new ServiceMap();
self::assertInstanceOf(ServiceMap::class, $serviceMap);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a19ff1 and 0975508.

📒 Files selected for processing (12)
  • CHANGELOG.md (1 hunks)
  • composer.json (1 hunks)
  • src/ServiceMap.php (6 hunks)
  • src/type/ContainerDynamicMethodReturnTypeExtension.php (3 hunks)
  • tests/ServiceMapComponentTest.php (1 hunks)
  • tests/ServiceMapServiceTest.php (4 hunks)
  • tests/extension-tests.neon (1 hunks)
  • tests/fixture/data/types/ContainerDynamicMethodReturnType.php (1 hunks)
  • tests/type/ActiveQueryDynamicReturnTypeExtensionTest.php (2 hunks)
  • tests/type/ActiveRecordDynamicMethodReturnTypeExtensionTest.php (2 hunks)
  • tests/type/ActiveRecordDynamicStaticMethodReturnTypeExtensionTest.php (2 hunks)
  • tests/type/ContainerDynamicMethodReturnTypeExtensionTest.php (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/type/ContainerDynamicMethodReturnTypeExtension.php (2)
src/ServiceMap.php (2)
  • ServiceMap (59-479)
  • getServiceById (201-204)
src/reflection/ComponentPropertyReflection.php (1)
  • getType (145-148)
tests/type/ContainerDynamicMethodReturnTypeExtensionTest.php (3)
tests/type/ActiveQueryDynamicReturnTypeExtensionTest.php (3)
  • DataProvider (50-54)
  • dataFileAsserts (38-43)
  • getAdditionalConfigFiles (45-48)
tests/type/ActiveRecordDynamicMethodReturnTypeExtensionTest.php (3)
  • DataProvider (50-54)
  • dataFileAsserts (38-43)
  • getAdditionalConfigFiles (45-48)
tests/type/ActiveRecordDynamicStaticMethodReturnTypeExtensionTest.php (3)
  • DataProvider (52-56)
  • dataFileAsserts (38-45)
  • getAdditionalConfigFiles (47-50)
tests/fixture/data/types/ContainerDynamicMethodReturnType.php (1)
tests/stub/MyActiveRecord.php (1)
  • MyActiveRecord (29-54)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: phpunit-compatibility / PHP 8.4-windows-latest
  • GitHub Check: phpunit-compatibility / PHP 8.1-windows-latest
🔇 Additional comments (1)
src/type/ContainerDynamicMethodReturnTypeExtension.php (1)

104-106: Early-return guard will hide service-map mistakes

When $serviceClass is null you immediately return MixedType. Falling back to the method signature type (as you do for the no-arg branch) is often more useful to the user. Verify this change is intentional.

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: 2

🧹 Nitpick comments (2)
tests/ServiceMapServiceTest.php (2)

26-32: Docblock bullets lose their formatting

The “Key features” list isn’t recognised by most IDE / api-doc generators because the lines don’t start with *.
Replacing the plain lines with proper * bullets keeps PhpDoc tooling happy.

- * Key features.
- * - Ensures compatibility with fixture-based configuration files.
- * - Resolves service class by ID for valid and initialized services.
- * - Retrieves service definitions by ID and class name.
- * - Returns `null` for non-existent or non-class service IDs.
- * - Throws exceptions for invalid service ID types and non-array service definitions.
- * - Validates error handling for unsupported or malformed configuration files.
+ * Key features  
+ * - Ensures compatibility with fixture-based configuration files.  
+ * - Resolves service class by ID for valid and initialised services.  
+ * - Retrieves service definitions by ID and class name.  
+ * - Returns `null` for non-existent or non-class service IDs.  
+ * - Throws exceptions for invalid service-ID types and non-array service definitions.  
+ * - Validates error handling for unsupported or malformed configuration files.

68-173: High duplication – consider a data-provider

From testReturnNullWhenServiceNonExistent() through testReturnServiceClassWhenSingletonServiceValid() every method:

  1. Builds $fixturePath the same way.
  2. Instantiates ServiceMap.
  3. Performs a single assertSame.

Consolidating into one parametrised test cuts ~150 lines and removes hidden drift risk.

Illustration:

/**
 * @dataProvider serviceCases
 */
public function testGetServiceById(string $serviceId, ?string $expected): void
{
    $serviceMap = new ServiceMap($this->fixturePath());
    self::assertSame($expected, $serviceMap->getServiceById($serviceId));
}

public static function serviceCases(): array
{
    return [
        ['non-existent-service', null],
        ['closure', SplStack::class],
        ['nested-service-class', SplFileInfo::class],
        // …
    ];
}

private function fixturePath(): string
{
    return __DIR__ . DIRECTORY_SEPARATOR . 'fixture' . DIRECTORY_SEPARATOR .
        'config' . DIRECTORY_SEPARATOR . 'config.php';
}

Reduces maintenance noise and accelerates future additions.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b663de and 1036002.

📒 Files selected for processing (8)
  • CHANGELOG.md (1 hunks)
  • composer.json (1 hunks)
  • tests/ServiceMapComponentTest.php (1 hunks)
  • tests/ServiceMapServiceTest.php (5 hunks)
  • tests/type/ActiveQueryDynamicReturnTypeExtensionTest.php (1 hunks)
  • tests/type/ActiveRecordDynamicMethodReturnTypeExtensionTest.php (1 hunks)
  • tests/type/ActiveRecordDynamicStaticMethodReturnTypeExtensionTest.php (1 hunks)
  • tests/type/ContainerDynamicMethodReturnTypeExtensionTest.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • CHANGELOG.md
  • tests/type/ActiveRecordDynamicMethodReturnTypeExtensionTest.php
  • tests/type/ActiveQueryDynamicReturnTypeExtensionTest.php
  • tests/type/ActiveRecordDynamicStaticMethodReturnTypeExtensionTest.php
  • composer.json
  • tests/type/ContainerDynamicMethodReturnTypeExtensionTest.php
  • tests/ServiceMapComponentTest.php
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/ServiceMapServiceTest.php (2)
src/ServiceMap.php (2)
  • ServiceMap (59-477)
  • getServiceById (199-202)
tests/stub/MyActiveRecord.php (1)
  • MyActiveRecord (29-54)
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: phpunit / PHP 8.4-windows-latest
  • GitHub Check: phpunit / PHP 8.2-windows-latest
  • GitHub Check: phpunit / PHP 8.3-windows-latest
  • GitHub Check: phpunit-compatibility / PHP 8.4-windows-latest
  • GitHub Check: phpunit / PHP 8.1-windows-latest
  • GitHub Check: phpunit-compatibility / PHP 8.1-windows-latest
  • GitHub Check: phpunit-compatibility / PHP 8.3-windows-latest
  • GitHub Check: phpunit-compatibility / PHP 8.2-windows-latest
  • GitHub Check: phpunit / PHP 8.1-windows-latest
  • GitHub Check: phpunit / PHP 8.3-windows-latest

@terabytesoftw terabytesoftw merged commit 9820c8f into main Jun 13, 2025
43 checks passed
@terabytesoftw terabytesoftw deleted the feat-mini-3 branch June 13, 2025 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant