Full Code of ray-di/Ray.Di for AI

2.x 13700a23ca7d cached
311 files
279.0 KB
77.0k tokens
940 symbols
1 requests
Download .txt
Showing preview only (343K chars total). Download the full file or copy to clipboard to get everything.
Repository: ray-di/Ray.Di
Branch: 2.x
Commit: 13700a23ca7d
Files: 311
Total size: 279.0 KB

Directory structure:
gitextract_o85d1j_u/

├── .gitattributes
├── .github/
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature.md
│   │   └── question.md
│   ├── SECURITY.md
│   └── workflows/
│       ├── coding-standards.yml
│       ├── continuous-integration.yml
│       ├── demo.yml
│       ├── prefer-lowest.yml
│       ├── static-analysis.yml
│       └── update-copyright-years-in-license-file.yml
├── .gitignore
├── .scrutinizer.yml
├── .sonarcloud.properties
├── CLAUDE.md
├── LICENSE
├── README.md
├── codecov.yml
├── composer-require-checker.json
├── composer.json
├── demo/
│   ├── 01a-linked-binding.php
│   ├── 01b-linked-binding-setter-injection.php
│   ├── 02-provider-binding.php
│   ├── 02a-named-by-qualifier.php
│   ├── 02b-named-by-named.php
│   ├── 03-injection-point.php
│   ├── 04-untarget-binding.php
│   ├── 05a-constructor-binding.php
│   ├── 05b-constructor-binding-setter-injection.php
│   ├── 07-assisted-injection.php
│   ├── 10-cache.php
│   ├── 11-script-injector.php
│   ├── 12-dependency-chain-error-message.php
│   ├── chain-error/
│   │   ├── A.php
│   │   ├── B.php
│   │   ├── C.php
│   │   ├── D.php
│   │   └── EInterface.php
│   ├── finder/
│   │   ├── Db.php
│   │   ├── DbFinder.php
│   │   ├── DbInterface.php
│   │   ├── Finder.php
│   │   ├── FinderInterface.php
│   │   ├── FinderModule.php
│   │   ├── MovieFinder.php
│   │   ├── MovieLister.php
│   │   ├── MovieListerInterface.php
│   │   └── Sorter.php
│   ├── run.php
│   └── tmp/
│       └── .gitkeep
├── phpcs.xml
├── phpmd.xml
├── phpstan.neon
├── phpunit.xml.dist
├── psalm.xml
├── rector.php
├── src/
│   └── di/
│       ├── AbstractModule.php
│       ├── AcceptInterface.php
│       ├── AnnotatedClass.php
│       ├── AnnotatedClassMethods.php
│       ├── Annotation/
│       │   └── ScriptDir.php
│       ├── Argument.php
│       ├── Arguments.php
│       ├── AspectBind.php
│       ├── AssistedInjectInterceptor.php
│       ├── AssistedInjectModule.php
│       ├── AssistedModule.php
│       ├── Bind.php
│       ├── BindValidator.php
│       ├── BuiltinModule.php
│       ├── CompileNullObject.php
│       ├── Container.php
│       ├── ContainerFactory.php
│       ├── Dependency.php
│       ├── DependencyFactory.php
│       ├── DependencyInterface.php
│       ├── DependencyProvider.php
│       ├── Di/
│       │   ├── Assisted.php
│       │   ├── Inject.php
│       │   ├── InjectInterface.php
│       │   ├── Named.php
│       │   ├── PostConstruct.php
│       │   ├── Qualifier.php
│       │   └── Set.php
│       ├── Exception/
│       │   ├── DirectoryNotWritable.php
│       │   ├── ExceptionInterface.php
│       │   ├── InvalidContext.php
│       │   ├── InvalidProvider.php
│       │   ├── InvalidToConstructorNameParameter.php
│       │   ├── InvalidType.php
│       │   ├── MethodInvocationNotAvailable.php
│       │   ├── NoHint.php
│       │   ├── NotFound.php
│       │   ├── SetNotFound.php
│       │   ├── Unbound.php
│       │   └── Untargeted.php
│       ├── Exception.php
│       ├── Grapher.php
│       ├── InjectableInterface.php
│       ├── InjectionPoint.php
│       ├── InjectionPointInterface.php
│       ├── InjectionPoints.php
│       ├── Injector.php
│       ├── InjectorInterface.php
│       ├── Instance.php
│       ├── Matcher/
│       │   └── AssistedInjectMatcher.php
│       ├── MethodInvocationProvider.php
│       ├── ModuleString.php
│       ├── MultiBinder.php
│       ├── MultiBinding/
│       │   ├── LazyInstance.php
│       │   ├── LazyInterface.php
│       │   ├── LazyProvider.php
│       │   ├── LazyTo.php
│       │   ├── Map.php
│       │   ├── MapProvider.php
│       │   ├── MultiBindingModule.php
│       │   └── MultiBindings.php
│       ├── Name.php
│       ├── NewInstance.php
│       ├── NullDependency.php
│       ├── NullModule.php
│       ├── NullObjectDependency.php
│       ├── ProviderInterface.php
│       ├── ProviderProvider.php
│       ├── ProviderSetModule.php
│       ├── ProviderSetProvider.php
│       ├── Scope.php
│       ├── SetContextInterface.php
│       ├── SetterMethod.php
│       ├── SetterMethods.php
│       ├── SpyCompiler.php
│       ├── Types.php
│       ├── Untarget.php
│       └── VisitorInterface.php
├── src-deprecated/
│   └── di/
│       ├── BcParameterQualifier.php
│       ├── BcStringParser.php
│       ├── EmptyModule.php
│       ├── NullCache.php
│       └── Provider.php
├── tests/
│   ├── bootstrap.php
│   ├── di/
│   │   ├── AnnotatedClassTest.php
│   │   ├── ArgumentTest.php
│   │   ├── ArgumentsTest.php
│   │   ├── AssistedTest.php
│   │   ├── BcParameterQualifierIntegrationTest.php
│   │   ├── BcParameterQualifierTest.php
│   │   ├── BcStringParserTest.php
│   │   ├── BindTest.php
│   │   ├── ContainerTest.php
│   │   ├── ContextualProviderTest.php
│   │   ├── DependencyTest.php
│   │   ├── Fake/
│   │   │   ├── Annotation/
│   │   │   │   ├── FakeInjectOne.php
│   │   │   │   ├── FakeLeft.php
│   │   │   │   ├── FakeNotQualifer.php
│   │   │   │   ├── FakeQualifierOnly.php
│   │   │   │   └── FakeRight.php
│   │   │   ├── FakeAbstractClass.php
│   │   │   ├── FakeAbstractDb.php
│   │   │   ├── FakeAnnoClass.php
│   │   │   ├── FakeAnnoInterceptor1.php
│   │   │   ├── FakeAnnoInterceptor2.php
│   │   │   ├── FakeAnnoInterceptor3.php
│   │   │   ├── FakeAnnoInterceptor4.php
│   │   │   ├── FakeAnnoInterceptor5.php
│   │   │   ├── FakeAnnoInterceptorInterface.php
│   │   │   ├── FakeAnnoMethod1.php
│   │   │   ├── FakeAnnoMethod2.php
│   │   │   ├── FakeAnnoMethod3.php
│   │   │   ├── FakeAnnoModule.php
│   │   │   ├── FakeAnnoOrderClass.php
│   │   │   ├── FakeAop.php
│   │   │   ├── FakeAopDoublyInstallModule.php
│   │   │   ├── FakeAopGrapher.php
│   │   │   ├── FakeAopGrapherModule.php
│   │   │   ├── FakeAopInstallModule.php
│   │   │   ├── FakeAopInterceptorModule.php
│   │   │   ├── FakeAopInterface.php
│   │   │   ├── FakeAopInterfaceModule.php
│   │   │   ├── FakeAopModule.php
│   │   │   ├── FakeAssistedConsumer.php
│   │   │   ├── FakeAssistedDb.php
│   │   │   ├── FakeAssistedDbModule.php
│   │   │   ├── FakeAssistedDbProvider.php
│   │   │   ├── FakeAssistedInjectConsumer.php
│   │   │   ├── FakeAssistedInjectDb.php
│   │   │   ├── FakeAssistedParamsConsumer.php
│   │   │   ├── FakeBcConstructorQualifierClass.php
│   │   │   ├── FakeBcParameterQualifierClass.php
│   │   │   ├── FakeBcParameterQualifierModule.php
│   │   │   ├── FakeBuiltin.php
│   │   │   ├── FakeCar.php
│   │   │   ├── FakeCarEngine.php
│   │   │   ├── FakeCarEngineModule.php
│   │   │   ├── FakeCarInterface.php
│   │   │   ├── FakeCarModule.php
│   │   │   ├── FakeClassInstanceBindModule.php
│   │   │   ├── FakeClassWithBcParameterQualifier.php
│   │   │   ├── FakeConcreteClass.php
│   │   │   ├── FakeConstant.php
│   │   │   ├── FakeConstantConsumer.php
│   │   │   ├── FakeConstantInterface.php
│   │   │   ├── FakeConstantModule.php
│   │   │   ├── FakeContextualModule.php
│   │   │   ├── FakeContextualProvider.php
│   │   │   ├── FakeContextualRobot.php
│   │   │   ├── FakeDoubleInterceptor.php
│   │   │   ├── FakeDoubleInterceptorInterface.php
│   │   │   ├── FakeEngine.php
│   │   │   ├── FakeEngine2.php
│   │   │   ├── FakeEngine3.php
│   │   │   ├── FakeEngineInterface.php
│   │   │   ├── FakeEngineProvider.php
│   │   │   ├── FakeEngineToProviderModule.php
│   │   │   ├── FakeFormerBindingHasPriorityModule.php
│   │   │   ├── FakeGearStickInject.php
│   │   │   ├── FakeGearStickInterface.php
│   │   │   ├── FakeGearStickProvider.php
│   │   │   ├── FakeHandle.php
│   │   │   ├── FakeHandleBar.php
│   │   │   ├── FakeHandleBarQualifier.php
│   │   │   ├── FakeHandleInterface.php
│   │   │   ├── FakeHandleProvider.php
│   │   │   ├── FakeHardtop.php
│   │   │   ├── FakeHardtopInterface.php
│   │   │   ├── FakeInjectionPoint.php
│   │   │   ├── FakeInstallModule.php
│   │   │   ├── FakeInstanceBindModule.php
│   │   │   ├── FakeInstanceBindModule2.php
│   │   │   ├── FakeInstanceBindModuleOneTo3.php
│   │   │   ├── FakeInternalTypeModule.php
│   │   │   ├── FakeInternalTypes.php
│   │   │   ├── FakeLeatherGearStick.php
│   │   │   ├── FakeLeft.php
│   │   │   ├── FakeLeftLeg.php
│   │   │   ├── FakeLegInterface.php
│   │   │   ├── FakeLogStringModule.php
│   │   │   ├── FakeMirrorInterface.php
│   │   │   ├── FakeMirrorLeft.php
│   │   │   ├── FakeMirrorRight.php
│   │   │   ├── FakeModuleInModule.php
│   │   │   ├── FakeModuleInModuleOverride.php
│   │   │   ├── FakeMultiBindingAnnotation.php
│   │   │   ├── FakeMultiBindingConsumer.php
│   │   │   ├── FakeOnionInterceptor2.php
│   │   │   ├── FakeOnionInterceptor3.php
│   │   │   ├── FakeOnionInterceptor4.php
│   │   │   ├── FakeOpenCarModule.php
│   │   │   ├── FakeOverrideInstallModule.php
│   │   │   ├── FakePdoModule.php
│   │   │   ├── FakePhp8Car.php
│   │   │   ├── FakePhp8CarModule.php
│   │   │   ├── FakePhp8HandleProvider.php
│   │   │   ├── FakePriorityModule.php
│   │   │   ├── FakePropConstruct.php
│   │   │   ├── FakeRenameModule.php
│   │   │   ├── FakeRight.php
│   │   │   ├── FakeRightLeg.php
│   │   │   ├── FakeRobot.php
│   │   │   ├── FakeRobotInterface.php
│   │   │   ├── FakeRobotProvider.php
│   │   │   ├── FakeRobotTeam.php
│   │   │   ├── FakeSet.php
│   │   │   ├── FakeSetNotFoundWithMap.php
│   │   │   ├── FakeSetNotFoundWithProvider.php
│   │   │   ├── FakeToBindInvalidClassModule.php
│   │   │   ├── FakeToBindModule.php
│   │   │   ├── FakeToBindSingletonModule.php
│   │   │   ├── FakeToConstructorRobot.php
│   │   │   ├── FakeToProviderBindModule.php
│   │   │   ├── FakeToProviderSingletonBindModule.php
│   │   │   ├── FakeTyre.php
│   │   │   ├── FakeTyreInterface.php
│   │   │   ├── FakeUnNamedClass.php
│   │   │   ├── FakeUnNamedModule.php
│   │   │   ├── FakeUntarget.php
│   │   │   ├── FakeUntargetChild.php
│   │   │   ├── FakeUntargetModule.php
│   │   │   ├── FakeUntargetToIntanceModule.php
│   │   │   ├── FakeWalkRobot.php
│   │   │   ├── FakeWalkRobotLegProvider.php
│   │   │   ├── FakeWalkRobotModule.php
│   │   │   ├── FakelNoConstructorCallModule.php
│   │   │   └── NullVisitor.php
│   │   ├── GrapherTest.php
│   │   ├── InjectionPointTest.php
│   │   ├── InjectionPointsTest.php
│   │   ├── InjectorTest.php
│   │   ├── ModuleMergerTest.php
│   │   ├── ModuleTest.php
│   │   ├── MultiBinding/
│   │   │   ├── MultiBinderTest.php
│   │   │   └── MultiBindingModuleTest.php
│   │   ├── NameTest.php
│   │   ├── NewInstanceTest.php
│   │   ├── NoHintTest.php
│   │   ├── NullModuleTest.php
│   │   ├── ProviderProviderTest.php
│   │   ├── SetterMethodTest.php
│   │   ├── SetterMethodsTest.php
│   │   ├── SpyCompilerTest.php
│   │   ├── UnboundTest.php
│   │   ├── VisitorTest.php
│   │   └── script/
│   │       ├── aop.php
│   │       ├── bench.php
│   │       └── grapher.php
│   ├── script/
│   │   └── aop.php.cache
│   ├── stub/
│   │   └── BindInterface.phpstub
│   └── type/
│       └── InjectorInterfaceTest.php
├── tests-php8/
│   ├── AssistedInjectTest.php
│   └── DualReaderTest.php
└── vendor-bin/
    └── tools/
        └── composer.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
# Normalize line endings
* text=auto eol=lf

/tests              export-ignore
/tests-php8         export-ignore
/demo               export-ignore
/demo-php8          export-ignore
/.gitattributes     export-ignore
/.github            export-ignore
/vendor-bin         export-ignore
/.gitignore         export-ignore
/.scrutinizer.yml   export-ignore
/.travis.yml        export-ignore
/codecov.yml        export-ignore
/phpcs.xml          export-ignore
/phpmd.xml          export-ignore
/phpstan.neon       export-ignore
/phpunit.xml.dist   export-ignore
/psalm.xml          export-ignore
/psalm.compiler.xml export-ignore

# Configure diff output for .php and .phar files.
*.php diff=php
*.phar -diff


================================================
FILE: .github/CONTRIBUTING.md
================================================
# CONTRIBUTING

## Code Contributions

## Installation

Install project dependencies and test tools by running the following commands.

```bash
$ composer install
```

## Running tests

```bash
$ composer test
```
```bash
$ composer coverage // xdebug
$ composer pcov     // pcov
```

Add tests for your new code ensuring that you have 100% code coverage.
In rare cases, code may be excluded from test coverage using `@codeCoverageIgnore`.

## Sending a pull request

To ensure your PHP code changes pass the CI checks, make sure to run all the same checks before submitting a PR.

```bash
$ composer tests
```

When you make a pull request, the tests will automatically be run again by GH action on multiple php versions.


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug Report
about: Create a bug report
labels: Bug
---

### Bug Report

<!-- Provide a summary describing the problem you are experiencing. -->

### How to reproduce

<!--- Describe exactly how to reproduce the problem, using a minimal test-case or working code sample. -->



================================================
FILE: .github/ISSUE_TEMPLATE/feature.md
================================================
---
name: Feature
about: Suggest a new feature or enhancement
labels: Feature
---

<!-- Write your suggestion here. -->


================================================
FILE: .github/ISSUE_TEMPLATE/question.md
================================================
---
name: Question
about: Ask a question regarding software usage
labels: Support
---

<!-- Write your question here. -->


================================================
FILE: .github/SECURITY.md
================================================
# Reporting a vulnerability

If you have found any issues that might have security implications,
please send a report privately to akihito.koriyama@gmail.com

Do not report security reports publicly.


================================================
FILE: .github/workflows/coding-standards.yml
================================================
name: Coding Standards

on:
  push:
  pull_request:
  workflow_dispatch:

jobs:
  cs:
    uses: ray-di/.github/.github/workflows/coding-standards.yml@v1
    with:
      php_version: 8.3


================================================
FILE: .github/workflows/continuous-integration.yml
================================================
name: Continuous Integration

on:
  push:
  pull_request:
  workflow_dispatch:

jobs:
  ci:
    uses: ray-di/.github/.github/workflows/continuous-integration.yml@v1
    with:
      old_stable: '["8.2", "8.3", "8.4"]'
      current_stable: 8.5
      script: demo/run.php


================================================
FILE: .github/workflows/demo.yml
================================================
name: Demo

on:
  push:
  pull_request:
  workflow_dispatch:

jobs:
  demo:
    name: Demo
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.3
          tools: cs2pr
          coverage: none

      - name: Get composer cache directory
        id: composer-cache
        run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-

      - name: Install dependencies
        run: composer install --no-interaction --no-progress --prefer-dist

      - name: Run Demo
        run: php demo/run.php


================================================
FILE: .github/workflows/prefer-lowest.yml
================================================
name: Prefer Lowest

on:
  push:
  pull_request:
  workflow_dispatch:

jobs:
  prefer-lowest:
    name: Prefer Lowest
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.2
          tools: cs2pr
          coverage: none

      - name: Get composer cache directory
        id: composer-cache
        run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-

      - name: Install dependencies
        run: composer update --prefer-lowest --no-interaction --no-progress --prefer-dist

      - name: Run tests
        run: composer test


================================================
FILE: .github/workflows/static-analysis.yml
================================================
name: Static Analysis

on:
  push:
  pull_request:
  workflow_dispatch:

jobs:
  sa:
    uses: ray-di/.github/.github/workflows/static-analysis.yml@v1
    with:
      php_version: 8.3


================================================
FILE: .github/workflows/update-copyright-years-in-license-file.yml
================================================
name: Update copyright year(s) in license file

on:
  workflow_dispatch:
  schedule:
    - cron: "0 3 1 1 *"

jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: FantasticFiasco/action-update-license-year@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .gitignore
================================================
/vendor/
/build/
/tests/compiler/tmp/car/
/tests/compiler/tmp/injector_cache/
/tests/compiler/tmp/logger/
/tests/compiler/tmp/prod/
/tests/di/tmp/
/tests/di/script/grapher.php.txt
/tests/tmp/
/tests/compiler/tmp/
/*.cache.php
/.phpunit.result.cache
/composer.lock
/.phpcs-cache
/coverage.xml
/demo/error.log
/demo/10-cache.php.cache.php
/demo-php8/error.log
/demo-php8/10-cache.php.cache.php
/vendor-bin/**/vendor


================================================
FILE: .scrutinizer.yml
================================================
build:
    image: default-jammy
    environment:
        php: 8.4
    nodes:
        analysis:
            tests:
                override:
                    - php-scrutinizer-run

filter:
    paths: ["src/*"]


================================================
FILE: .sonarcloud.properties
================================================
# Exclude files from SonarCloud analysis
# - GitHub workflow files: version tags are best practice
# - Demo files: example code with intentional duplication for demonstration
sonar.exclusions=.github/workflows/**,demo/**


================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Ray.Di is a dependency injection and AOP (Aspect-Oriented Programming) framework for PHP inspired by Google Guice. It provides annotations-based dependency injection with support for AOP interceptors.

## Core Architecture

### Key Components
- **AbstractModule**: Base class for defining dependency bindings. Modules are composed using `install()` and can be overridden using `override()`
- **Injector**: Main entry point that manages the DI container and creates instances. Auto-registers generated proxy classes and handles untargeted bindings
- **Bind**: Fluent API for creating bindings (`.to()`, `.toProvider()`, `.toInstance()`, `.in()`)
- **Container**: Internal storage for all bindings and dependencies
- **Annotations**: Located in `src/di/Di/` - includes `@Inject`, `@Named`, `@Assisted`, etc.

### Directory Structure
- `src/di/`: Core DI framework code
- `src-deprecated/`: Legacy code maintained for compatibility  
- `tests/di/`: Unit tests with extensive fake classes for testing
- `demo/` and `demo-php8/`: Examples showing framework usage
- Compiled proxy classes are cached in configurable temp directories

## Development Commands

### Testing
```bash
composer test              # Run PHPUnit tests
composer coverage          # Generate test coverage with Xdebug
composer pcov              # Generate coverage with PCOV (faster)
```

### Code Quality
```bash
composer cs                # Run PHP_CodeSniffer
composer cs-fix            # Auto-fix coding standards
composer sa                # Static analysis (Psalm + PHPStan)
composer clean             # Clear analysis caches
```

### Build Pipeline
```bash
composer build             # Full build: cs + sa + pcov + metrics
composer tests             # Quick check: cs + sa + test
```

### Analysis Tools
```bash
composer phpmd             # PHP Mess Detector
composer metrics           # Generate code metrics
composer baseline          # Update static analysis baselines
```

## Testing Strategy

- Tests use extensive fake classes in `tests/di/Fake/` to simulate real-world scenarios
- Supports both PHP 7.2+ and PHP 8+ with separate test suites
- Cache files are automatically cleaned between test runs
- AOP proxy generation is tested with temporary directories

## Framework Patterns

### Module Definition
```php
class MyModule extends AbstractModule
{
    protected function configure(): void
    {
        $this->bind(Interface::class)->to(Implementation::class);
        $this->bind(Service::class)->toProvider(ServiceProvider::class);
    }
}
```

### Injection Usage
```php
$injector = new Injector(new MyModule());
$instance = $injector->getInstance(Interface::class);
```

## Important Notes

- Ray.Di generates proxy classes for AOP which are cached in temp directories
- The framework supports both constructor and setter injection
- All bindings are resolved at runtime with automatic proxy weaving for aspects
- Multi-binding support allows collecting multiple implementations of the same interface

================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2012-2026 Akihito Koriyama

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

================================================
FILE: README.md
================================================
# Ray.Di

## A dependency injection framework for PHP

[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/ray-di/Ray.Di/badges/quality-score.png?b=2.x)](https://scrutinizer-ci.com/g/ray-di/Ray.Di/?branch=2.x)
[![codecov](https://codecov.io/gh/ray-di/Ray.Di/branch/2.x/graph/badge.svg?token=KCQXtu01zc)](https://codecov.io/gh/ray-di/Ray.Di)
[![Type Coverage](https://shepherd.dev/github/ray-di/Ray.Di/coverage.svg)](https://shepherd.dev/github/ray-di/Ray.Di)
[![Continuous Integration](https://github.com/ray-di/Ray.Di/actions/workflows/continuous-integration.yml/badge.svg?branch=2.x)](https://github.com/ray-di/Ray.Di/actions/workflows/continuous-integration.yml)
[![Total Downloads](https://poser.pugx.org/ray/di/downloads)](https://packagist.org/packages/ray/di)

<img src="https://ray-di.github.io/images/logo.svg" width=160  alt="logo">

Ray.Di is DI and AOP framework for PHP inspired by [Google Guice](https://github.com/google/guice/wiki).

https://ray-di.github.io


================================================
FILE: codecov.yml
================================================
codecov:
  notify:
    require_ci_to_pass: yes

coverage:
  status:
    project:
      default:
        target: 100%
    patch:
      default:
        target: 100%


================================================
FILE: composer-require-checker.json
================================================
{
  "symbol-whitelist": [
    "null", "true", "false",
    "static", "self", "parent",
    "array", "string", "int", "float", "bool", "iterable", "callable", "void", "object",
    "Attribute", "ReflectionAttribute",
    "Doctrine\\Common\\Cache\\CacheProvider"
  ]
}


================================================
FILE: composer.json
================================================
{
    "name": "ray/di",
    "description": "Guice style dependency injection framework",
    "keywords": ["di", "aop"],
    "license": "MIT",
    "authors": [
        {
            "name": "Akihito Koriyama",
            "email": "akihito.koriyama@gmail.com"
        }
    ],
    "require": {
        "php": "^8.2",
        "koriym/null-object": "^1.0",
        "ray/aop": "^2.19"
    },
    "require-dev": {
        "ext-pdo": "*",
        "bamarni/composer-bin-plugin": "^1.4",
        "infection/infection": "*",
        "phpunit/phpunit": "^9.6.31"
    },
    "suggest": {
        "ray/compiler": "For compiling dependency injection container to improve performance"
    },
    "config": {
        "sort-packages": true,
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true,
            "infection/extension-installer": true
        }
    },
    "autoload": {
        "psr-4": {
            "Ray\\Di\\": ["src/di", "src-deprecated/di"]
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Ray\\Di\\": ["tests/di", "tests/di/Fake/"]
        }
    },
    "scripts": {
        "test": "phpunit --log-junit=build/junit.xml",
        "tests": ["@cs", "@sa", "@test"],
        "coverage": ["php -dzend_extension=xdebug.so -dxdebug.mode=coverage ./vendor/bin/phpunit --coverage-text --coverage-html=build/coverage"],
        "pcov": ["php -dextension=pcov.so -d pcov.enabled=1 ./vendor/bin/phpunit --coverage-text --coverage-html=build/coverage --coverage-xml=build/coverage-xml --coverage-clover=coverage.xml"],
        "mutation": ["php -dextension=pcov.so -d pcov.enabled=1 vendor/bin/infection --coverage=build/coverage-xml --threads=2 --min-msi=80 --min-covered-msi=80 --no-progress"],
        "cs": ["vendor-bin/tools/vendor/squizlabs/php_codesniffer/bin/phpcs --standard=./phpcs.xml src tests"],
        "cs-fix": ["vendor-bin/tools/vendor/squizlabs/php_codesniffer/bin/phpcbf src tests"],
        "clean": ["vendor-bin/tools/vendor/phpstan/phpstan/phpstan clear-result-cache", "vendor-bin/tools/vendor/vimeo/psalm/psalm --clear-cache", "rm -rf tests/tmp/*.php"],
        "sa": ["vendor-bin/tools/vendor/vimeo/psalm/psalm -m -c psalm.xml --show-info=false", "vendor-bin/tools/vendor/phpstan/phpstan/phpstan analyse -c phpstan.neon --no-progress "],
        "metrics": ["@test", "vendor-bin/tools/vendor/phpmetrics/phpmetrics/bin/phpmetrics --report-html=build/metrics --exclude=Exception --log-junit=build/junit.xml --junit=build/junit.xml src"],
        "phpmd": ["vendor-bin/tools/vendor/phpmd/phpmd/src/bin/phpmd src/di text ./phpmd.xml"],
        "build": ["@cs", "@sa", "@pcov", "@metrics"],
        "baseline": ["vendor-bin/tools/vendor/phpstan/phpstan/phpstan analyse -c phpstan.neon --generate-baseline", "vendor-bin/tools/vendor/vimeo/psalm/psalm --set-baseline=psalm-baseline.xml"]
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}


================================================
FILE: demo/01a-linked-binding.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\AbstractModule;
use Ray\Di\Injector;

require dirname(__DIR__) . '/vendor/autoload.php';

interface FinderInterface
{
}

class Finder implements FinderInterface
{
}

interface MovieListerInterface
{
}

class MovieLister implements MovieListerInterface
{
    public function __construct(
        public FinderInterface $finder
    ){}
}

class FinderModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(FinderInterface::class)->to(Finder::class);
        $this->bind(MovieListerInterface::class)->to(MovieLister::class);
    }
}

$injector = new Injector(new FinderModule());
$movieLister = $injector->getInstance(MovieListerInterface::class);
/** @var MovieLister $movieLister */
$works = ($movieLister->finder instanceof Finder);

echo($works ? 'It works!' : 'It DOES NOT work!') . PHP_EOL;


================================================
FILE: demo/01b-linked-binding-setter-injection.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\AbstractModule;
use Ray\Di\Di\Inject;
use Ray\Di\Injector;

require dirname(__DIR__) . '/vendor/autoload.php';

interface FinderInterface
{
}

class Finder implements FinderInterface
{
}

interface MovieListerInterface
{
}

class MovieLister implements MovieListerInterface
{
    public FinderInterface $finder;

    #[Inject]
    public function setFinder(FinderInterface $finder)
    {
        $this->finder = $finder;
    }
}

class FinderModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(FinderInterface::class)->to(Finder::class);
        $this->bind(MovieListerInterface::class)->to(MovieLister::class);
    }
}

$injector = new Injector(new FinderModule());
$movieLister = $injector->getInstance(MovieListerInterface::class);
/** @var MovieLister $movieLister */
$works = ($movieLister->finder instanceof Finder);

echo($works ? 'It works!' : 'It DOES NOT work!') . PHP_EOL;


================================================
FILE: demo/02-provider-binding.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\AbstractModule;
use Ray\Di\Injector;
use Ray\Di\ProviderInterface;

require dirname(__DIR__) . '/vendor/autoload.php';

interface FinderInterface
{
}

class Finder implements FinderInterface
{
    public $datetime;

    public function __construct(DateTimeInterface $dateTime)
    {
        $this->datetime = $dateTime;
    }
}

class FinderProvider implements ProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function get()
    {
        return new Finder(new DateTimeImmutable('now'));
    }
}

interface MovieListerInterface
{
}

class MovieLister implements MovieListerInterface
{
    public function __construct(
        public FinderInterface $finder
    ){
    }
}

class FinderModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(FinderInterface::class)->toProvider(FinderProvider::class);
        $this->bind(MovieListerInterface::class)->to(MovieLister::class);
    }
}

$injector = new Injector(new FinderModule());
$movieLister = $injector->getInstance(MovieListerInterface::class);
/** @var MovieLister $movieLister */
$works = ($movieLister->finder->datetime instanceof DateTimeImmutable);

echo($works ? 'It works!' : 'It DOES NOT work!') . PHP_EOL;


================================================
FILE: demo/02a-named-by-qualifier.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\AbstractModule;
use Ray\Di\Di\Qualifier;
use Ray\Di\Injector;

require dirname(__DIR__) . '/vendor/autoload.php';

interface FinderInterface
{
}

class LegacyFinder implements FinderInterface
{
}

interface MovieListerInterface
{
}

class MovieLister implements MovieListerInterface
{
    public FinderInterface $finder;

    public function __construct(#[Legacy] FinderInterface $finder)
    {
        $this->finder = $finder;
    }
}

#[Attribute(Attribute::TARGET_PARAMETER), Qualifier]
class Legacy
{
}

class FinderModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(FinderInterface::class)->annotatedWith(Legacy::class)->to(LegacyFinder::class);
        $this->bind(MovieListerInterface::class)->to(MovieLister::class);
    }
}

$injector = new Injector(new FinderModule());
$movieLister = $injector->getInstance(MovieListerInterface::class);
/** @var MovieLister $movieLister */
$works = ($movieLister->finder instanceof LegacyFinder);

echo($works ? 'It works!' : 'It DOES NOT work!') . PHP_EOL;


================================================
FILE: demo/02b-named-by-named.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\AbstractModule;
use Ray\Di\Di\Named;
use Ray\Di\Injector;

require dirname(__DIR__) . '/vendor/autoload.php';

interface FinderInterface
{
}

class LegacyFinder implements FinderInterface
{
}

class ModernFinder implements FinderInterface
{
}

interface MovieListerInterface
{
}

class MovieLister implements MovieListerInterface
{
    public function __construct(
        #[Named('legacy')] public FinderInterface $finder
    ){}
}

class FinderModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(FinderInterface::class)->annotatedWith('legacy')->to(LegacyFinder::class);
        $this->bind(MovieListerInterface::class)->to(MovieLister::class);
    }
}

$injector = new Injector(new FinderModule());
$movieLister = $injector->getInstance(MovieListerInterface::class);
/** @var MovieLister $movieLister */
$works = ($movieLister->finder instanceof LegacyFinder);

echo($works ? 'It works!' : 'It DOES NOT work!') . PHP_EOL;


================================================
FILE: demo/03-injection-point.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\AbstractModule;
use Ray\Di\InjectionPointInterface;
use Ray\Di\Injector;
use Ray\Di\ProviderInterface;

require dirname(__DIR__) . '/vendor/autoload.php';

interface FinderInterface
{
}

interface MovieListerInterface
{
}

class Finder implements FinderInterface
{
    private string $className;

    public function __construct(string $className)
    {
        $this->className = $className;
    }

    public function find(): string
    {
        return sprintf('search for [%s]', $this->className);
    }
}

class MovieLister implements MovieListerInterface
{
    public FinderInterface $finder;

    public function __construct(FinderInterface $finder)
    {
        $this->finder = $finder;
    }
}

class FinderProvider implements ProviderInterface
{
    public function __construct(
        public InjectionPointInterface $ip
    )
    {}

    /**
     * {@inheritdoc}
     */
    public function get()
    {
        $className = $this->ip->getClass()->getName();

        return new Finder($className);
    }
}

class FinderModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(FinderInterface::class)->toProvider(FinderProvider::class);
        $this->bind(MovieListerInterface::class)->to(MovieLister::class);
    }
}

$injector = new Injector(new FinderModule());
$movieLister = $injector->getInstance(MovieListerInterface::class);
/** @var MovieLister $movieLister */
$result = $movieLister->finder->find();
$works = ($result === 'search for [MovieLister]');

echo($works ? 'It works!' : 'It DOES NOT work!') . PHP_EOL;


================================================
FILE: demo/04-untarget-binding.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\AbstractModule;
use Ray\Di\Injector;
use Ray\Di\Scope;

require dirname(__DIR__) . '/vendor/autoload.php';

class Finder
{
}

class FinderModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(Finder::class)->in(Scope::SINGLETON);
    }
}

$injector = new Injector(new FinderModule());
$finder1 = $injector->getInstance(Finder::class);
$finder2 = $injector->getInstance(Finder::class);
$works = spl_object_hash($finder1) === spl_object_hash($finder2);

echo($works ? 'It works!' : 'It DOES NOT work!') . PHP_EOL;


================================================
FILE: demo/05a-constructor-binding.php
================================================
<?php

declare(strict_types=1);

require dirname(__DIR__) . '/vendor/autoload.php';

use Ray\Di\AbstractModule;
use Ray\Di\Injector;

//  public PDO::__construct ( string $dsn [, string $username [, string $password [, array $options ]]] )

class PdoModuleLegacyNaming extends AbstractModule
{
    protected function configure()
    {
        // 'dsn=pdo_dsn' (still) works
        $this->bind(PDO::class)->toConstructor(PDO::class, 'dsn=pdo_dsn');
        $this->bind()->annotatedWith('pdo_dsn')->toInstance('sqlite::memory:');
    }
}

class PdoModule extends AbstractModule
{
    protected function configure()
    {
        // ['dsn' => 'pdo_dsn'] works (recommended)
        $this->bind(PDO::class)->toConstructor(PDO::class, ['dsn' => 'pdo_dsn']);
        $this->bind()->annotatedWith('pdo_dsn')->toInstance('sqlite::memory:');
    }
}

$injector = new Injector(new PdoModuleLegacyNaming());
$pdo = $injector->getInstance(PDO::class);
$works = $pdo instanceof PDO;

$injector = new Injector(new PdoModule());
$pdo = $injector->getInstance(PDO::class);
$works &= $pdo instanceof PDO;

echo($works ? 'It works!' : 'It DOES NOT work!') . PHP_EOL;


================================================
FILE: demo/05b-constructor-binding-setter-injection.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Demo;

use Ray\Di\AbstractModule;
use Ray\Di\InjectionPoints;
use Ray\Di\Injector;

use function dirname;

use const PHP_EOL;

require dirname(__DIR__) . '/vendor/autoload.php';

interface FinderInterface
{
}
interface MovieListerInterface
{
}

class Finder implements FinderInterface
{
}

class MovieLister implements MovieListerInterface
{
    public FinderInterface $finder;

    /**
     * Setter Injection with no Inject annotation
     */
    public function setFinder(FinderInterface $finder)
    {
        $this->finder = $finder;
    }
}

class ListerModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(FinderInterface::class)->to(Finder::class);
        $this->bind(MovieListerInterface::class)->toConstructor(
            MovieLister::class,
            '',
            (new InjectionPoints())->addMethod('setFinder') // or (new InjectionPoints)->addOptionalMethod('setFinder')
        );
    }
}
$injector = new Injector(new ListerModule());
$lister = $injector->getInstance(MovieListerInterface::class);
/** @var MovieLister $lister */
$works = ($lister->finder instanceof FinderInterface);

echo($works ? 'It works!' : 'It DOES NOT work!') . PHP_EOL;


================================================
FILE: demo/07-assisted-injection.php
================================================
<?php

declare(strict_types=1);

use Composer\Autoload\ClassLoader;
use Ray\Di\AbstractModule;
use Ray\Di\Injector;

$loader = require dirname(__DIR__) . '/vendor/autoload.php';
/** @var ClassLoader $loader */
$loader->addPsr4('', __DIR__ . '/finder');

$injector = new Injector(new class extends AbstractModule{
    protected function configure()
    {
        $this->bind(FinderInterface::class)->to(Finder::class);
    }
});
$finder = $injector->getInstance(MovieFinder::class);
/** @var MovieFinder $finder */
$works = $finder->find('Tokyo Story') === 'searching [Tokyo Story] by [Finder]';

echo($works ? 'It works!' : 'It DOES NOT work!') . PHP_EOL;


================================================
FILE: demo/10-cache.php
================================================
<?php

declare(strict_types=1);

use Composer\Autoload\ClassLoader;
use Ray\Di\Injector;

$loader = require dirname(__DIR__) . '/vendor/autoload.php';
/** @var ClassLoader $loader */
$loader->addPsr4('', __DIR__ . '/finder');

$start = microtime(true);
$injector = new Injector(new FinderModule());
$movieLister = $injector->getInstance(MovieListerInterface::class);
assert($movieLister instanceof MovieLister);
$time1 = microtime(true) - $start;

// save file cache
file_put_contents(__FILE__ . '.cache.php', serialize(new Injector(new FinderModule())));

// cached injector
$start = microtime(true);
$injector = unserialize(file_get_contents(__FILE__ . '.cache.php'));
$movieLister2 = $injector->getInstance(MovieListerInterface::class);
assert($movieLister2 instanceof MovieLister);
$time2 = microtime(true) - $start;

$works = $movieLister instanceof MovieListerInterface;
echo $works ? 'It works!' : 'It DOES NOT work!';
echo ' [Injector cache] x' . round($time1 / $time2) . ' times faster.' . PHP_EOL;


================================================
FILE: demo/11-script-injector.php
================================================
<?php

declare(strict_types=1);

use Composer\Autoload\ClassLoader;
use Ray\Compiler\DiCompiler;
use Ray\Compiler\ScriptInjector;
use Ray\Di\Injector;

$loader = require dirname(__DIR__) . '/vendor/autoload.php';
/** @var ClassLoader $loader */
$loader->addPsr4('', __DIR__ . '/finder');

$start = microtime(true);
$injector = new Injector(new FinderModule());
$movieLister = $injector->getInstance(MovieListerInterface::class);
assert($movieLister instanceof MovieLister);
$time1 = microtime(true) - $start;

// compile
$tmpDir = __DIR__ . '/tmp';
$compiler = new DiCompiler(new FinderModule(), $tmpDir);
$compiler->compile();
$scriptInjector = new ScriptInjector($tmpDir);
$movieLister2 = $scriptInjector->getInstance(MovieListerInterface::class);

// script injector
$start = microtime(true);
$movieLister2 = $scriptInjector->getInstance(MovieListerInterface::class);
assert($movieLister2 instanceof MovieLister);
$time2 = microtime(true) - $start;

$works = $movieLister instanceof MovieListerInterface;
echo $works ? 'It works!' : 'It DOES NOT work!';
echo ' [Script injector] x' . round($time1 / $time2) . ' times faster.' . PHP_EOL;


================================================
FILE: demo/12-dependency-chain-error-message.php
================================================
<?php

declare(strict_types=1);

use Composer\Autoload\ClassLoader;
use Ray\Di\AbstractModule;
use Ray\Di\Exception\Unbound;
use Ray\Di\Injector;

$loader = require dirname(__DIR__) . '/vendor/autoload.php';
/** @var ClassLoader $loader */
$loader->addPsr4('', __DIR__ . '/chain-error');

class DeepLinkedClassBindingModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(A::class);
        $this->bind(B::class);
        $this->bind(C::class);
        $this->bind(D::class);
        // purposefully not bound.
        // D will require EInterface object to be injected, but
        // EInterface is not bound and an Unbound exception is thrown.
    }
}

$injector = new Injector(new DeepLinkedClassBindingModule());
// this will fail with an exception as EInterface is not bound

try {
    $injector->getInstance(A::class);
} catch (Unbound $e) {
    $msg = $e->getMessage();
    ob_start();
    // dependency 'B' with name '' used
    //    dependency 'B' with name '' used in12-dependency-chain-error-message.php:11
    echo PHP_EOL . '---------' . PHP_EOL;
    echo $e;
//    exception 'Ray\Di\Exception\Unbound' with message 'EInterface-'
//    - dependency 'EInterface' with name '' used in12-dependency-chain-error-message.php:26
//    - dependency 'D' with name '' used in12-dependency-chain-error-message.php:21
//    - dependency 'C' with name '' used in12-dependency-chain-error-message.php:16
//    - dependency 'B' with name '' used in12-dependency-chain-error-message.php:11
    // ...
    echo PHP_EOL . '---------' . PHP_EOL;
    do {
        echo get_class($e) . ':' . $e->getMessage() . PHP_EOL;
    } while ($e = $e->getPrevious());

//    Ray\Di\Exception\Unbound:dependency 'B' with name '' used in12-dependency-chain-error-message.php:11
//    Ray\Di\Exception\Unbound:dependency 'C' with name '' used in12-dependency-chain-error-message.php:16
//    Ray\Di\Exception\Unbound:dependency 'D' with name '' used in12-dependency-chain-error-message.php:21
//    Ray\Di\Exception\Unbound:dependency 'EInterface' with name '' used in12-dependency-chain-error-message.php:26
//    Ray\Di\Exception\Unbound:EInterface-
}

$log = ob_get_clean();
$works = strpos($log, "dependency 'B' with name '' used");
echo $works ? 'It works!' : 'It DOES NOT work!';
file_put_contents(__DIR__ . '/error.log', $log);

echo '[error log]: ' . __DIR__ . '/error.log' . PHP_EOL;


================================================
FILE: demo/chain-error/A.php
================================================
<?php

declare(strict_types=1);

class A
{
    public function __construct(B $b)
    {
    }
}


================================================
FILE: demo/chain-error/B.php
================================================
<?php

declare(strict_types=1);

class B
{
    public function __construct(C $c)
    {
    }
}


================================================
FILE: demo/chain-error/C.php
================================================
<?php

declare(strict_types=1);

class C
{
    public function __construct(D $d)
    {
    }
}


================================================
FILE: demo/chain-error/D.php
================================================
<?php

declare(strict_types=1);

class D
{
    public function __construct(EInterface $e)
    {
    }
}


================================================
FILE: demo/chain-error/EInterface.php
================================================
<?php

declare(strict_types=1);

interface EInterface
{
}


================================================
FILE: demo/finder/Db.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\Di\PostConstruct;

class Db implements DbInterface
{
    public function __construct($dsn, $username, $password)
    {
    }

    #[PostConstruct]
    public function init()
    {
    }
}


================================================
FILE: demo/finder/DbFinder.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\Di\Inject;

class DbFinder implements FinderInterface
{
    public function __construct(DbInterface $db)
    {
    }

    #[Inject]
    public function setDb(DbInterface $db)
    {
    }

    #[Inject]
    public function setSorter(Sorter $sorter, Sorter $sorte2)
    {
    }
}


================================================
FILE: demo/finder/DbInterface.php
================================================
<?php

declare(strict_types=1);

interface DbInterface
{
}


================================================
FILE: demo/finder/Finder.php
================================================
<?php

declare(strict_types=1);

class Finder implements FinderInterface
{
}


================================================
FILE: demo/finder/FinderInterface.php
================================================
<?php

declare(strict_types=1);

interface FinderInterface
{
}


================================================
FILE: demo/finder/FinderModule.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\AbstractModule;
use Ray\Di\Scope;

class FinderModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(Sorter::class)->in(Scope::SINGLETON);
        $this->bind(DbInterface::class)->toConstructor(Db::class, 'dsn=dsn,username=username,password=password');
        $this->bind()->annotatedWith('dsn')->toInstance('msql:host=localhost;dbname=test');
        $this->bind()->annotatedWith('username')->toInstance('root');
        $this->bind()->annotatedWith('password')->toInstance('');
        $this->bind(FinderInterface::class)->to(DbFinder::class);
        $this->bind(MovieListerInterface::class)->to(MovieLister::class);
    }
}


================================================
FILE: demo/finder/MovieFinder.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\Di\Assisted;
use Ray\Di\Di\Inject;

class MovieFinder
{
    public function find($name, #[Inject] ?FinderInterface $finder = null)
    {
        return sprintf('searching [%s] by [%s]', $name, get_class($finder));
    }
}


================================================
FILE: demo/finder/MovieLister.php
================================================
<?php

declare(strict_types=1);

use Ray\Di\Di\Inject;

class MovieLister implements MovieListerInterface
{
    public function __construct(FinderInterface $finder)
    {
    }

    #[Inject]
    public function setFinder01(FinderInterface $finder, FinderInterface $finder1, FinderInterface $finder2)
    {
    }
}


================================================
FILE: demo/finder/MovieListerInterface.php
================================================
<?php

declare(strict_types=1);

interface MovieListerInterface
{
}


================================================
FILE: demo/finder/Sorter.php
================================================
<?php

declare(strict_types=1);

class Sorter
{
}


================================================
FILE: demo/run.php
================================================
<?php

declare(strict_types=1);

putenv('TMPDIR=' . __DIR__ . '/tmp');

passthru('php ' . __DIR__ . '/01a-linked-binding.php');
passthru('php ' . __DIR__ . '/01b-linked-binding-setter-injection.php');
passthru('php ' . __DIR__ . '/02-provider-binding.php');
passthru('php ' . __DIR__ . '/02a-named-by-qualifier.php');
passthru('php ' . __DIR__ . '/02b-named-by-named.php');
passthru('php ' . __DIR__ . '/03-injection-point.php');
passthru('php ' . __DIR__ . '/04-untarget-binding.php');
passthru('php ' . __DIR__ . '/05a-constructor-binding.php');
passthru('php ' . __DIR__ . '/05b-constructor-binding-setter-injection.php');
passthru('php ' . __DIR__ . '/07-assisted-injection.php');
passthru('php ' . __DIR__ . '/10-cache.php');
passthru('php ' . __DIR__ . '/11-script-injector.php');
passthru('php ' . __DIR__ . '/12-dependency-chain-error-message.php');


================================================
FILE: demo/tmp/.gitkeep
================================================


================================================
FILE: phpcs.xml
================================================
<?xml version="1.0"?>
<ruleset
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="bearcs"
    xsi:noNamespaceSchemaLocation="./vendor-bin/tools/vendor/squizlabs/php_codesniffer/phpcs.xsd">

    <arg name="basepath" value="."/>
    <arg name="extensions" value="php"/>
    <arg name="parallel" value="80"/>
    <arg name="cache" value=".phpcs-cache"/>

    <!-- Compatibility with PHP 7.2.0 -->
    <config name="php_version" value="70200"/>

    <!-- Ignore warnings, show progress of the run and show sniff names -->
    <arg value="nps"/>

    <!-- Directories to be checked -->
    <file>src</file>
    <file>tests</file>
    <exclude-pattern>*/tests/tmp/*</exclude-pattern>

    <!-- PSR12 Coding Standard -->
    <rule ref="PSR12"/>

    <!-- Doctrine Coding Standard -->
    <rule ref="Doctrine">
    <!-- Inapplicable for this project -->
        <!-- Base -->
        <exclude name="Generic.Formatting.MultipleStatementAlignment.NotSame"/>
        <exclude name="Squiz.Strings.DoubleQuoteUsage.ContainsVar"/>
        <exclude name="Squiz.WhiteSpace.LanguageConstructSpacing"/>
        <exclude name="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix"/>
        <exclude name="SlevomatCodingStandard.Classes.SuperfluousExceptionNaming.SuperfluousSuffix"/>
        <exclude name="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming.SuperfluousSuffix"/>
        <exclude name="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable"/>
        <exclude name="SlevomatCodingStandard.Commenting.UselessInheritDocComment.UselessInheritDocComment"/>
        <exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint"/>
        <exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint"/>
        <exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint"/>
        <exclude name="Squiz.PHP.GlobalKeyword.NotAllowed"/>
        <exclude name="SlevomatCodingStandard.PHP.RequireExplicitAssertion.RequiredExplicitAssertion"/>
        <exclude name="SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.InvalidFormat"/>
        <exclude name="Squiz.Commenting.FunctionComment.InvalidNoReturn"/>
        <!-- PHP8 attribute -->
        <exclude name="PSR12.Files.FileHeader.IncorrectOrder"/>
        <!-- /Base -->
        <!-- Option -->
        <exclude name="SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed"/>
        <!-- /Option -->
        <!-- exclude for PHP8 attributes -->
        <exclude name="PEAR.Commenting.InlineComment.WrongStyle" />
        <exclude name="PSR12.Files.FileHeader.SpacingAfterBlock" />
        <!-- Exclude Fake files form Doctrine CS -->
        <exclude-pattern>*/tests/Fake/*</exclude-pattern>
    </rule>

    <!-- Additional Rules -->
    <rule ref="Generic.WhiteSpace.LanguageConstructSpacing"/>
    <rule ref="SlevomatCodingStandard.Namespaces.UnusedUses">
        <properties>
            <property name="searchAnnotations" value="true"/>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.Commenting.DocCommentSpacing">
        <properties>
            <property name="annotationsGroups" type="array">
                <element value="@param, @psalm-param, @phpstan-param"/>
                <element value="@return, @psalm-return, @phpstan-return"/>
                <element value="@throws"/>
            </property>
        </properties>
    </rule>
    <rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint">
        <include-pattern>src/Module/*</include-pattern>
        <include-pattern>tests/*</include-pattern>
    </rule>
    <rule ref="SlevomatCodingStandard.Classes.PropertySpacing">
        <properties>
            <property name="minLinesCountBeforeWithComment" value="1"/>
            <property name="maxLinesCountBeforeWithComment" value="1"/>
            <property name="maxLinesCountBeforeWithoutComment" value="0"/>
        </properties>
    </rule>
    <exclude-pattern>*/Fake/*</exclude-pattern>
    <exclude-pattern>*/tmp/*</exclude-pattern>
</ruleset>


================================================
FILE: phpmd.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="PHPMD rule set"
         xmlns="http://pmd.sf.net/ruleset/1.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
         xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
    <!--codesize-->
    <rule ref="rulesets/codesize.xml/CyclomaticComplexity"/>
    <rule ref="rulesets/codesize.xml/NPathComplexity"/>
    <rule ref="rulesets/codesize.xml/ExcessiveClassComplexity"/>
    <rule ref="rulesets/codesize.xml/ExcessiveClassLength"/>
    <rule ref="rulesets/codesize.xml/ExcessiveMethodLength"/>
    <rule ref="rulesets/codesize.xml/ExcessiveParameterList"/>
    <rule ref="rulesets/codesize.xml/ExcessivePublicCount"/>
    <rule ref="rulesets/codesize.xml/TooManyFields"/>
    <rule ref="rulesets/codesize.xml/TooManyMethods"/>
    <!--design-->
    <rule ref="rulesets/design.xml/EvalExpression"/>
    <rule ref="rulesets/design.xml/ExitExpression"/>
    <rule ref="rulesets/design.xml/GotoStatement" />
    <rule ref="rulesets/design.xml/NumberOfChildren"/>
    <rule ref="rulesets/design.xml/DepthOfInheritance"/>
    <!-- <rule ref="rulesets/design.xml/CouplingBetweenObjects" /> -->
    <!--naming-->
    <rule ref="rulesets/naming.xml/ConstantNamingConventions"/>
    <rule ref="rulesets/naming.xml/BooleanGetMethodName"/>
    <!--unusedcode-->
    <rule ref="rulesets/unusedcode.xml/UnusedFormalParameter"/>
    <rule ref="rulesets/unusedcode.xml/UnusedLocalVariable"/>
    <rule ref="rulesets/unusedcode.xml/UnusedPrivateField"/>
    <rule ref="rulesets/unusedcode.xml/UnusedPrivateMethod"/>
    <!--controversial-->
    <rule ref="rulesets/controversial.xml/CamelCaseClassName"/>
    <rule ref="rulesets/controversial.xml/CamelCasePropertyName"/>
    <rule ref="rulesets/controversial.xml/CamelCaseMethodName"/>
    <!--cleancode-->
    <rule ref="rulesets/cleancode.xml/BooleanArgumentFlag"/>
     <rule ref="rulesets/cleancode.xml/ElseExpression" />
</ruleset>


================================================
FILE: phpstan.neon
================================================
parameters:
  errorFormat: raw
  level: max
  paths:
    - src/di
    - tests/di
    - tests/type
  excludePaths:
    - tests/tmp/*
    - tests/di/tmp/*
    - tests/Di/tmp/*
    - tests/di/Fake/*
    - tests/di/Ray_Di_*
    - tests/di/script/*
  ignoreErrors:
    - '#contains generic class ReflectionAttribute but does not specify its types:#'
    - '#generic class ReflectionAttribute but does not specify its types:#'


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
        bootstrap="tests/bootstrap.php"
        convertDeprecationsToExceptions="true"
>
  <coverage processUncoveredFiles="true">
    <include>
      <directory suffix=".php">src/di</directory>
    </include>
  </coverage>
  <testsuites>
    <testsuite name="Ray.Di Test Suite">
      <directory>tests/di/</directory>
      <directory phpVersion="8.0.0" phpVersionOperator=">=">tests-php8</directory>
      <directory>tests/di</directory>
    </testsuite>
  </testsuites>
  <php>
    <ini name="error_reporting" value="-1"/>
  </php>
</phpunit>


================================================
FILE: psalm.xml
================================================
<?xml version="1.0"?>
<psalm
    errorLevel="1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="https://getpsalm.org/schema/config"
    xsi:schemaLocation="https://getpsalm.org/schema/config https://psalm.dev/schema/config"
    findUnusedCode="false"
>
    <projectFiles>
        <directory name="src/di"/>
    </projectFiles>
    <stubs>
        <file name="tests/stub/BindInterface.phpstub"/>
    </stubs>
    <issueHandlers>
        <MissingOverrideAttribute errorLevel="suppress"/>
    </issueHandlers>
</psalm>


================================================
FILE: rector.php
================================================
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;

return RectorConfig::configure()
    ->withPaths([
        __DIR__ . '/src/di',
    ])
    ->withSkip([
        __DIR__ . '/src-deprecated',
    ])
    ->withPhpSets(php82: true)
    ->withPreparedSets(
        deadCode: true,
        codeQuality: true,
        codingStyle: true,
        typeDeclarations: true,
        privatization: true,
        instanceOf: true,
        earlyReturn: true
    );


================================================
FILE: src/di/AbstractModule.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\AbstractMatcher;
use Ray\Aop\Matcher;
use Ray\Aop\Pointcut;
use Ray\Aop\PriorityPointcut;
use Stringable;

use function assert;
use function class_exists;
use function interface_exists;

/**
 * @psalm-import-type BindableInterface from Types
 * @psalm-import-type PointcutList from Types
 * @psalm-import-type InterceptorClassList from Types
 */
abstract class AbstractModule implements Stringable
{
    /** @var Matcher */
    protected $matcher;

    /** @var ?AbstractModule */
    protected $lastModule;
    private ?Container $container = null;

    public function __construct(
        ?self $module = null
    ) {
        $this->lastModule = $module;
        $this->activate();
        if ($module instanceof self && $this->container instanceof Container) {
            $this->container->merge($module->getContainer());
        }
    }

    public function __toString(): string
    {
        return (new ModuleString())($this->getContainer(), $this->getContainer()->getPointcuts());
    }

    /**
     * Install module
     */
    public function install(self $module): void
    {
        $this->getContainer()->merge($module->getContainer());
    }

    /**
     * Override module
     */
    public function override(self $module): void
    {
        $module->getContainer()->merge($this->getContainer());
        $this->container = $module->getContainer();
    }

    /**
     * Return activated container
     */
    public function getContainer(): Container
    {
        if ($this->container === null) {
            $this->activate();
        }

        assert($this->container instanceof Container);

        return $this->container;
    }

    /**
     * Bind interceptor
     *
     * @param InterceptorClassList $interceptors
     */
    public function bindInterceptor(AbstractMatcher $classMatcher, AbstractMatcher $methodMatcher, array $interceptors): void
    {
        $pointcut = new Pointcut($classMatcher, $methodMatcher, $interceptors);
        $this->getContainer()->addPointcut($pointcut);
        foreach ($interceptors as $interceptor) {
            if (class_exists($interceptor)) {
                (new Bind($this->getContainer(), $interceptor))->to($interceptor)->in(Scope::SINGLETON);

                return;
            }

            assert(interface_exists($interceptor));
            (new Bind($this->getContainer(), $interceptor))->in(Scope::SINGLETON);
        }
    }

    /**
     * Bind interceptor early
     *
     * @param InterceptorClassList $interceptors
     */
    public function bindPriorityInterceptor(AbstractMatcher $classMatcher, AbstractMatcher $methodMatcher, array $interceptors): void
    {
        $pointcut = new PriorityPointcut($classMatcher, $methodMatcher, $interceptors);
        $this->getContainer()->addPointcut($pointcut);
        foreach ($interceptors as $interceptor) {
            (new Bind($this->getContainer(), $interceptor))->to($interceptor)->in(Scope::SINGLETON);
        }
    }

    /**
     * Rename binding name
     *
     * @param string $interface       Interface
     * @param string $newName         New binding name
     * @param string $sourceName      Original binding name
     * @param string $targetInterface Original interface
     */
    public function rename(string $interface, string $newName, string $sourceName = Name::ANY, string $targetInterface = ''): void
    {
        $targetInterface = $targetInterface ?: $interface;
        if ($this->lastModule instanceof self) {
            $this->lastModule->getContainer()->move($interface, $sourceName, $targetInterface, $newName);
        }
    }

    /**
     * Configure binding
     *
     * @return void
     *
     * @noinspection ReturnTypeCanBeDeclaredInspection
     */
    abstract protected function configure();

    /**
     * Bind interface
     *
     * @param BindableInterface $interface
     */
    protected function bind(string $interface = ''): Bind
    {
        return new Bind($this->getContainer(), $interface);
    }

    /**
     * Activate bindings
     */
    private function activate(): void
    {
        $this->container = new Container();
        $this->matcher = new Matcher();
        $this->configure();
    }
}


================================================
FILE: src/di/AcceptInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

/**
 * This interface defines the accept method, which allows an object to accept a visitor.
 */
interface AcceptInterface
{
    /**
     * Accepts a visitor and applies its behavior on the current object.
     *
     * @param VisitorInterface $visitor The visitor to accept
     *
     * @return mixed|void
     */
    public function accept(VisitorInterface $visitor);
}


================================================
FILE: src/di/AnnotatedClass.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\ReflectionClass;
use Ray\Di\Di\PostConstruct;
use ReflectionMethod;

final class AnnotatedClass
{
    private AnnotatedClassMethods $injectionMethod;

    public function __construct()
    {
        $this->injectionMethod = new AnnotatedClassMethods();
    }

    /**
     * Return factory instance
     *
     * @phpstan-param ReflectionClass<object> $class Target class reflection
     */
    public function getNewInstance(ReflectionClass $class): NewInstance
    {
        $setterMethods = new SetterMethods([]);
        $methods = $class->getMethods();
        foreach ($methods as $method) {
            if ($method->name === '__construct') {
                continue;
            }

            $setterMethods->add($this->injectionMethod->getSetterMethod($method));
        }

        $name = $this->injectionMethod->getConstructorName($class);

        return new NewInstance($class, $setterMethods, $name);
    }

    /**
     * Return @-PostConstruct method reflection
     *
     * @phpstan-param ReflectionClass<object> $class
     */
    public function getPostConstruct(ReflectionClass $class): ?ReflectionMethod
    {
        $methods = $class->getMethods();
        foreach ($methods as $method) {
            $annotation = $method->getAnnotation(PostConstruct::class);
            if ($annotation instanceof PostConstruct) {
                return $method;
            }
        }

        return null;
    }
}


================================================
FILE: src/di/AnnotatedClassMethods.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\ReflectionClass;
use Ray\Aop\ReflectionMethod;
use Ray\Di\Di\InjectInterface;
use ReflectionAttribute;

final class AnnotatedClassMethods
{
    /**
     * @phpstan-param ReflectionClass<object> $class
     */
    public function getConstructorName(ReflectionClass $class): Name
    {
        $constructor = $class->getConstructor();
        if (! $constructor instanceof \ReflectionMethod) {
            return new Name(Name::ANY);
        }

        $reflMethod = new \ReflectionMethod($class->getName(), '__construct');
        $name = Name::withAttributes($reflMethod);
        if ($name instanceof Name) {
            return $name;
        }

        return new Name(Name::ANY);
    }

    public function getSetterMethod(ReflectionMethod $method): ?SetterMethod
    {
        $inject = $method->getAnnotation(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF);
        if (! $inject instanceof InjectInterface) {
            return null;
        }

        $name = $this->getName($method);
        $setterMethod = new SetterMethod($method, $name);
        if ($inject->isOptional()) {
            $setterMethod->setOptional();
        }

        return $setterMethod;
    }

    private function getName(ReflectionMethod $method): Name
    {
        $name = Name::withAttributes($method);
        if ($name instanceof Name) {
            return $name;
        }

        return new Name(Name::ANY);
    }
}


================================================
FILE: src/di/Annotation/ScriptDir.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Annotation;

use Attribute;
use Ray\Di\Di\Qualifier;

/**
 * Script directory
 *
 * This qualifier should not use in an application code.
 */
#[Attribute, Qualifier]
final class ScriptDir
{
}


================================================
FILE: src/di/Argument.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use ReflectionException;
use ReflectionMethod;
use ReflectionNamedType;
use ReflectionParameter;
use Stringable;

use function assert;
use function in_array;
use function sprintf;

/**
 * @psalm-import-type DependencyIndex from Types
 */
final class Argument implements AcceptInterface, Stringable
{
    public const UNBOUND_TYPE = ['bool', 'int', 'float', 'string', 'array', 'resource', 'callable', 'iterable'];

    /** @var DependencyIndex */
    private string $index;
    private bool $isDefaultAvailable;

    /** @var mixed */
    private $default;
    private string $meta;
    private ReflectionParameter $reflection;

    public function __construct(ReflectionParameter $parameter, string $name)
    {
        $type = $this->getType($parameter);
        $isOptional = $parameter->isOptional();
        $this->isDefaultAvailable = $parameter->isDefaultValueAvailable() || $isOptional;
        if ($isOptional) {
            $this->default = null;
        }

        $this->setDefaultValue($parameter);
        $this->index = $type . '-' . $name;
        $this->reflection = $parameter;
        $this->meta = sprintf(
            "'%s-%s' in %s:%d ($%s)",
            $type,
            $name,
            $this->reflection->getDeclaringFunction()->getFileName(),
            $this->reflection->getDeclaringFunction()->getStartLine(),
            $parameter->getName()
        );
    }

    /**
     * Return index
     *
     * @return DependencyIndex
     */
    public function __toString(): string
    {
        return $this->index;
    }

    /**
     * Return reflection
     */
    public function get(): ReflectionParameter
    {
        return $this->reflection;
    }

    public function isDefaultAvailable(): bool
    {
        return $this->isDefaultAvailable;
    }

    /**
     * @return mixed
     */
    public function getDefaultValue()
    {
        return $this->default;
    }

    public function getMeta(): string
    {
        return $this->meta;
    }

    /**
     * @return array<mixed>
     */
    public function __serialize(): array
    {
        $method = $this->reflection->getDeclaringFunction();
        assert($method instanceof ReflectionMethod);
        $ref = [
            $method->class,
            $method->name,
            $this->reflection->getName(),
        ];

        return [
            $this->index,
            $this->isDefaultAvailable,
            $this->default,
            $this->meta,
            $ref,
        ];
    }

    /**
     * @param array{0: DependencyIndex, 1: bool, 2: string, 3: string, 4: string, 5: array{0: string, 1: string, 2:string}} $unserialized
     */
    public function __unserialize(array $unserialized): void
    {
        [
            $this->index,
            $this->isDefaultAvailable,
            $this->default,
            $this->meta,
            $ref,
        ] = $unserialized;
        $this->reflection = new ReflectionParameter([$ref[0], $ref[1]], $ref[2]);
    }

    /** @inheritDoc */
    public function accept(VisitorInterface $visitor): void
    {
        $visitor->visitArgument(
            $this->index,
            $this->isDefaultAvailable,
            $this->default,
            $this->reflection
        );
    }

    private function setDefaultValue(ReflectionParameter $parameter): void
    {
        if (! $this->isDefaultAvailable) {
            return;
        }

        try {
            $this->default = $parameter->getDefaultValue();
            // @codeCoverageIgnoreStart
        } catch (ReflectionException) {
            $this->default = null;
            // @codeCoverageIgnoreEnd
        }
    }

    /**
     * @psalm-pure
     */
    private function getType(ReflectionParameter $parameter): string
    {
        $type = $parameter->getType();

        return $type instanceof ReflectionNamedType && ! in_array($type->getName(), self::UNBOUND_TYPE, true) ? $type->getName() : '';
    }
}


================================================
FILE: src/di/Arguments.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Di\Exception\NoHint;
use Ray\Di\Exception\Unbound;
use ReflectionMethod;

use function sprintf;

/**
 * @psalm-import-type ArgumentsList from Types
 * @psalm-import-type MethodArguments from Types
 */
final class Arguments implements AcceptInterface
{
    /** @var ArgumentsList */
    private array $arguments = [];

    public function __construct(ReflectionMethod $method, Name $name)
    {
        $parameters = $method->getParameters();
        foreach ($parameters as $parameter) {
            $this->arguments[] = new Argument($parameter, $name($parameter));
        }
    }

    /**
     * Return arguments
     *
     * @return list<mixed>
     *
     * @throws Exception\Unbound
     */
    public function inject(Container $container): array
    {
        $parameters = [];
        foreach ($this->arguments as $parameter) {
            /** @psalm-suppress MixedAssignment */
            $parameters[] = $this->getParameter($container, $parameter);
        }

        return $parameters;
    }

    public function accept(VisitorInterface $visitor): void
    {
        $visitor->visitArguments($this->arguments);
    }

    /**
     * @return mixed
     *
     * @throws Unbound
     */
    private function getParameter(Container $container, Argument $argument)
    {
        $this->bindInjectionPoint($container, $argument);
        try {
            return $container->getDependency((string) $argument);
        } catch (Unbound $unbound) {
            if ($argument->isDefaultAvailable()) {
                return $argument->getDefaultValue();
            }

            if ($unbound instanceof NoHint) {
                throw new NoHint($this->getNoHintMsg($argument), 0, $unbound);
            }

            throw new Unbound($argument->getMeta(), 0, $unbound);
        }
    }

    private function bindInjectionPoint(Container $container, Argument $argument): void
    {
        $isSelf = (string) $argument === InjectionPointInterface::class . '-' . Name::ANY;
        if ($isSelf) {
            return;
        }

        (new Bind($container, InjectionPointInterface::class))->toInstance(new InjectionPoint($argument->get()));
    }

    private function getNoHintMsg(Argument $argument): string
    {
        $ref = $argument->get();
        $func = $ref->getDeclaringFunction();
        $fileName = $func->getFileName();

        return sprintf(
            '$%s (%s:%d)',
            $ref->getName(),
            $fileName !== false ? $fileName : 'unknown file',
            $func->getStartLine()
        );
    }
}


================================================
FILE: src/di/AspectBind.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\Bind as AopBind;
use Ray\Aop\MethodInterceptor;

use function assert;

/**
 * @psalm-import-type MethodInterceptorBindings from Types
 */
final class AspectBind implements AcceptInterface
{
    public function __construct(private AopBind $bind)
    {
    }

    /**
     * Instantiate interceptors
     *
     * @return MethodInterceptorBindings
     */
    public function inject(Container $container): array
    {
        $bindings = $this->bind->getBindings();
        $instantiatedBindings = [];
        foreach ($bindings as $methodName => $interceptorClassNames) {
            $interceptors = [];
            foreach ($interceptorClassNames as $interceptorClassName) {
                /** @var class-string $interceptorClassName */
                $interceptor = $container->getInstance($interceptorClassName);
                assert($interceptor instanceof MethodInterceptor);
                $interceptors[] = $interceptor;
            }

            $instantiatedBindings[$methodName] = $interceptors;
        }

        return $instantiatedBindings;
    }

    /** @inheritDoc */
    public function accept(VisitorInterface $visitor): void
    {
        $visitor->visitAspectBind($this->bind);
    }
}


================================================
FILE: src/di/AssistedInjectInterceptor.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\MethodInterceptor;
use Ray\Aop\MethodInvocation;
use Ray\Di\Di\Assisted;
use Ray\Di\Di\Inject;
use Ray\Di\Di\InjectInterface;
use Ray\Di\Di\Named;
use ReflectionAttribute;
use ReflectionNamedType;
use ReflectionParameter;

use function assert;
use function call_user_func_array;
use function in_array;
use function is_callable;

/**
 * @psalm-import-type NamedArguments from Types
 * @psalm-import-type InjectableValue from Types
 */

/**
 * Assisted injection interceptor for #[Inject] attributed parameter
 *
 * @psalm-import-type NamedArguments from Types
 */
final class AssistedInjectInterceptor implements MethodInterceptor
{
    public function __construct(private InjectorInterface $injector, private MethodInvocationProvider $methodInvocationProvider)
    {
    }

    /**
     * @return mixed
     */
    public function invoke(MethodInvocation $invocation)
    {
        $this->methodInvocationProvider->set($invocation);
        $params = $invocation->getMethod()->getParameters();
        $namedArguments = $this->getNamedArguments($invocation);
        foreach ($params as $param) {
            /** @var list<ReflectionAttribute> $inject */
            $inject = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF); // @phpstan-ignore-line
            /** @var list<ReflectionAttribute> $assisted */
            $assisted = $param->getAttributes(Assisted::class);
            if (isset($assisted[0]) || isset($inject[0])) {
                /** @psalm-suppress MixedAssignment */
                $namedArguments[$param->getName()] = $this->getDependency($param);
            }
        }

        $callable = [$invocation->getThis(), $invocation->getMethod()->getName()];
        assert(is_callable($callable));

        return call_user_func_array($callable, $namedArguments);
    }

    /**
     * @param MethodInvocation<object> $invocation
     *
     * @return array<string, mixed>
     */
    private function getNamedArguments(MethodInvocation $invocation): array
    {
        $args = $invocation->getArguments();
        $params = $invocation->getMethod()->getParameters();
        $namedParams = [];
        foreach ($params as $param) {
            $pos = $param->getPosition();
            if (isset($args[$pos])) {
                /** @psalm-suppress MixedAssignment */
                $namedParams[$param->getName()] = $args[$pos];
            }
        }

        return $namedParams;
    }

    /**
     * @return mixed
     */
    private function getDependency(ReflectionParameter $param)
    {
        $named = (string) $this->getName($param);
        $type = $param->getType();
        assert($type instanceof ReflectionNamedType || $type === null);
        $typeName = $type instanceof ReflectionNamedType ? $type->getName() : '';
        $interface = in_array($typeName, Argument::UNBOUND_TYPE) ? '' : $typeName;

        /** @var class-string $interface */
        return $this->injector->getInstance($interface, $named);
    }

    private function getName(ReflectionParameter $param): ?string
    {
        /** @var list<ReflectionAttribute> $nameds */
        $nameds = $param->getAttributes(Named::class);
        if (isset($nameds[0])) {
            $named = $nameds[0]->newInstance();
            assert($named instanceof Named);

            return $named->value;
        }

        if ($param->getAttributes(Inject::class)) {
            return null;
        }

        return $this->getCustomInject($param);
    }

    /**
     * @return ?class-string
     */
    private function getCustomInject(ReflectionParameter $param): ?string
    {
        /** @var list<ReflectionAttribute> $injects */
        $injects = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF);
        if (! $injects) {
            return null;
        }

        $inject = $injects[0]->newInstance();
        assert($inject instanceof InjectInterface);

        return $inject::class;
    }
}


================================================
FILE: src/di/AssistedInjectModule.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Di\Matcher\AssistedInjectMatcher;

/**
 * Assisted module for php8 attributes
 */
final class AssistedInjectModule extends AbstractModule
{
    protected function configure(): void
    {
        $this->bindInterceptor(
            $this->matcher->any(),
            (new AssistedInjectMatcher()),
            [AssistedInjectInterceptor::class]
        );
    }
}


================================================
FILE: src/di/AssistedModule.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\MethodInvocation;

/**
 * Module for assisted injection using PHP 8 attributes
 *
 * This module enables assisted injection by delegating to AssistedInjectModule
 * and provides necessary bindings for method invocation context.
 * Use #[Assisted] attribute on method parameters to enable automatic injection.
 */
final class AssistedModule extends AbstractModule
{
    protected function configure(): void
    {
        $this->install(new AssistedInjectModule());
        $this->bind(MethodInvocation::class)->toProvider(MethodInvocationProvider::class)->in(Scope::SINGLETON);
        $this->bind(MethodInvocationProvider::class)->in(Scope::SINGLETON);
    }
}


================================================
FILE: src/di/Bind.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\MethodInterceptor;
use Ray\Aop\ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use Stringable;

use function assert;
use function class_exists;
use function interface_exists;

/**
 * @psalm-import-type BindableInterface from Types
 * @psalm-import-type BindingName from Types
 * @psalm-import-type ParameterNameMapping from Types
 */
final class Bind implements Stringable
{
    private string $name = Name::ANY;
    private DependencyInterface $bound;
    private readonly BindValidator $validate;

    /** @var ?Untarget */
    private $untarget;

    /**
     * @param Container         $container dependency container
     * @param BindableInterface $interface interface or concrete class name
     * @phpstan-param class-string<MethodInterceptor>|string $interface
     */
    public function __construct(
        private readonly Container $container,
        private readonly string $interface
    ) {
        $this->validate = new BindValidator();
        $bindUntarget = class_exists($this->interface) && ! (new \ReflectionClass($this->interface))->isAbstract() && ! $this->isRegistered($this->interface);
        $this->bound = new NullDependency();
        if ($bindUntarget) {
            /** @var class-string $interface */
            $interface = $this->interface;
            $this->untarget = new Untarget($interface);

            return;
        }

        $this->validate->constructor($this->interface);
    }

    public function __destruct()
    {
        if ($this->untarget) {
            ($this->untarget)($this->container, $this);
            $this->untarget = null;
        }
    }

    /**
     * @return non-empty-string
     */
    public function __toString(): string
    {
        return $this->interface . '-' . $this->name;
    }

    /**
     * Set dependency name
     *
     * @param BindingName $name
     */
    public function annotatedWith(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Bind to class
     *
     * @param class-string $class
     */
    public function to(string $class): self
    {
        $this->untarget = null;
        $refClass = $this->validate->to($this->interface, $class);
        $this->bound = (new DependencyFactory())->newAnnotatedDependency($refClass);
        $this->container->add($this);

        return $this;
    }

    /**
     * Bind to constructor
     *
     * @param class-string<T>             $class class name
     * @param ParameterNameMapping|string $name  "varName=bindName,..." or [$varName => $bindName, $varName => $bindName...]
     *
     * @throws ReflectionException
     *
     * @template T of object
     */
    public function toConstructor(string $class, string|array $name, ?InjectionPoints $injectionPoints = null, ?string $postConstruct = null): self
    {
        $this->untarget = null;
        $postConstructRef = $postConstruct !== null ? new ReflectionMethod($class, $postConstruct) : null;
        /** @var ReflectionClass<object> $reflection */
        $reflection = new ReflectionClass($class);
        $this->bound = (new DependencyFactory())->newToConstructor($reflection, $name, $injectionPoints, $postConstructRef);
        $this->container->add($this);

        return $this;
    }

    /**
     * Bind to provider
     *
     * @phpstan-param class-string $provider
     */
    public function toProvider(string $provider, string $context = ''): self
    {
        $this->untarget = null;
        $refClass = $this->validate->toProvider($provider);
        $this->bound = (new DependencyFactory())->newProvider($refClass, $context);
        $this->container->add($this);

        return $this;
    }

    /**
     * Bind to instance
     *
     * @param mixed $instance
     */
    public function toInstance($instance): self
    {
        $this->untarget = null;
        $this->bound = new Instance($instance);
        $this->container->add($this);

        return $this;
    }

    /**
     * Bind to NullObject
     */
    public function toNull(): self
    {
        $this->untarget = null;
        assert(interface_exists($this->interface));
        $this->bound = new NullObjectDependency($this->interface);
        $this->container->add($this);

        return $this;
    }

    /**
     * Set scope
     */
    public function in(string $scope): self
    {
        if ($this->bound instanceof Dependency || $this->bound instanceof DependencyProvider || $this->bound instanceof NullDependency) {
            $this->bound->setScope($scope);
        }

        if ($this->untarget) {
            $this->untarget->setScope($scope);
        }

        return $this;
    }

    public function getBound(): DependencyInterface
    {
        return $this->bound;
    }

    public function setBound(DependencyInterface $bound): void
    {
        $this->bound = $bound;
    }

    private function isRegistered(string $interface): bool
    {
        return isset($this->container->getContainer()[$interface . '-' . Name::ANY]);
    }
}


================================================
FILE: src/di/BindValidator.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\MethodInterceptor;
use Ray\Aop\NullInterceptor;
use Ray\Aop\ReflectionClass;
use Ray\Di\Exception\InvalidProvider;
use Ray\Di\Exception\InvalidType;
use Ray\Di\Exception\NotFound;

use function class_exists;
use function interface_exists;
use function sprintf;

final class BindValidator
{
    public function constructor(string $interface): void
    {
        if ($interface && ! interface_exists($interface) && ! class_exists($interface)) {
            throw new NotFound($interface);
        }
    }

    /**
     * To validator
     *
     * @param class-string<T> $class
     *
     * @return ReflectionClass<T>
     *
     * @template T of object
     */
    public function to(string $interface, string $class): ReflectionClass
    {
        if (! class_exists($class)) {
            throw new NotFound($class);
        }

        if (! $this->isNullInterceptorBinding($class, $interface) && interface_exists($interface) && ! (new ReflectionClass($class))->implementsInterface($interface)) {
            throw new InvalidType(sprintf('[%s] is no implemented [%s] interface', $class, $interface));
        }

        return new ReflectionClass($class);
    }

    /**
     * toProvider validator
     *
     * @phpstan-param class-string $provider
     *
     * @psalm-return ReflectionClass
     * @phpstan-return ReflectionClass<object>
     *
     * @throws NotFound
     */
    public function toProvider(string $provider): ReflectionClass
    {
        if (! class_exists($provider)) {
            /** @psalm-suppress MixedArgument -- should be string */
            throw new NotFound($provider);
        }

        $reflectioClass = new ReflectionClass($provider);
        if (! $reflectioClass->implementsInterface(ProviderInterface::class)) {
            throw new InvalidProvider($provider);
        }

        return $reflectioClass;
    }

    private function isNullInterceptorBinding(string $class, string $interface): bool
    {
        return $class === NullInterceptor::class && interface_exists($interface) && (new ReflectionClass($interface))->implementsInterface(MethodInterceptor::class);
    }
}


================================================
FILE: src/di/BuiltinModule.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Di\MultiBinding\MultiBindingModule;

final class BuiltinModule
{
    public function __invoke(AbstractModule $module): AbstractModule
    {
        $module->install(new AssistedModule());
        $module->install(new ProviderSetModule());
        $module->install(new MultiBindingModule());

        return $module;
    }
}


================================================
FILE: src/di/CompileNullObject.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

/**
 * Convert NullObjectDependency to Dependency
 */
final class CompileNullObject
{
    public function __invoke(Container $container, string $scriptDir): void
    {
        $container->map(static function (DependencyInterface $dependency) use ($scriptDir): Dependency|DependencyInterface {
            if ($dependency instanceof NullObjectDependency) {
                return $dependency->toNull($scriptDir);
            }

            return $dependency;
        });
    }
}


================================================
FILE: src/di/Container.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use BadMethodCallException;
use Ray\Aop\Compiler;
use Ray\Aop\CompilerInterface;
use Ray\Aop\Pointcut;
use Ray\Di\Exception\NoHint;
use Ray\Di\Exception\Unbound;
use Ray\Di\Exception\Untargeted;
use Ray\Di\MultiBinding\MultiBindings;
use ReflectionClass;

use function array_merge;
use function class_exists;
use function explode;
use function ksort;
use function sprintf;

/**
 * @psalm-import-type DependencyContainer from Types
 * @psalm-import-type PointcutList from Types
 * @psalm-import-type DependencyIndex from Types
 * @psalm-import-type MethodArguments from Types
 * @psalm-import-type InjectableValue from Types
 */
final class Container implements InjectorInterface
{
    /** @var MultiBindings */
    public $multiBindings;

    /** @var DependencyContainer */
    private array $container = [];

    /** @var array<int, Pointcut> */
    private array $pointcuts = [];

    public function __construct()
    {
        $this->multiBindings = new MultiBindings();
    }

    /**
     * @return list<string>
     */
    public function __sleep()
    {
        return ['container', 'pointcuts', 'multiBindings'];
    }

    /**
     * Add binding to a container
     */
    public function add(Bind $bind): void
    {
        $dependency = $bind->getBound();
        $dependency->register($this->container, $bind);
    }

    /**
     * Add Pointcut to container
     */
    public function addPointcut(Pointcut $pointcut): void
    {
        $this->pointcuts[] = $pointcut;
    }

    /**
     * {@inheritDoc}
     *
     * @param ''|class-string<T> $interface
     * @param string             $name
     *
     * @return ($interface is '' ? mixed : T)
     *
     * @template T of object
     */
    public function getInstance($interface, $name = Name::ANY)
    {
        /** @psalm-suppress MixedReturnStatement */
        return $this->getDependency($interface . '-' . $name);
    }

    /**
     * Return dependency injected instance
     *
     * @param MethodArguments $params
     *
     * @return mixed
     *
     * @throws Unbound
     */
    public function getInstanceWithArgs(string $interface, array $params)
    {
        $index = $interface . '-';
        if (! isset($this->container[$index])) {
            throw $this->unbound($index);
        }

        $dependency = $this->container[$index];
        if (! $dependency instanceof Dependency) {
            throw new BadMethodCallException($interface);
        }

        return $dependency->injectWithArgs($this, $params);
    }

    /**
     * Return dependency injected instance
     *
     * @param DependencyIndex $index
     *
     * @return mixed
     *
     * @throws Unbound
     */
    public function getDependency(string $index)
    {
        if (! isset($this->container[$index])) {
            throw $this->unbound($index);
        }

        return $this->container[$index]->inject($this);
    }

    /**
     * Rename existing dependency interface + name
     */
    public function move(string $sourceInterface, string $sourceName, string $targetInterface, string $targetName): void
    {
        $sourceIndex = $sourceInterface . '-' . $sourceName;
        if (! isset($this->container[$sourceIndex])) {
            throw $this->unbound($sourceIndex);
        }

        $targetIndex = $targetInterface . '-' . $targetName;
        $this->container[$targetIndex] = $this->container[$sourceIndex];
        unset($this->container[$sourceIndex]);
    }

    /**
     * Return Unbound exception
     *
     * @param DependencyIndex $index {interface}-{bind name}
     */
    public function unbound(string $index): Untargeted|Unbound
    {
        [$class, $name] = explode('-', $index);
        if (class_exists($class) && ! (new ReflectionClass($class))->isAbstract()) {
            return new Untargeted($class);
        }

        if ($class === '' && $name === '') {
            return new NoHint();
        }

        return new Unbound(sprintf("'%s-%s'", $class, $name));
    }

    /**
     * Return container
     *
     * @return array<non-empty-string, DependencyInterface>
     * @psalm-return DependencyContainer
     */
    public function getContainer(): array
    {
        return $this->container;
    }

    /**
     * Return pointcuts
     *
     * @return array<int, Pointcut>
     * @psalm-return PointcutList
     */
    public function getPointcuts(): array
    {
        return $this->pointcuts;
    }

    /**
     * Merge container
     */
    public function merge(self $container): void
    {
        $this->multiBindings->merge($container->multiBindings);
        $this->container += $container->getContainer();
        $this->pointcuts = array_merge($this->pointcuts, $container->getPointcuts());
    }

    /**
     * Weave aspects to all dependency in container
     */
    public function weaveAspects(CompilerInterface $compiler): void
    {
        if ($this->pointcuts === []) {
            return;
        }

        foreach ($this->container as $dependency) {
            if ($dependency instanceof Dependency) {
                $dependency->weaveAspects($compiler, $this->pointcuts);
            }
        }
    }

    /**
     * Weave aspect to single dependency
     */
    public function weaveAspect(Compiler $compiler, Dependency $dependency): self
    {
        $dependency->weaveAspects($compiler, $this->pointcuts);

        return $this;
    }

    /**
     * @param callable(DependencyInterface, string): DependencyInterface $f
     */
    public function map(callable $f): void
    {
        foreach ($this->container as $key => &$index) {
            $index = $f($index, $key);
        }
    }

    public function sort(): void
    {
        ksort($this->container);
    }
}


================================================
FILE: src/di/ContainerFactory.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\Compiler;

use function array_shift;

/**
 * @psalm-import-type ModuleList from Types
 * @psalm-import-type ScriptDir from Types
 */
final class ContainerFactory
{
    /**
     * @param AbstractModule|ModuleList|null $module   Module(s)
     * @param ScriptDir                      $classDir
     */
    public function __invoke($module, string $classDir): Container
    {
        $oneModule = $this->getModule($module);
        // install built-in module
        $appModule = (new BuiltinModule())($oneModule);
        $container = $appModule->getContainer();
        // Compile null objects
        (new CompileNullObject())($container, $classDir);
        // Compile aspects
        /** @psalm-suppress InvalidArgument */
        $container->weaveAspects(new Compiler($classDir));

        return $container;
    }

    /**
     * @param AbstractModule|ModuleList|null $module Module(s)
     */
    private function getModule($module): AbstractModule
    {
        if ($module instanceof AbstractModule) {
            return $module;
        }

        if ($module === null) {
            return new NullModule();
        }

        $modules = $module;
        $oneModule = array_shift($modules);
        foreach ($modules as $module) {
            $oneModule->install($module);
        }

        return $oneModule;
    }
}


================================================
FILE: src/di/Dependency.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\Bind as AopBind;
use Ray\Aop\CompilerInterface;
use Ray\Aop\MethodInterceptor;
use Ray\Aop\WeavedInterface;
use ReflectionClass;
use ReflectionMethod;

use function assert;
use function method_exists;
use function sprintf;

/**
 * @psalm-import-type MethodArguments from Types
 * @psalm-import-type PointcutList from Types
 */
final class Dependency implements DependencyInterface, AcceptInterface
{
    /** @var NewInstance */
    private $newInstance;

    /** @var ?string */
    private $postConstruct;
    private bool $isSingleton = false;

    /** @var ?mixed */
    private $instance;

    public function __construct(NewInstance $newInstance, ?ReflectionMethod $postConstruct = null)
    {
        $this->newInstance = $newInstance;
        $this->postConstruct = $postConstruct->name ?? null;
    }

    /**
     * @return array<string>
     */
    public function __sleep()
    {
        return ['newInstance', 'postConstruct', 'isSingleton'];
    }

    public function __toString(): string
    {
        return sprintf(
            '(dependency) %s',
            (string) $this->newInstance
        );
    }

    /**
     * {@inheritdoc}
     */
    public function register(array &$container, Bind $bind): void
    {
        $container[(string) $bind] = $bind->getBound();
    }

    /**
     * {@inheritdoc}
     */
    public function inject(Container $container)
    {
        // singleton ?
        if ($this->isSingleton === true && $this->instance !== null) {
            return $this->instance;
        }

        // create dependency injected instance
        $this->instance = ($this->newInstance)($container);

        // @PostConstruct
        if ($this->postConstruct !== null) {
            assert(method_exists($this->instance, $this->postConstruct));
            $this->instance->{$this->postConstruct}();
        }

        return $this->instance;
    }

    /**
     * @param MethodArguments $params
     *
     * @return mixed
     */
    public function injectWithArgs(Container $container, array $params)
    {
        // singleton ?
        if ($this->isSingleton === true && $this->instance !== null) {
            return $this->instance;
        }

        // create dependency injected instance
        $this->instance = $this->newInstance->newInstanceArgs($container, $params);

        // @PostConstruct
        if ($this->postConstruct !== null) {
            assert(method_exists($this->instance, $this->postConstruct));
            $this->instance->{$this->postConstruct}();
        }

        return $this->instance;
    }

    /**
     * {@inheritdoc}
     */
    public function setScope($scope): void
    {
        if ($scope === Scope::SINGLETON) {
            $this->isSingleton = true;
        }
    }

    /**
     * @param PointcutList $pointcuts
     */
    public function weaveAspects(CompilerInterface $compiler, array $pointcuts): void
    {
        $class = (string) $this->newInstance;
        if ((new ReflectionClass($class))->isFinal()) {
            return;
        }

        $isInterceptor = (new ReflectionClass($class))->implementsInterface(MethodInterceptor::class);
        $isWeaved = (new ReflectionClass($class))->implementsInterface(WeavedInterface::class);
        if ($isInterceptor || $isWeaved) {
            return;
        }

        $bind = new AopBind();
        $className = (string) $this->newInstance;
        $bind->bind($className, $pointcuts);
        if (! $bind->getBindings()) {
            return;
        }

        $class = $compiler->compile($className, $bind);
        $this->newInstance->weaveAspects($class, $bind);
    }

    /** @inheritDoc */
    public function accept(VisitorInterface $visitor)
    {
        return $visitor->visitDependency(
            $this->newInstance,
            $this->postConstruct,
            $this->isSingleton
        );
    }

    public function isSingleton(): bool
    {
        return $this->isSingleton;
    }
}


================================================
FILE: src/di/DependencyFactory.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\ReflectionClass;
use ReflectionMethod;

use function assert;
use function class_exists;

final class DependencyFactory
{
    /**
     * Create dependency object
     *
     * @param ReflectionClass<object> $class
     */
    public function newAnnotatedDependency(ReflectionClass $class): Dependency
    {
        $annotateClass = new AnnotatedClass();
        $newInstance = $annotateClass->getNewInstance($class);
        $postConstruct = $annotateClass->getPostConstruct($class);

        return new Dependency($newInstance, $postConstruct);
    }

    /**
     * Create Provider binding
     *
     * @param ReflectionClass<object> $provider
     */
    public function newProvider(ReflectionClass $provider, string $context): DependencyProvider
    {
        $dependency = $this->newAnnotatedDependency($provider);

        return new DependencyProvider($dependency, $context);
    }

    /**
     * Create ToConstructor binding
     *
     * @param ReflectionClass<object>      $class
     * @param string|array<string, string> $name
     */
    public function newToConstructor(
        ReflectionClass $class,
        string|array $name,
        ?InjectionPoints $injectionPoints = null,
        ?ReflectionMethod $postConstruct = null
    ): Dependency {
        assert(class_exists($class->name));
        $setterMethods = $injectionPoints instanceof InjectionPoints ? $injectionPoints($class->name) : new SetterMethods([]);
        $newInstance = new NewInstance($class, $setterMethods, new Name($name));

        return new Dependency($newInstance, $postConstruct);
    }
}


================================================
FILE: src/di/DependencyInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

/**
 * @psalm-import-type DependencyContainer from Types
 * @psalm-import-type ScopeType from Types
 * @psalm-import-type InjectableValue from Types
 */
interface DependencyInterface
{
    /**
     * @return string
     */
    public function __toString();

    /**
     * Inject dependencies into dependent objects
     *
     * @return mixed
     */
    public function inject(Container $container);

    /**
     * Register dependency to container
     *
     * @param DependencyContainer $container
     *
     * @return void
     *
     * @param-out DependencyContainer $container
     */
    public function register(array &$container, Bind $bind);

    /**
     * Set scope
     *
     * @param string $scope
     *
     * @return void
     */
    public function setScope($scope);
}


================================================
FILE: src/di/DependencyProvider.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use function assert;
use function sprintf;

final class DependencyProvider implements DependencyInterface, AcceptInterface
{
    private bool $isSingleton = false;

    /** @var ?mixed */
    private $instance;

    public function __construct(
        /**
         * Provider dependency
         */
        private readonly Dependency $dependency,
        public string $context
    ) {
    }

    /**
     * @return list<string>
     */
    public function __sleep()
    {
        return ['context', 'dependency', 'isSingleton'];
    }

    public function __toString(): string
    {
        return sprintf(
            '(provider) %s',
            (string) $this->dependency
        );
    }

    /**
     * {@inheritdoc}
     */
    public function register(array &$container, Bind $bind): void
    {
        $container[(string) $bind] = $bind->getBound();
    }

    /**
     * {@inheritdoc}
     */
    public function inject(Container $container)
    {
        if ($this->isSingleton && $this->instance !== null) {
            return $this->instance;
        }

        $provider = $this->dependency->inject($container);
        assert($provider instanceof ProviderInterface);
        if ($provider instanceof SetContextInterface) {
            $this->setContext($provider);
        }

        $this->instance = $provider->get();

        return $this->instance;
    }

    /**
     * {@inheritdoc}
     */
    public function setScope($scope): void
    {
        if ($scope === Scope::SINGLETON) {
            $this->isSingleton = true;
        }
    }

    public function setContext(SetContextInterface $provider): void
    {
        $provider->setContext($this->context);
    }

    public function isSingleton(): bool
    {
        return $this->isSingleton;
    }

    /** @inheritDoc */
    public function accept(VisitorInterface $visitor)
    {
        return $visitor->visitProvider(
            $this->dependency,
            $this->context,
            $this->isSingleton
        );
    }
}


================================================
FILE: src/di/Di/Assisted.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Di;

use Attribute;

/**
 * Marks a parameter for assisted injection
 *
 * When applied to a method parameter, this indicates that the parameter
 * should be automatically injected by the DI container during method invocation.
 */
#[Attribute(Attribute::TARGET_PARAMETER)]
final class Assisted
{
}


================================================
FILE: src/di/Di/Inject.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Di;

use Attribute;

/**
 * Annotates your class methods into which the Injector should inject values
 *
 * @psalm-immutable
 */
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER)]
final class Inject implements InjectInterface
{
    /**
     * @SuppressWarnings("PHPMD.BooleanArgumentFlag")
     */
    public function __construct(
        /**
         * If true, and the appropriate binding is not found, the Injector will skip injection of this method or field rather than produce an error.
         */
        public bool $optional = false
    ) {
    }

    /**
     * {@inheritdoc}
     */
    public function isOptional(): bool
    {
        return $this->optional;
    }
}


================================================
FILE: src/di/Di/InjectInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Di;

interface InjectInterface
{
    /**
     * Whether or not to use optional injection
     *
     * @return bool
     */
    public function isOptional();
}


================================================
FILE: src/di/Di/Named.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Di;

use Attribute;

/**
 * Annotates named things
 *
 * @psalm-immutable
 */
#[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY)]
final class Named
{
    public function __construct(public string $value)
    {
    }
}


================================================
FILE: src/di/Di/PostConstruct.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Di;

use Attribute;

/**
 * PostConstruct
 *
 * The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to
 * perform any initialization. This method MUST be invoked before the class is put into service. The method annotated
 * with PostConstruct MUST be invoked even if the class does not request any resources to be injected. Only one method
 * can be annotated with this annotation. The method on which the PostConstruct annotation is applied MUST fulfill
 * all of the following criteria
 *
 *  - The method MUST NOT have any parameters.
 *  - The return type of the method MUST be void.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class PostConstruct
{
}


================================================
FILE: src/di/Di/Qualifier.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Di;

use Attribute;

/**
 * Identifies qualifier annotations
 */
#[Attribute(Attribute::TARGET_CLASS)]
final class Qualifier
{
}


================================================
FILE: src/di/Di/Set.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Di;

use Attribute;

/**
 * @template T of object
 * @psalm-immutable
 */
#[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_PROPERTY)]
final class Set
{
    /**
     * @param ''|class-string<T> $interface
     */
    public function __construct(public string $interface, public string $name = '')
    {
    }
}


================================================
FILE: src/di/Exception/DirectoryNotWritable.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

use RuntimeException;

final class DirectoryNotWritable extends RuntimeException implements ExceptionInterface
{
}


================================================
FILE: src/di/Exception/ExceptionInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

interface ExceptionInterface
{
}


================================================
FILE: src/di/Exception/InvalidContext.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

use InvalidArgumentException;

final class InvalidContext extends InvalidArgumentException implements ExceptionInterface
{
}


================================================
FILE: src/di/Exception/InvalidProvider.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

use InvalidArgumentException;

final class InvalidProvider extends InvalidArgumentException implements ExceptionInterface
{
}


================================================
FILE: src/di/Exception/InvalidToConstructorNameParameter.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

use InvalidArgumentException;

/**
 * @see https://github.com/ray-di/Ray.Di#constructor-bindings
 */
final class InvalidToConstructorNameParameter extends InvalidArgumentException implements ExceptionInterface
{
}


================================================
FILE: src/di/Exception/InvalidType.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

use InvalidArgumentException;

final class InvalidType extends InvalidArgumentException implements ExceptionInterface
{
}


================================================
FILE: src/di/Exception/MethodInvocationNotAvailable.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

final class MethodInvocationNotAvailable extends Unbound
{
}


================================================
FILE: src/di/Exception/NoHint.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

/**
 * Exception thrown when a parameter has no type hint and no binding name
 *
 * Message format: ${var} (file:line)
 *
 * This occurs when Ray.Di cannot identify what to inject because
 * the parameter has neither a class/interface type hint nor a #[Named] or #[Qualifier] attribute.
 */
final class NoHint extends Unbound
{
}


================================================
FILE: src/di/Exception/NotFound.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

use LogicException;

final class NotFound extends LogicException implements ExceptionInterface
{
}


================================================
FILE: src/di/Exception/SetNotFound.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

use LogicException;

final class SetNotFound extends LogicException implements ExceptionInterface
{
}


================================================
FILE: src/di/Exception/Unbound.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

use Exception;
use LogicException;

use function array_pop;
use function array_reverse;
use function implode;
use function sprintf;

/**
 * Exception thrown when a binding is not found
 *
 * Message format: '{type}-{name}' in file:line ($var)
 *
 * Examples:
 * - "'FooInterface-' in file.php:10 ($foo)" - FooInterface with no name
 * - "'FooInterface-db' in file.php:10 ($foo)" - FooInterface with name 'db'
 * - "'-db' in file.php:10 ($foo)" - no type with name 'db'
 */
class Unbound extends LogicException implements ExceptionInterface
{
    public function __toString(): string
    {
        $messages = [sprintf("- %s\n", $this->getMessage())];
        $e = $this->getPrevious();
        if (! $e instanceof Exception) {
            return $this->getMainMessage($this);
        }

        if ($e instanceof self) {
            return $this->buildMessage($e, $messages) . "\n" . $e->getTraceAsString();
        }

        return parent::__toString();
    }

    /**
     * @param array<int, string> $msg
     */
    private function buildMessage(self $e, array $msg): string
    {
        $lastE = $e;
        while ($e instanceof self) {
            $msg[] = sprintf("- %s\n", $e->getMessage());
            $lastE = $e;
            $e = $e->getPrevious();
        }

        array_pop($msg);
        $msg = array_reverse($msg);

        return $this->getMainMessage($lastE) . implode('', $msg);
    }

    /**
     * @psalm-pure
     */
    private function getMainMessage(self $e): string
    {
        return sprintf(
            "exception '%s' with message '%s'\n",
            $e::class,
            $e->getMessage()
        );
    }
}


================================================
FILE: src/di/Exception/Untargeted.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Exception;

final class Untargeted extends Unbound
{
}


================================================
FILE: src/di/Exception.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

final class Exception extends \Exception
{
}


================================================
FILE: src/di/Grapher.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\Compiler;

use function file_exists;
use function spl_autoload_register;
use function sprintf;
use function str_replace;

/**
 * @psalm-import-type MethodArguments from Types
 * @psalm-import-type ScriptDir from Types
 */
final class Grapher
{
    private Container $container;

    /**
     * @param AbstractModule $module   Binding module
     * @param ScriptDir      $classDir Class directory
     */
    public function __construct(AbstractModule $module, private string $classDir)
    {
        $module->install(new AssistedModule());
        $this->container = $module->getContainer();
        /** @psalm-suppress InvalidArgument */
        $this->container->weaveAspects(new Compiler($this->classDir));

        // builtin injection
        (new Bind($this->container, InjectorInterface::class))->toInstance(new Injector($module));
    }

    /**
     * Wakeup
     */
    public function __wakeup()
    {
        spl_autoload_register(
            function (string $class): void {
                $file = sprintf('%s/%s.php', $this->classDir, str_replace('\\', '_', $class));
                if (file_exists($file)) {
                    include $file; //@codeCoverageIgnore
                }
            }
        );
    }

    /**
     * Build an object graph with give constructor parameters
     *
     * @param string      $class  class name
     * @param list<mixed> $params construct parameters (MethodArguments)
     *
     * @return mixed
     */
    public function newInstanceArgs(string $class, array $params)
    {
        return $this->container->getInstanceWithArgs($class, $params);
    }
}


================================================
FILE: src/di/InjectableInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

interface InjectableInterface
{
}


================================================
FILE: src/di/InjectionPoint.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\ReflectionClass;
use Ray\Aop\ReflectionMethod;
use Ray\Di\Di\Qualifier;
use ReflectionParameter;

use function assert;
use function class_exists;

final class InjectionPoint implements InjectionPointInterface
{
    private string $pClass;

    /** @var string */
    private $pFunction;

    /** @var string */
    private $pName;

    public function __construct(private ReflectionParameter $parameter)
    {
        $this->pFunction = $this->parameter->getDeclaringFunction()->name;
        $class = $this->parameter->getDeclaringClass();
        $this->pClass = $class instanceof ReflectionClass ? $class->name : '';
        $this->pName = $this->parameter->name;
    }

    /**
     * {@inheritdoc}
     */
    public function getParameter(): ReflectionParameter
    {
        return $this->parameter;
    }

    /**
     * {@inheritdoc}
     */
    public function getMethod(): ReflectionMethod
    {
        $this->parameter = $this->getParameter();
        $class = $this->parameter->getDeclaringClass();
        $method = $this->parameter->getDeclaringFunction()->getShortName();
        assert($class instanceof \ReflectionClass);
        assert(class_exists($class->getName()));

        return new ReflectionMethod($class->getName(), $method);
    }

    /**
     * {@inheritdoc}
     */
    public function getClass(): ReflectionClass
    {
        $this->parameter = $this->getParameter();
        $class = $this->parameter->getDeclaringClass();
        assert($class instanceof \ReflectionClass);

        return new ReflectionClass($class->getName());
    }

    /**
     * {@inheritdoc}
     */
    public function getQualifiers(): array
    {
        $qualifiers = [];
        $annotations = $this->getMethod()->getAnnotations();
        foreach ($annotations as $annotation) {
            $maybeQualifier = (new ReflectionClass($annotation))->getAnnotation(Qualifier::class);
            if ($maybeQualifier instanceof Qualifier) {
                $qualifiers[] = $annotation;
            }
        }

        return $qualifiers;
    }

    /**
     * @return array<string>
     */
    public function __serialize(): array
    {
        return [$this->pClass, $this->pFunction, $this->pName];
    }

    /**
     * @param array<string> $array
     */
    public function __unserialize(array $array): void
    {
        [$this->pClass, $this->pFunction, $this->pName] = $array;
    }
}


================================================
FILE: src/di/InjectionPointInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\ReflectionClass;
use Ray\Aop\ReflectionMethod;
use ReflectionParameter;

/**
 * @psalm-import-type QualifierList from Types
 */
interface InjectionPointInterface
{
    /**
     * Return parameter reflection
     */
    public function getParameter(): ReflectionParameter;

    /**
     * Return method reflection
     */
    public function getMethod(): ReflectionMethod;

    /**
     * Return class reflection
     *
     * @psalm-return ReflectionClass
     * @phpstan-return ReflectionClass<object>
     */
    public function getClass(): ReflectionClass;

    /**
     * Return Qualifier annotations
     *
     * @return QualifierList
     */
    public function getQualifiers(): array;
}


================================================
FILE: src/di/InjectionPoints.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use ReflectionException;
use ReflectionMethod;

/**
 * @psalm-import-type InjectionPointDefinition from Types
 * @psalm-import-type InjectionPointsList from Types
 */
final class InjectionPoints
{
    /**
     * Injection points
     *
     * @var InjectionPointsList
     */
    private array $points = [];

    /**
     * @param class-string $class
     *
     * @throws ReflectionException
     */
    public function __invoke(string $class): SetterMethods
    {
        $points = [];
        foreach ($this->points as $point) {
            $points[] = $this->getSetterMethod($class, $point);
        }

        return new SetterMethods($points);
    }

    public function addMethod(string $method, string $name = Name::ANY): self
    {
        $this->points[] = [$method, $name, false];

        return $this;
    }

    public function addOptionalMethod(string $method, string $name = Name::ANY): self
    {
        $this->points[] = [$method, $name, true];

        return $this;
    }

    /**
     * @param class-string             $class
     * @param InjectionPointDefinition $point
     *
     * @throws ReflectionException
     */
    private function getSetterMethod(string $class, array $point): SetterMethod
    {
        $setterMethod = new SetterMethod(new ReflectionMethod($class, $point[0]), new Name($point[1]));
        if ($point[2]) {
            $setterMethod->setOptional();
        }

        return $setterMethod;
    }
}


================================================
FILE: src/di/Injector.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\Compiler;
use Ray\Di\Exception\DirectoryNotWritable;
use Ray\Di\Exception\Untargeted;

use function assert;
use function file_exists;
use function is_dir;
use function is_writable;
use function spl_autoload_register;
use function sprintf;
use function str_replace;
use function sys_get_temp_dir;

/**
 * @psalm-import-type BindableInterface from Types
 * @psalm-import-type ModuleList from Types
 * @psalm-import-type ScriptDir from Types
 */
final class Injector implements InjectorInterface
{
    /** @var ScriptDir */
    private readonly string $classDir;
    private readonly Container $container;

    /**
     * @param AbstractModule|ModuleList|null $module Module(s)
     * @param string                         $tmpDir Temp directory for generated class
     */
    public function __construct($module = null, string $tmpDir = '')
    {
        /** @var ScriptDir $classDir */
        $classDir = is_dir($tmpDir) ? $tmpDir : sys_get_temp_dir();
        if (! is_writable($classDir)) {
            throw new DirectoryNotWritable($classDir); // @codeCoverageIgnore
        }

        $this->classDir = $classDir;
        $this->container = (new ContainerFactory())($module, $this->classDir);
        // Bind injector (built-in bindings)
        (new Bind($this->container, InjectorInterface::class))->toInstance($this);
        $this->container->sort();
    }

    /**
     * Wakeup
     */
    public function __wakeup()
    {
        spl_autoload_register(
            function (string $class): void {
                $file = sprintf('%s/%s.php', $this->classDir, str_replace('\\', '_', $class));
                if (file_exists($file)) {
                    include $file; //@codeCoverageIgnore
                }
            }
        );
    }

    /**
     * {@inheritDoc}
     */
    public function getInstance($interface, $name = Name::ANY)
    {
        try {
            /** @psalm-suppress MixedAssignment */
            $instance = $this->container->getInstance($interface, $name);
        } catch (Untargeted) {
            /** @psalm-var class-string $interface */
            $this->bind($interface);
            /** @psalm-suppress MixedAssignment */
            $instance = $this->getInstance($interface, $name);
        }

        /** @psalm-suppress MixedReturnStatement */
        return $instance;
    }

    /**
     * @param BindableInterface $class
     */
    private function bind(string $class): void
    {
        new Bind($this->container, $class);
        $bound = $this->container->getContainer()[$class . '-' . Name::ANY];
        assert($bound instanceof Dependency);
        /** @psalm-suppress InvalidArgument */
        $this->container->weaveAspect(new Compiler($this->classDir), $bound)->getInstance($class);
    }
}


================================================
FILE: src/di/InjectorInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

/**
 * Builds the graphs of objects that make up your application
 *
 * The injector tracks the dependencies for each type and uses bindings to inject them.
 * This is the core of Ray.Di, although you rarely interact with it directly.
 * This "behind-the-scenes" operation is what distinguishes dependency injection from its cousin, the service locator pattern.
 */
interface InjectorInterface
{
    /**
     * Return object graph
     *
     * @param ''|class-string<T> $interface
     * @param string             $name
     *
     * @return ($interface is '' ? mixed : T)
     *
     * @template T of object
     */
    public function getInstance($interface, $name = Name::ANY);
}


================================================
FILE: src/di/Instance.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use function gettype;
use function is_object;
use function is_scalar;
use function sprintf;

final class Instance implements DependencyInterface, AcceptInterface
{
    /**
     * @param mixed $value
     */
    public function __construct(public $value)
    {
    }

    public function __toString(): string
    {
        if (is_scalar($this->value)) {
            return sprintf(
                '(%s) %s',
                gettype($this->value),
                (string) $this->value
            );
        }

        if (is_object($this->value)) {
            return '(object) ' . $this->value::class;
        }

        return '(' . gettype($this->value) . ')';
    }

    /**
     * {@inheritdoc}
     */
    public function register(array &$container, Bind $bind): void
    {
        $index = (string) $bind;
        $container[$index] = $bind->getBound();
    }

    /**
     * {@inheritdoc}
     */
    public function inject(Container $container)
    {
        return $this->value;
    }

    /**
     * {@inheritdoc}
     *
     * @codeCoverageIgnore
     */
    public function setScope($scope): void
    {
    }

    /** @inheritDoc */
    public function accept(VisitorInterface $visitor)
    {
        return $visitor->visitInstance($this->value);
    }
}


================================================
FILE: src/di/Matcher/AssistedInjectMatcher.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Matcher;

use LogicException;
use Ray\Aop\AbstractMatcher;
use Ray\Di\Di\Assisted;
use Ray\Di\Di\InjectInterface;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;

final class AssistedInjectMatcher extends AbstractMatcher
{
    /**
     * {@inheritdoc}
     *
     * @codeCoverageIgnore
     */
    public function matchesClass(ReflectionClass $class, array $arguments): bool
    {
        throw new LogicException('Should not used in class matcher');
    }

    /**
     * {@inheritdoc}
     */
    public function matchesMethod(ReflectionMethod $method, array $arguments): bool
    {
        $params = $method->getParameters();
        foreach ($params as $param) {
            /** @var list<ReflectionAttribute> $attributes */
            $attributes = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF);
            if (isset($attributes[0])) {
                return true;
            }

            /** @var list<ReflectionAttribute> $assisted */
            $assisted = $param->getAttributes(Assisted::class);
            if (isset($assisted[0])) {
                return true;
            }
        }

        return false;
    }
}


================================================
FILE: src/di/MethodInvocationProvider.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\MethodInvocation;
use Ray\Di\Exception\MethodInvocationNotAvailable;

/**
 * @implements ProviderInterface<MethodInvocation>
 */
final class MethodInvocationProvider implements ProviderInterface
{
    /** @var ?MethodInvocation<object> */
    private ?MethodInvocation $invocation = null;

    /**
     * @param MethodInvocation<object> $invocation
     */
    public function set(MethodInvocation $invocation): void
    {
        $this->invocation = $invocation;
    }

    /**
     * @return MethodInvocation<object>
     */
    public function get(): MethodInvocation
    {
        if ($this->invocation === null) {
            throw new MethodInvocationNotAvailable();
        }

        return $this->invocation;
    }
}


================================================
FILE: src/di/ModuleString.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use function assert;
use function implode;
use function serialize;
use function sort;
use function sprintf;
use function unserialize;

use const PHP_EOL;

/**
 * @psalm-import-type PointcutList from Types
 */
final class ModuleString
{
    /**
     * @param PointcutList $pointcuts
     */
    public function __invoke(Container $container, array $pointcuts): string
    {
        $log = [];
        /** @psalm-suppress MixedAssignment */
        $container = unserialize(serialize($container), ['allowed_classes' => true]);
        assert($container instanceof Container);
        $spy = new SpyCompiler();
        foreach ($container->getContainer() as $dependencyIndex => $dependency) {
            if ($dependency instanceof Dependency) {
                $dependency->weaveAspects($spy, $pointcuts);
            }

            $log[] = sprintf(
                '%s => %s',
                $dependencyIndex,
                (string) $dependency
            );
        }

        sort($log);

        return implode(PHP_EOL, $log);
    }
}


================================================
FILE: src/di/MultiBinder.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Di\MultiBinding\LazyInstance;
use Ray\Di\MultiBinding\LazyInterface;
use Ray\Di\MultiBinding\LazyProvider;
use Ray\Di\MultiBinding\LazyTo;
use Ray\Di\MultiBinding\MultiBindings;

/**
 * @psalm-import-type BindableInterface from Types
 * @psalm-import-type LazyBindingList from Types
 */
final class MultiBinder
{
    private readonly Container $container;

    /** @var MultiBindings */
    private $multiBindings;
    private ?string $key = null;

    private function __construct(AbstractModule $module, private readonly string $interface)
    {
        $this->container = $module->getContainer();
        $this->multiBindings = $this->container->multiBindings;
        $this->container->add(
            (new Bind($this->container, MultiBindings::class))->toInstance($this->multiBindings)
        );
    }

    public static function newInstance(AbstractModule $module, string $interface): self
    {
        return new self($module, $interface);
    }

    public function addBinding(?string $key = null): self
    {
        $this->key = $key;

        return $this;
    }

    public function setBinding(?string $key = null): self
    {
        $this->container->multiBindings->exchangeArray([]);
        $this->key = $key;

        return $this;
    }

    /**
     * @param class-string $class
     */
    public function to(string $class): void
    {
        $this->bind(new LazyTo($class), $this->key);
    }

    /**
     * @param class-string<ProviderInterface<T>> $provider
     *
     * @template T of mixed
     */
    public function toProvider(string $provider): void
    {
        $this->bind(new LazyProvider($provider), $this->key);
    }

    /**
     * @param mixed $instance
     */
    public function toInstance($instance): void
    {
        $this->bind(new LazyInstance($instance), $this->key);
    }

    private function bind(LazyInterface $lazy, ?string $key): void
    {
        $bindings = [];
        if ($this->multiBindings->offsetExists($this->interface)) {
            $bindings = $this->multiBindings->offsetGet($this->interface);
        }

        if ($key === null) {
            $bindings[] = $lazy;
            $this->multiBindings->offsetSet($this->interface, $bindings);

            return;
        }

        $bindings[$key] = $lazy;
        $this->multiBindings->offsetSet($this->interface, $bindings);
    }
}


================================================
FILE: src/di/MultiBinding/LazyInstance.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\MultiBinding;

use Ray\Di\InjectorInterface;

/**
 * @template T of mixed
 * @psalm-immutable
 */
final class LazyInstance implements LazyInterface
{
    /**
     * @param T $instance
     */
    public function __construct(private $instance)
    {
    }

    /**
     * @return T
     */
    public function __invoke(InjectorInterface $injector)
    {
        unset($injector);

        return $this->instance;
    }
}


================================================
FILE: src/di/MultiBinding/LazyInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\MultiBinding;

use Ray\Di\InjectorInterface;

interface LazyInterface
{
    /**
     * @return mixed
     */
    public function __invoke(InjectorInterface $injector);
}


================================================
FILE: src/di/MultiBinding/LazyProvider.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\MultiBinding;

use Ray\Di\InjectorInterface;
use Ray\Di\ProviderInterface;

/**
 * @template T of ProviderInterface
 */
final class LazyProvider implements LazyInterface
{
    /**
     * @param class-string<T> $class
     */
    public function __construct(private string $class)
    {
    }

    /**
     * @return mixed
     */
    public function __invoke(InjectorInterface $injector)
    {
        $provider = $injector->getInstance($this->class);

        return $provider->get();
    }
}


================================================
FILE: src/di/MultiBinding/LazyTo.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\MultiBinding;

use Ray\Di\InjectorInterface;

/**
 * @template T of object
 */
final class LazyTo implements LazyInterface
{
    /**
     * @param class-string<T> $class
     */
    public function __construct(private string $class)
    {
    }

    /**
     * @return T
     */
    public function __invoke(InjectorInterface $injector)
    {
        return $injector->getInstance($this->class);
    }
}


================================================
FILE: src/di/MultiBinding/Map.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\MultiBinding;

use ArrayAccess;
use Countable;
use Generator;
use Iterator;
use IteratorAggregate;
use LogicException;
use Ray\Di\InjectorInterface;
use ReturnTypeWillChange;

use function array_key_exists;
use function count;

/**
 * @template T
 * @implements ArrayAccess<array-key, T>
 * @implements IteratorAggregate<string, mixed>
 */
final class Map implements IteratorAggregate, ArrayAccess, Countable
{
    /**
     * @param array<array-key, LazyInterface> $lazies
     */
    public function __construct(private array $lazies, private readonly InjectorInterface $injector)
    {
    }

    /**
     * @param array-key $offset
     *
     * @codeCoverageIgnore
     */
    #[ReturnTypeWillChange]
    public function offsetExists($offset): bool
    {
        return array_key_exists($offset, $this->lazies);
    }

    /**
     * @param array-key $offset
     *
     * @return T
     *
     * @codeCoverageIgnore
     */
    #[ReturnTypeWillChange]
    public function offsetGet($offset)
    {
        /** @var T $instance */
        $instance = ($this->lazies[$offset])($this->injector);

        return $instance;
    }

    /**
     * @param array-key $offset
     * @param mixed     $value
     *
     * @return never
     *
     * @codeCoverageIgnore
     */
    #[ReturnTypeWillChange]
    public function offsetSet($offset, $value): void
    {
        unset($offset, $value);

        throw new LogicException();
    }

    /**
     * @param array-key $offset
     *
     * @return never
     *
     * @codeCoverageIgnore
     */
    #[ReturnTypeWillChange]
    public function offsetUnset($offset): void
    {
        unset($offset);

        throw new LogicException();
    }

    /** @return Generator<array-key, T, void, void> */
    public function getIterator(): Iterator
    {
        foreach ($this->lazies as $key => $lazy) {
            /** @var T $object */
            $object = ($lazy)($this->injector);

            yield $key => $object;
        }
    }

    public function count(): int
    {
        return count($this->lazies);
    }
}


================================================
FILE: src/di/MultiBinding/MapProvider.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\MultiBinding;

use Ray\Di\Di\Set;
use Ray\Di\Exception\SetNotFound;
use Ray\Di\InjectionPointInterface;
use Ray\Di\InjectorInterface;
use Ray\Di\ProviderInterface;

/**
 * @implements ProviderInterface<Map>
 */
final class MapProvider implements ProviderInterface
{
    public function __construct(private readonly InjectionPointInterface $ip, private MultiBindings $multiBindings, private readonly InjectorInterface $injector)
    {
    }

    /**
     * @return Map<mixed>
     */
    public function get(): Map
    {
        $param = $this->ip->getParameter();
        $setAttribute = $param->getAttributes(Set::class);
        if (! isset($setAttribute[0])) {
            throw new SetNotFound((string) $param);
        }

        /** @var Set<object> $set */
        $set = $setAttribute[0]->newInstance();

        /** @var array<string, LazyTo<object>> $lazies */
        $lazies = $this->multiBindings[$set->interface];

        return new Map($lazies, $this->injector);
    }
}


================================================
FILE: src/di/MultiBinding/MultiBindingModule.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\MultiBinding;

use Ray\Di\AbstractModule;
use Ray\Di\Types;

/**
 * @psalm-import-type BindableInterface from Types
 */
final class MultiBindingModule extends AbstractModule
{
    protected function configure(): void
    {
        $this->bind(MultiBindings::class);
        $this->bind(Map::class)->toProvider(MapProvider::class);
    }
}


================================================
FILE: src/di/MultiBinding/MultiBindings.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\MultiBinding;

use ArrayObject;
use Ray\Di\Types;

use function array_merge_recursive;

/**
 * @psalm-import-type LazyBindingList from Types
 * @extends ArrayObject<string, LazyBindingList>
 */
final class MultiBindings extends ArrayObject
{
    public function merge(self $multiBindings): void
    {
        $this->exchangeArray(
            array_merge_recursive($this->getArrayCopy(), $multiBindings->getArrayCopy())
        );
    }
}


================================================
FILE: src/di/Name.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Di\Di\Named;
use Ray\Di\Di\Qualifier;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use ReflectionParameter;

use function class_exists;
use function is_string;
use function preg_match;

/**
 * @psalm-import-type ParameterNameMapping from Types
 */
final class Name
{
    /**
     * 'Unnamed' name
     */
    public const ANY = '';

    private string $name = '';

    /**
     * Named database
     *
     * format: array<varName, NamedName>
     *
     * @var ParameterNameMapping
     */
    private array $names = [];

    /**
     * @param string|ParameterNameMapping $name
     */
    public function __construct(string|array $name)
    {
        if (is_string($name)) {
            $this->setName($name);

            return;
        }

        $this->names = $name;
    }

    /**
     * Create instance from PHP8 attributes
     *
     * psalm does not know ReflectionAttribute?? PHPStan produces no type error here.
     */
    public static function withAttributes(ReflectionMethod $method): ?self
    {
        $params = $method->getParameters();
        $names = [];
        foreach ($params as $param) {
            /** @var array<ReflectionAttribute> $attributes */
            $attributes = $param->getAttributes();
            if ($attributes) {
                $name = self::getName($attributes);
                $names[$param->name] = $name;
            }
        }

        // Backward compatibility: Get parameter qualifiers from method-level attribute
        if ($names === []) {
            /** @psalm-suppress DeprecatedClass */
            $names = BcParameterQualifier::getNames($method);
        }

        if ($names !== []) {
            return new self($names);
        }

        return null;
    }

    /**
     * @param non-empty-array<ReflectionAttribute> $attributes
     *
     * @throws ReflectionException
     *
     * @psalm-suppress MixedAssignment
     * @psalm-suppress MixedArgument
     */
    private static function getName(array $attributes): string
    {
        $refAttribute = $attributes[0];
        $attribute = $refAttribute->newInstance();
        if ($attribute instanceof Named) {
            return $attribute->value;
        }

        $isQualifier = (bool) (new ReflectionClass($attribute))->getAttributes(Qualifier::class);
        if ($isQualifier) {
            return $attribute::class;
        }

        return '';
    }

    public function __invoke(ReflectionParameter $parameter): string
    {
        // single variable named binding
        if ($this->name) {
            return $this->name;
        }

        $parameterName = $parameter->name;

        // multiple variable named binding
        return $this->names[$parameterName] ?? $this->names[self::ANY] ?? self::ANY;
    }

    private function setName(string $name): void
    {
        // annotation
        if (class_exists($name, false)) {
            $this->name = $name;

            return;
        }

        // single name
        // @Named(name)
        if ($name === self::ANY || preg_match('/^\w+$/', $name)) {
            $this->name = $name;

            return;
        }

        // name list (backward compatibility)
        // @Named(varName1=name1, varName2=name2) or toConstructor string format
        /** @psalm-suppress DeprecatedClass */
        $this->names = BcStringParser::parse($name);
    }
}


================================================
FILE: src/di/NewInstance.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\Bind as AopBind;
use Ray\Aop\WeavedInterface;
use ReflectionClass;
use ReflectionException;
use Stringable;

use function assert;

/**
 * @psalm-import-type MethodArguments from Types
 */
final class NewInstance implements Stringable
{
    /** @var class-string */
    private string $class;

    /** @var SetterMethods */
    private $setterMethods;
    private ?Arguments $arguments = null;
    private ?AspectBind $bind = null;

    /**
     * @phpstan-param ReflectionClass<object> $class
     */
    public function __construct(
        ReflectionClass $class,
        SetterMethods $setterMethods,
        ?Name $constructorName = null
    ) {
        $constructorName = $constructorName ?: new Name(Name::ANY);
        $this->class = $class->getName();
        $constructor = $class->getConstructor();
        if ($constructor) {
            $this->arguments = new Arguments($constructor, $constructorName);
        }

        $this->setterMethods = $setterMethods;
    }

    /**
     * @throws ReflectionException
     */
    public function __invoke(Container $container): object
    {
        $reflection = new ReflectionClass($this->class);
        /** @psalm-suppress MixedMethodCall */
        $instance = $this->arguments instanceof Arguments ? $reflection->newInstanceArgs($this->arguments->inject($container)) : new $this->class();

        return $this->postNewInstance($container, $instance);
    }

    /**
     * @return class-string
     */
    public function __toString(): string
    {
        return $this->class;
    }

    /**
     * @param MethodArguments $params
     *
     * @throws ReflectionException
     */
    public function newInstanceArgs(Container $container, array $params): object
    {
        $instance = (new ReflectionClass($this->class))->newInstanceArgs($params);

        return $this->postNewInstance($container, $instance);
    }

    /**
     * @param class-string $class
     */
    public function weaveAspects(string $class, AopBind $bind): void
    {
        $this->class = $class;
        $this->bind = new AspectBind($bind);
    }

    public function accept(VisitorInterface $visitor): void
    {
        $visitor->visitNewInstance(
            $this->class,
            $this->setterMethods,
            $this->arguments,
            $this->bind
        );
    }

    private function postNewInstance(Container $container, object $instance): object
    {
        $this->enableAop($instance, $container);

        // setter injection
        ($this->setterMethods)($instance, $container);

        return $instance;
    }

    public function enableAop(object $instance, Container $container): void
    {
        if (! $this->bind instanceof AspectBind) {
            return;
        }

        assert($instance instanceof WeavedInterface);

        $instance->_setBindings($this->bind->inject($container));  // Ray.Aop ^2.18
    }
}


================================================
FILE: src/di/NullDependency.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

/**
 * @codeCoverageIgnore
 */
final class NullDependency implements DependencyInterface
{
    /**
     * {@inheritdoc}
     */
    public function __toString(): string
    {
        return '';
    }

    /**
     * {@inheritdoc}
     */
    public function inject(Container $container): void
    {
    }

    /**
     * {@inheritdoc}
     */
    public function register(array &$container, Bind $bind): void
    {
        $container[(string) $bind] = $bind->getBound();
    }

    /**
     * {@inheritdoc}
     */
    public function setScope($scope): void
    {
    }
}


================================================
FILE: src/di/NullModule.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

final class NullModule extends AbstractModule
{
    protected function configure(): void
    {
    }
}


================================================
FILE: src/di/NullObjectDependency.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Koriym\NullObject\NullObject;
use ReflectionClass;

use function assert;
use function is_dir;

/**
 * @codeCoverageIgnore
 */
final class NullObjectDependency implements DependencyInterface
{
    /**
     * @param class-string $interface
     */
    public function __construct(private string $interface)
    {
    }

    /**
     * {@inheritdoc}
     */
    public function __toString(): string
    {
        return '';
    }

    /**
     * {@inheritdoc}
     */
    public function inject(Container $container): null
    {
        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function register(array &$container, Bind $bind): void
    {
        $container[(string) $bind] = $bind->getBound();
    }

    /**
     * {@inheritdoc}
     */
    public function setScope($scope): void
    {
    }

    public function toNull(string $scriptDir): Dependency
    {
        assert(is_dir($scriptDir));
        $nullObject = new NullObject();
        $class = $nullObject->save($this->interface, $scriptDir);

        return new Dependency(new NewInstance(new ReflectionClass($class), new SetterMethods([])));
    }
}


================================================
FILE: src/di/ProviderInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

/**
 * @template T of mixed
 */
interface ProviderInterface
{
    /**
     * @return T
     */
    public function get();
}


================================================
FILE: src/di/ProviderProvider.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Di\Di\Set;

/**
 * @implements ProviderInterface<mixed>
 * @template T of object
 */
final class ProviderProvider implements ProviderInterface
{
    /** @param Set<T> $set */
    public function __construct(private InjectorInterface $injector, private Set $set)
    {
    }

    /** @return mixed */
    public function get()
    {
        return $this->injector->getInstance($this->set->interface, $this->set->name);
    }
}


================================================
FILE: src/di/ProviderSetModule.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

final class ProviderSetModule extends AbstractModule
{
    protected function configure(): void
    {
        $this->bind(ProviderInterface::class)->toProvider(ProviderSetProvider::class);
    }
}


================================================
FILE: src/di/ProviderSetProvider.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Di\Di\Set;
use Ray\Di\Exception\SetNotFound;

/**
 * @implements ProviderInterface<mixed>
 * @template T of object
 */
final class ProviderSetProvider implements ProviderInterface
{
    public function __construct(private InjectionPointInterface $ip, private InjectorInterface $injector)
    {
    }

    /** @phpstan-return ProviderProvider<object> */
    public function get(): ProviderProvider
    {
        $param = $this->ip->getParameter();
        $setAttribute = $param->getAttributes(Set::class);
        if (! isset($setAttribute[0])) {
            throw new SetNotFound((string) $this->ip->getParameter());
        }

        $set = $setAttribute[0]->newInstance();

        return new ProviderProvider($this->injector, $set);
    }
}


================================================
FILE: src/di/Scope.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

final class Scope
{
    /**
     * Singleton scope
     */
    public const SINGLETON = 'Singleton';

    /**
     * Prototype scope
     */
    public const PROTOTYPE = 'Prototype';
}


================================================
FILE: src/di/SetContextInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

interface SetContextInterface
{
    /**
     * Set provider context
     *
     * @param string $context
     *
     * @return void
     */
    public function setContext($context);
}


================================================
FILE: src/di/SetterMethod.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Exception;
use LogicException;
use Ray\Di\Exception\Unbound;
use ReflectionMethod;

use function call_user_func_array;
use function is_callable;

final class SetterMethod implements AcceptInterface
{
    private readonly string $method;
    private readonly Arguments $arguments;

    /**
     * Is optional binding ?
     */
    private bool $isOptional = false;

    public function __construct(ReflectionMethod $method, Name $name)
    {
        $this->method = $method->name;
        $this->arguments = new Arguments($method, $name);
    }

    /**
     * @param object $instance
     *
     * @throws Unbound
     * @throws Exception
     */
    public function __invoke($instance, Container $container): void
    {
        try {
            $parameters = $this->arguments->inject($container);
        } catch (Unbound $unbound) {
            if ($this->isOptional) {
                return;
            }

            throw $unbound;
        }

        $callable = [$instance, $this->method];
        if (! is_callable($callable)) {
            throw new LogicException(); // @codeCoverageIgnore
        }

        call_user_func_array($callable, $parameters);
    }

    public function setOptional(): void
    {
        $this->isOptional = true;
    }

    /** @inheritDoc */
    public function accept(VisitorInterface $visitor): void
    {
        try {
            $visitor->visitSetterMethod($this->method, $this->arguments);
        } catch (Unbound $unbound) {
            if ($this->isOptional) {
                // Return when no dependency given and @Inject(optional=true) annotated to setter method.
                return;
            }

            // Throw exception when no dependency given and @Inject(optional=false) annotated to setter method.
            throw $unbound;
        }
    }
}


================================================
FILE: src/di/SetterMethods.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Exception;

/**
 * @psalm-import-type SetterMethodsList from Types
 */
final class SetterMethods implements AcceptInterface
{
    /**
     * @param SetterMethodsList $setterMethods
     */
    public function __construct(private array $setterMethods)
    {
    }

    /**
     * @throws Exception
     */
    public function __invoke(object $instance, Container $container): void
    {
        foreach ($this->setterMethods as $setterMethod) {
            ($setterMethod)($instance, $container);
        }
    }

    public function add(?SetterMethod $setterMethod = null): void
    {
        if (! $setterMethod instanceof SetterMethod) {
            return;
        }

        $this->setterMethods[] = $setterMethod;
    }

    /** @inheritDoc */
    public function accept(VisitorInterface $visitor): void
    {
        $visitor->visitSetterMethods($this->setterMethods);
    }
}


================================================
FILE: src/di/SpyCompiler.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use LogicException;
use Ray\Aop\BindInterface;
use Ray\Aop\CompilerInterface;

use function array_keys;
use function implode;
use function method_exists;
use function sprintf;

/**
 * @codeCoverageIgnore
 */
final class SpyCompiler implements CompilerInterface
{
    /**
     * {@inheritDoc}
     *
     * @phpstan-return never
     *
     * @psalm-suppress InvalidReturnType
     * @template T of object
     */
    public function newInstance(string $class, array $args, BindInterface $bind)
    {
        // never called
        throw new LogicException('SpyCompiler::newInstance() should never be called');
    }

    /**
     * Return "logging" class name
     *
     * Dummy classes are used for logging and don't really exist.
     * So the code breaks the QA rules as shown below.
     * NOTE: psalm-suppress is acceptable here for dummy/logging infrastructure
     *
     * @psalm-suppress MoreSpecificReturnType
     * @psalm-suppress LessSpecificReturnStatement
     */
    public function compile(string $class, BindInterface $bind): string
    {
        if ($this->hasNoBinding($class, $bind)) {
            return $class;
        }

        return $class . $this->getInterceptors($bind); // @phpstan-ignore-line
    }

    /**
     * @param class-string $class
     */
    private function hasNoBinding(string $class, BindInterface $bind): bool
    {
        $hasMethod = $this->hasBoundMethod($class, $bind);

        return ! $bind->getBindings() && ! $hasMethod;
    }

    /**
     * @param class-string $class
     */
    private function hasBoundMethod(string $class, BindInterface $bind): bool
    {
        $bindingMethods = array_keys($bind->getBindings());
        $hasMethod = false;
        foreach ($bindingMethods as $bindingMethod) {
            if (method_exists($class, $bindingMethod)) {
                $hasMethod = true;
            }
        }

        return $hasMethod;
    }

    private function getInterceptors(BindInterface $bind): string
    {
        $bindings = $bind->getBindings();
        if (! $bindings) {
            return ''; // @codeCoverageIgnore
        }

        $log = ' (aop)';
        foreach ($bindings as $method => $interceptors) {
            /**
             * @phpstan-var array<string> $interceptors
             * @psalm-ignore-var
             */
            $log .= sprintf(
                ' +%s(%s)',
                $method,
                implode(', ', $interceptors)
            );
        }

        return $log;
    }
}


================================================
FILE: src/di/Types.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\MethodInterceptor;
use Ray\Aop\Pointcut;

/**
 * Ray.Di Domain Types for Psalm
 *
 * This file contains Psalm type definitions for the Ray.Di dependency injection framework.
 * These types enhance static analysis and provide better IDE support.
 *
 * Container and Registry Types
 *
 * @psalm-type DependencyContainer = array<non-empty-string, DependencyInterface>
 * @psalm-type DependencyIndex = non-empty-string
 * @psalm-type PointcutList array<int, Pointcut>
 * @psalm-type BindingName = non-empty-string
 * @psalm-type BindableInterface = class-string|''
 * @psalm-type ConstructorNameMapping = array<non-empty-string, non-empty-string>
 * @psalm-type ParameterNameMapping = array<string, string>
 * @psalm-type NamedParameterString = non-empty-string
 * @psalm-type ScriptDir = non-empty-string
 *
 * Enhanced Injection and Argument Types
 * @psalm-type InjectableValue object|scalar|array<array-key, (object|scalar|null)>|null
 * @psalm-type InjectionPointDefinition = array{0: string, 1: string, 2: bool}
 * @psalm-type InjectionPointsList = list<InjectionPointDefinition>
 * @psalm-type MethodArguments = list<mixed>
 * @psalm-type ArgumentSerializationData = array{0: DependencyIndex, 1: bool, 2: string, 3: string, 4: string, 5: array{0: string, 1: string, 2: string}}
 * @psalm-type UnboundTypeList = list<'bool'|'int'|'float'|'string'|'array'|'resource'|'callable'|'iterable'|'object'|'mixed'>
 * @psalm-type QualifierList = array<object>
 *
 * Scope and Lifecycle Types
 * @psalm-type ScopeType = Scope::SINGLETON|Scope::PROTOTYPE
 * @psalm-type ProviderContext = string
 *
 * MultiBinding Types
 * @psalm-type MultiBindingMap = array<string, non-empty-array<array-key, MultiBinding\LazyInterface>>
 * @psalm-type LazyBindingList = non-empty-array<array-key, MultiBinding\LazyInterface>
 *
 * AOP and Aspect Types
 * @psalm-type MethodInterceptorBindings array<non-empty-string, list<MethodInterceptor>>
 * @psalm-type InterceptorClassList array<class-string<MethodInterceptor>>
 * @psalm-type VisitorResult = object|array<array-key, mixed>|null
 *
 * Reflection and Metadata Types
 * @psalm-type ReflectionMethodReference = array{0: string, 1: string, 2: string}
 * @psalm-type DependencyMeta = string
 *
 * Exception and Error Types
 * @psalm-type DiException = Exception\Unbound|Exception\Untargeted|Exception\NotFound|Exception\InvalidProvider|Exception\InvalidType
 *
 * Annotation Types
 * @psalm-type AnnotationType = Di\Named|Di\Inject|Di\Qualifier|Di\PostConstruct|Di\Assisted|Di\Set<object>
 * @psalm-type DependencyImplementation = Dependency|DependencyProvider|Instance|NullDependency|NullObjectDependency
 * @psalm-type LazyImplementation = MultiBinding\LazyInstance<mixed>|MultiBinding\LazyProvider<ProviderInterface<mixed>>|MultiBinding\LazyTo<object>
 *
 * Core Component Types
 * @psalm-type SetterMethodsList = array<SetterMethod>
 * @psalm-type ArgumentsList = array<Argument>
 *
 * Domain-Specific Array Types
 * @psalm-type ModuleList = non-empty-array<AbstractModule>
 * @psalm-type NamedArguments = array<string, InjectableValue>
 */
final class Types
{
}


================================================
FILE: src/di/Untarget.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\ReflectionClass;

final class Untarget
{
    /**
     * @phpstan-var ReflectionClass<object>
     * @psalm-var ReflectionClass
     */
    private readonly ReflectionClass $class;
    private string $scope = Scope::PROTOTYPE;

    /**
     * @param class-string $class
     */
    public function __construct(string $class)
    {
        $this->class = new ReflectionClass($class);
    }

    /**
     * Bind untargeted binding
     */
    public function __invoke(Container $container, Bind $bind): void
    {
        $bound = (new DependencyFactory())->newAnnotatedDependency($this->class);
        $bound->setScope($this->scope);

        $bind->setBound($bound);
        $container->add($bind);
    }

    public function setScope(string $scope): void
    {
        $this->scope = $scope;
    }
}


================================================
FILE: src/di/VisitorInterface.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\Bind as AopBind;
use ReflectionParameter;

/**
 * Visits a Dependency
 *
 * @psalm-import-type SetterMethodsList from Types
 * @psalm-import-type ArgumentsList from Types
 */
interface VisitorInterface
{
    /**
     * Visits a Dependency
     *
     * @return mixed|void
     */
    public function visitDependency(NewInstance $newInstance, ?string $postConstruct, bool $isSingleton);

    /**
     * Visits a Provider
     *
     * @return mixed|void
     */
    public function visitProvider(Dependency $dependency, string $context, bool $isSingleton);

    /**
     * Visits an Instance
     *
     * @param mixed $value
     *
     * @return mixed|void
     */
    public function visitInstance($value);

    /**
     * Visits an AspectBind
     *
     * @return mixed|void
     */
    public function visitAspectBind(AopBind $aopBind);

    /**
     * Visits a New Instance
     *
     * @return mixed|void
     */
    public function visitNewInstance(
        string $class,
        SetterMethods $setterMethods,
        ?Arguments $arguments,
        ?AspectBind $bind
    );

    /**
     * Visits Setter Methods
     *
     * @param SetterMethodsList $setterMethods
     *
     * @return mixed|void
     */
    public function visitSetterMethods(array $setterMethods);

    /**
     * Visits a Setter Method
     *
     * @return mixed|void
     */
    public function visitSetterMethod(string $method, Arguments $arguments);

    /**
     * Visits Arguments
     *
     * @param ArgumentsList $arguments
     *
     * @return mixed|void
     */
    public function visitArguments(array $arguments);

    /**
     * Visits an Argument
     *
     * @param mixed $defaultValue
     *
     * @return mixed|void
     */
    public function visitArgument(
        string $index,
        bool $isDefaultAvailable,
        $defaultValue,
        ReflectionParameter $parameter
    );
}


================================================
FILE: src-deprecated/di/BcParameterQualifier.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Di\Di\InjectInterface;
use Ray\Di\Di\Qualifier;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;

use function count;

/**
 * Backward compatible parameter qualifier for single-parameter methods
 *
 * Automatically applies method-level Qualifier attributes to parameters when:
 * - Method has exactly one parameter
 * - Parameter has no explicit qualifier
 * - For constructors: Method has a Qualifier attribute (InjectInterface is implicit)
 * - For setters: Method has an attribute implementing both InjectInterface and Qualifier
 *
 * This behavior is deprecated for the following reasons:
 * - Violates Single Responsibility Principle (one attribute serving dual purposes)
 * - Creates fragility when refactoring (adding parameters changes behavior)
 * - Reduces code clarity (implicit rather than explicit)
 *
 * @deprecated Use explicit separation: #[Inject] at method level, Qualifier at parameter level
 *
 * Recommended migration:
 *   OLD (implicit):
 *     #[FakeLogDbInject]
 *     public function setDb(ExtendedPdoInterface $pdo) { }
 *
 *   NEW (explicit):
 *     #[Inject]
 *     public function setDb(#[FakeLogDb] ExtendedPdoInterface $pdo) { }
 *
 * @internal
 */
final class BcParameterQualifier
{
    /**
     * Get parameter qualifier names from method-level attribute if applicable
     *
     * Returns parameter name mapping if:
     * 1. Method has exactly one parameter
     * 2. Parameter has no qualifier attribute
     * 3. For setters: Method has an attribute implementing both InjectInterface and Qualifier
     * 4. For constructors: Method has a Qualifier attribute (InjectInterface is implicit)
     *
     * @param ReflectionMethod $method The method to analyze
     *
     * @return array<string, string> Parameter name to qualifier mapping (empty if not applicable)
     */
    public static function getNames(ReflectionMethod $method): array
    {
        $params = $method->getParameters();

        // Only for single-parameter methods
        if (count($params) !== 1) {
            return [];
        }

        $qualifier = self::getQualifier($method);
        if ($qualifier === '') {
            return [];
        }

        return [$params[0]->name => $qualifier];
    }

    /**
     * Get parameter qualifier from method-level attribute if applicable
     *
     * Returns the qualifier name if:
     * 1. Method has exactly one parameter
     * 2. Parameter has no qualifier attribute
     * 3. For setters: Method has an attribute implementing both InjectInterface and Qualifier
     * 4. For constructors: Method has a Qualifier attribute (InjectInterface is implicit)
     *
     * @param ReflectionMethod $method The method to analyze
     *
     * @return string The qualifier class name, or empty string if not applicable
     */
    private static function getQualifier(ReflectionMethod $method): string
    {
        $params = $method->getParameters();

        // Only for single-parameter methods
        if (count($params) !== 1) {
            return '';
        }

        $param = $params[0];

        // Check if parameter already has a qualifier
        if (self::hasParameterQualifier($param->getAttributes())) {
            return '';
        }

        $isConstructor = $method->name === '__construct';

        // Check method-level attributes for Qualifier
        $methodAttributes = $method->getAttributes();
        foreach ($methodAttributes as $attr) {
            $instance = $attr->newInstance();
            $attrClass = new ReflectionClass($attr->getName());
            $qualifierAttr = $attrClass->getAttributes(Qualifier::class);

            // Skip if not a Qualifier
            if ($qualifierAttr === []) {
                continue;
            }

            // For constructors: Qualifier alone is sufficient (InjectInterface is implicit)
            if ($isConstructor) {
                return $attr->getName();
            }

            // For setters: Must also implement InjectInterface
            if ($instance instanceof InjectInterface) {
                return $attr->getName();
            }
        }

        return '';
    }

    /**
     * Check if parameter already has a qualifier attribute
     *
     * @param array<ReflectionAttribute> $attributes
     */
    private static function hasParameterQualifier(array $attributes): bool
    {
        foreach ($attributes as $attr) {
            $attrClass = new ReflectionClass($attr->getName());

            // Check for Qualifier marker
            if ($attrClass->getAttributes(Qualifier::class) !== []) {
                return true;
            }
        }

        return false;
    }
}


================================================
FILE: src-deprecated/di/BcStringParser.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use function explode;
use function substr;
use function trim;

/**
 * Backward compatible string format parser for toConstructor() method
 *
 * Parses "key=value,key=value" string format for backward compatibility.
 * This format is deprecated. Use parameter-level attributes or array format instead.
 *
 * @deprecated Use parameter-level attributes (#[Named('name')]) or array format (['param' => 'name'])
 *
 * @internal
 */
final class BcStringParser
{
    /**
     * Parse "key=value,key=value" format
     *
     * @return array<string, string>
     *
     * @psalm-pure
     */
    public static function parse(string $name): array
    {
        $names = [];
        $keyValues = explode(',', $name);
        foreach ($keyValues as $keyValue) {
            $exploded = explode('=', $keyValue, 2);
            if (isset($exploded[1])) {
                [$key, $value] = $exploded;
                if (isset($key[0]) && $key[0] === '$') {
                    $key = substr($key, 1);
                }

                $trimmedKey = trim($key);
                if ($trimmedKey === '') {
                    continue;
                }

                $names[$trimmedKey] = trim($value);
            }
        }

        return $names;
    }
}


================================================
FILE: src-deprecated/di/EmptyModule.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

/**
 * @deprecated User NullModule instead
 */
class EmptyModule extends AbstractModule
{
    /**
     * {@inheritdoc}
     */
    protected function configure() : void
    {
    }
}


================================================
FILE: src-deprecated/di/NullCache.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Doctrine\Common\Cache\CacheProvider;

/**
 * @psalm-suppress DeprecatedInterface
 * @deprecated
 */
final class NullCache extends CacheProvider
{
    /**
     * {@inheritDoc}
     */
    protected function doFetch($id)
    {
        return false;
    }

    /**
     * {@inheritDoc}
     */
    protected function doContains($id)
    {
        return false;
    }

    /**
     * {@inheritDoc}
     */
    protected function doSave($id, $data, $lifeTime = 0)
    {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    protected function doDelete($id)
    {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    protected function doFlush()
    {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    protected function doGetStats()
    {
        return [];
    }
}


================================================
FILE: src-deprecated/di/Provider.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

/**
 * @deprecated User ProviderInterface instead
 */
interface Provider extends ProviderInterface
{
}


================================================
FILE: tests/bootstrap.php
================================================
<?php

declare(strict_types=1);

putenv('TMPDIR=' . __DIR__ . '/tmp');

require_once dirname(__DIR__) . '/vendor/autoload.php';

$deleteFiles = static function (string $path) use (&$deleteFiles): void {
    foreach (array_filter((array) glob($path . '/*')) as $file) {
        is_dir($file) ? $deleteFiles($file) : unlink($file);
        @rmdir($file);
    }
};

$deleteFiles(__DIR__ . '/tmp');
$deleteFiles(__DIR__ . '/compiler/tmp');


================================================
FILE: tests/di/AnnotatedClassTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use LogicException;
use PHPUnit\Framework\TestCase;
use Ray\Aop\ReflectionClass;

class AnnotatedClassTest extends TestCase
{
    /** @var AnnotatedClass */
    protected $annotatedClass;

    protected function setUp(): void
    {
        parent::setUp();
        $this->annotatedClass = new AnnotatedClass();
    }

    public function testInvoke(): void
    {
        $newInstance = $this->annotatedClass->getNewInstance(new ReflectionClass(FakeCar::class)); // @phpstan-ignore-line
        $this->assertInstanceOf(NewInstance::class, $newInstance);
        $container = new Container();
        (new Bind($container, FakeTyreInterface::class))->to(FakeTyre::class);
        (new Bind($container, FakeEngineInterface::class))->to(FakeEngine::class);
        (new Bind($container, FakeHandleInterface::class))->toProvider(FakeHandleProvider::class);
        (new Bind($container, FakeMirrorInterface::class))->annotatedWith('right')->to(FakeMirrorRight::class);
        (new Bind($container, FakeMirrorInterface::class))->annotatedWith('left')->to(FakeMirrorRight::class);
        (new Bind($container, FakeGearStickInterface::class))->annotatedWith(FakeGearStickInject::class)->toProvider(FakeGearStickProvider::class);
        $car = $newInstance($container);
        if (! $car instanceof FakeCar) {
            throw new LogicException();
        }

        $this->assertInstanceOf(FakeCar::class, $car);
        $this->assertInstanceOf(FakeTyre::class, $car->frontTyre);
        $this->assertInstanceOf(FakeTyre::class, $car->rearTyre);
        $this->assertInstanceOf(FakeLeatherGearStick::class, $car->gearStick);
        $this->assertNull($car->hardtop);
    }

    /**
     * @phpstan-param class-string $class
     *
     * @dataProvider classProvider
     */
    public function testAnnotatedByAnnotation(string $class): void
    {
        $newInstance = $this->annotatedClass->getNewInstance(new ReflectionClass($class));
        $container = new Container();
        (new Bind($container, FakeMirrorInterface::class))->annotatedWith(FakeLeft::class)->to(FakeMirrorLeft::class);
        (new Bind($container, FakeMirrorInterface::class))->annotatedWith(FakeRight::class)->to(FakeMirrorRight::class);
        $handleBar = $newInstance($container);
        if (! $handleBar instanceof FakeHandleBar && ! $handleBar instanceof FakeHandleBarQualifier) {
            throw new LogicException();
        }

        $this->assertInstanceOf(FakeMirrorLeft::class, $handleBar->leftMirror);
        $this->assertInstanceOf(FakeMirrorRight::class, $handleBar->rightMirror);
    }

    /**
     * @return array<array<class-string>>
     */
    public function classProvider(): array
    {
        return [
            [FakeHandleBar::class],
            [FakeHandleBarQualifier::class],
        ];
    }
}


================================================
FILE: tests/di/ArgumentTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use PHPUnit\Framework\TestCase;
use ReflectionMethod;
use ReflectionParameter;

use function assert;
use function serialize;
use function unserialize;

class ArgumentTest extends TestCase
{
    /** @var Argument */
    protected $argument;

    protected function setUp(): void
    {
        $this->argument = new Argument(new ReflectionParameter([FakeCar::class, '__construct'], 'engine'), Name::ANY);
    }

    public function testToString(): void
    {
        $this->assertSame('Ray\Di\FakeEngineInterface-' . Name::ANY, (string) $this->argument);
    }

    public function testToStringScalar(): void
    {
        $argument = new Argument(new ReflectionParameter([FakeInternalTypes::class, 'stringId'], 'id'), Name::ANY);
        $this->assertSame('-' . Name::ANY, (string) $argument);
    }

    public function testSerializable(): void
    {
        $argument = unserialize(serialize(new Argument(new ReflectionParameter([FakeInternalTypes::class, 'stringId'], 'id'), Name::ANY)));
        assert($argument instanceof Argument);
        $class = $argument->get()->getDeclaringFunction();
        $this->assertInstanceOf(ReflectionMethod::class, $class);
    }
}


================================================
FILE: tests/di/ArgumentsTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use PHPUnit\Framework\TestCase;
use ReflectionMethod;
use ReflectionParameter;

use function spl_object_hash;

class ArgumentsTest extends TestCase
{
    /** @var Arguments */
    protected $arguments;

    protected function setUp(): void
    {
        $this->arguments = new Arguments(new ReflectionMethod(FakeCar::class, 'setTires'), new Name(Name::ANY));
    }

    public function testInject(): void
    {
        $container = (new FakeCarModule())->getContainer();
        $parameters = $this->arguments->inject($container);
        $this->assertInstanceOf(FakeTyre::class, $parameters[0]);
        $this->assertInstanceOf(FakeTyre::class, $parameters[1]);
        $param0 = $parameters[0];
        $this->assertNotSame(spl_object_hash($param0), $parameters[1]);
    }

    public function testParameterDefaultValue(): void
    {
        $defaultValue = (new ReflectionParameter([FakeHandleProvider::class, '__construct'], 'logo'))->getDefaultValue();
        $emptyContainer = new Container();
        $parameters = new Arguments(new ReflectionMethod(FakeHandleProvider::class, '__construct'), new Name(Name::ANY));
        $parametersValue = $parameters->inject($emptyContainer);
        $this->assertSame($defaultValue, $parametersValue[0]);
    }
}


================================================
FILE: tests/di/AssistedTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use PHPUnit\Framework\TestCase;
use Ray\Di\Exception\MethodInvocationNotAvailable;

class AssistedTest extends TestCase
{
    /** @var InjectorInterface */
    private $injector;

    protected function setUp(): void
    {
        $this->injector = new Injector(new FakeToBindModule());
    }

    public function testAssisted(): void
    {
        $consumer = $this->injector->getInstance(FakeAssistedConsumer::class);
        $assistedDependency = $consumer->assistOne('a', 'b');
        $expected = FakeRobot::class;
        $this->assertInstanceOf($expected, $assistedDependency);
    }

    public function testAssistedWithName(): void
    {
        $this->injector = new Injector(new FakeInstanceBindModule());
        $consumer = $this->injector->getInstance(FakeAssistedConsumer::class);
        $assistedDependency = $consumer->assistWithName('a7');
        $expected = 1;
        $this->assertSame($expected, $assistedDependency);
    }

    public function testAssistedAnyWithName(): void
    {
        $injector = new Injector(new FakeToBindModule(new FakeInstanceBindModule()));
        $consumer = $injector->getInstance(FakeAssistedConsumer::class);
        [$assistedDependency1, $assistedDependency2] = $consumer->assistAny();
        $expected1 = 1;
        $this->assertSame($expected1, $assistedDependency1);
        $this->assertInstanceOf(FakeRobot::class, $assistedDependency2);
    }

    public function testAssistedMethodInvocation(): void
    {
        $assistedConsumer = (new Injector(new FakeAssistedDbModule()))->getInstance(FakeAssistedParamsConsumer::class);
        [$id, $db] = $assistedConsumer->getUser(1);
        /** @var FakeAbstractDb $db */
        $this->assertSame(1, $id);
        $this->assertSame(1, $db->dbId);
    }

    public function testAssistedMethodInvocationNotAvailable(): void
    {
        $this->expectException(MethodInvocationNotAvailable::class);
        $assistedDbProvider = (new Injector(new FakeAssistedDbModule()))->getInstance(FakeAssistedDbProvider::class);
        $assistedDbProvider->get();
    }

    public function testAssistedCustomInject(): void
    {
        $assistedConsumer = (new Injector(new FakeAssistedDbModule()))->getInstance(FakeAssistedParamsConsumer::class);
        [$id] = $assistedConsumer->getUser(1);
        $this->assertSame(1, $id);
    }
}


================================================
FILE: tests/di/BcParameterQualifierIntegrationTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use PHPUnit\Framework\TestCase;

class BcParameterQualifierIntegrationTest extends TestCase
{
    public function testBcParameterQualifierWorks(): void
    {
        $injector = new Injector(new FakeBcParameterQualifierModule());
        /** @psalm-suppress DeprecatedClass */
        $instance = $injector->getInstance(FakeClassWithBcParameterQualifier::class);

        // The setGearStick method uses #[FakeInjectOne] at method level only
        // BcParameterQualifier should apply it to the parameter
        $this->assertInstanceOf(FakeLeatherGearStick::class, $instance->gearStick);
    }
}


================================================
FILE: tests/di/BcParameterQualifierTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use PHPUnit\Framework\TestCase;
use Ray\Di\Annotation\FakeInjectOne;
use Ray\Di\Annotation\FakeQualifierOnly;
use ReflectionMethod;

class BcParameterQualifierTest extends TestCase
{
    public function testGetNamesFromMethodLevelAttribute(): void
    {
        $method = new ReflectionMethod(FakeBcParameterQualifierClass::class, 'setSingleParam');
        /** @psalm-suppress DeprecatedClass */
        $names = BcParameterQualifier::getNames($method);

        $this->assertSame(['param' => FakeInjectOne::class], $names);
    }

    public function testNoNamesForMultipleParameters(): void
    {
        $method = new ReflectionMethod(FakeBcParameterQualifierClass::class, 'setMultipleParams');
        /** @psalm-suppress DeprecatedClass */
        $names = BcParameterQualifier::getNames($method);

        $this->assertSame([], $names);
    }

    public function testNoNamesWhenParameterHasQualifier(): void
    {
        $method = new ReflectionMethod(FakeBcParameterQualifierClass::class, 'setSingleParamWithQualifier');
        /** @psalm-suppress DeprecatedClass */
        $names = BcParameterQualifier::getNames($method);

        $this->assertSame([], $names);
    }

    public function testNoNamesForNonQualifierAttribute(): void
    {
        $method = new ReflectionMethod(FakeBcParameterQualifierClass::class, 'setSingleParamWithInjectOnly');
        /** @psalm-suppress DeprecatedClass */
        $names = BcParameterQualifier::getNames($method);

        $this->assertSame([], $names);
    }

    public function testNoNamesForMethodWithoutInjectInterface(): void
    {
        $method = new ReflectionMethod(FakeBcParameterQualifierClass::class, 'setSingleParamNoInject');
        /** @psalm-suppress DeprecatedClass */
        $names = BcParameterQualifier::getNames($method);

        $this->assertSame([], $names);
    }

    public function testNamesForTargetMethodOnly(): void
    {
        // FakeGearStickInject has TARGET_METHOD only
        // BC parameter qualifier now supports TARGET_METHOD-only attributes for backward compatibility
        $method = new ReflectionMethod(FakeBcParameterQualifierClass::class, 'setSingleParamMethodOnly');
        /** @psalm-suppress DeprecatedClass */
        $names = BcParameterQualifier::getNames($method);

        $this->assertSame(['param' => FakeGearStickInject::class], $names);
    }

    public function testConstructorWithQualifierOnly(): void
    {
        // Constructor with Qualifier-only attribute (no InjectInterface)
        // BC parameter qualifier should apply for constructors (InjectInterface is implicit)
        $method = new ReflectionMethod(FakeBcConstructorQualifierClass::class, '__construct');
        /** @psalm-suppress DeprecatedClass */
        $names = BcParameterQualifier::getNames($method);

        $this->assertSame(['param' => FakeQualifierOnly::class], $names);
    }
}


================================================
FILE: tests/di/BcStringParserTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use PHPUnit\Framework\TestCase;

class BcStringParserTest extends TestCase
{
    public function testParseStringFormat(): void
    {
        $result = BcStringParser::parse('engine=engine_name,var=var_name');
        $expected = ['engine' => 'engine_name', 'var' => 'var_name'];
        $this->assertSame($expected, $result);
    }

    public function testParseStringFormatWithSpaces(): void
    {
        $result = BcStringParser::parse('engine=engine_name, var=var_name');
        $expected = ['engine' => 'engine_name', 'var' => 'var_name'];
        $this->assertSame($expected, $result);
    }

    public function testParseStringFormatWithDollarPrefix(): void
    {
        $result = BcStringParser::parse('$engine=engine_name,$var=var_name');
        $expected = ['engine' => 'engine_name', 'var' => 'var_name'];
        $this->assertSame($expected, $result);
    }
}


================================================
FILE: tests/di/BindTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use PHPUnit\Framework\TestCase;
use Ray\Di\Exception\InvalidProvider;
use Ray\Di\Exception\InvalidType;
use Ray\Di\Exception\NotFound;

use function assert;
use function property_exists;
use function spl_object_hash;

class BindTest extends TestCase
{
    /** @var Bind */
    private $bind;

    protected function setUp(): void
    {
        parent::setUp();
        $this->bind = new Bind(new Container(), FakeTyreInterface::class);
    }

    public function testGetBound(): void
    {
        $this->bind->to(FakeTyre::class);
        $bound = $this->bind->getBound();
        $this->assertInstanceOf(Dependency::class, $bound);
    }

    public function testToString(): void
    {
        $this->assertSame('Ray\Di\FakeTyreInterface-' . Name::ANY, (string) $this->bind);
    }

    public function testInvalidToTest(): void
    {
        $this->expectException(NotFound::class);
        $this->bind->to('invalid-class'); // @phpstan-ignore-line
    }

    public function testInvalidToProviderTest(): void
    {
        $this->expectException(NotFound::class);
        $this->bind->toProvider('invalid-class'); // @phpstan-ignore-line
    }

    public function testInValidInterfaceBinding(): void
    {
        $this->expectException(NotFound::class);
        new Bind(new Container(), 'invalid-interface');
    }

    public function testUntargetedBind(): void
    {
        $container = new Container();
        $bind = new Bind($container, FakeEngine::class);
        unset($bind);
        $container = $container->getContainer();
        $this->assertArrayHasKey(FakeEngine::class . '-' . Name::ANY, $container);
    }

    public function testUntargetedBindSingleton(): void
    {
        $container = new Container();
        $bind = (new Bind($container, FakeEngine::class))->in(Scope::SINGLETON);
        unset($bind);
        $dependency1 = $container->getInstance(FakeEngine::class, Name::ANY);
        $dependency2 = $container->getInstance(FakeEngine::class, Name::ANY);
        $this->assertSame(spl_object_hash($dependency1), spl_object_hash($dependency2));
    }

    /**
     * @return array<int, array<int, array<string, string>>>
     */
    public function nameProvider(): array
    {
        return [
            [['tmpDir' => 'tmp_dir', 'leg' => 'left']],
        ];
    }

    /**
     * @param array<string, string>|string $name
     *
     * @dataProvider nameProvider
     */
    public function testToConstructor($name): void
    {
        $container = new Container();
        $container->add((new Bind($container, ''))->annotatedWith('tmp_dir')->toInstance('/tmp'));
        $container->add((new Bind($container, FakeLegInterface::class))->annotatedWith('left')->to(FakeLeftLeg::class));
        $container->add((new Bind($container, FakeRobotInterface::class))->toConstructor(FakeToConstructorRobot::class, $name));
        $instance = $container->getInstance(FakeRobotInterface::class, Name::ANY);
        assert($instance instanceof FakeToConstructorRobot);
        $this->assertInstanceOf(FakeLeftLeg::class, $instance->leg);
        $this->assertSame('/tmp', $instance->tmpDir);
    }

    public function testToConstructorWithMethodInjection(): void
    {
        $container = new Container();
        $container->add((new Bind($container, ''))->annotatedWith('tmp_dir')->toInstance('/tmp'));
        $container->add((new Bind($container, FakeLegInterface::class))->annotatedWith('left')->to(FakeLeftLeg::class));
        $container->add((new Bind($container, FakeEngineInterface::class))->to(FakeEngine::class));
        $container->add(
            (new Bind($container, FakeRobotInterface::class))->toConstructor(
                FakeToConstructorRobot::class,
                ['tmpDir' => 'tmp_dir', 'leg' => 'left'],
                (new InjectionPoints())->addMethod('setEngine')
            )
        );
        $instance = $container->getInstance(FakeRobotInterface::class, Name::ANY);
        assert($instance instanceof FakeToConstructorRobot);
        $this->assertInstanceOf(FakeEngine::class, $instance->engine);
    }

    public function testToValidation(): void
    {
        $this->expectException(InvalidType::class);
        (new Bind(new Container(), FakeHandleInterface::class))->to(FakeEngine::class);
    }

    public function testToProvider(): void
    {
        $this->expectException(InvalidProvider::class);
        (new Bind(new Container(), FakeHandleInterface::class))->toProvider(FakeEngine::class);
    }

    public function testBindProviderAsProvider(): void
    {
        $container = new Container();
        (new Bind($container, ProviderInterface::class))->annotatedWith('handle')->to(FakeHandleProvider::class);
        $instance = $container->getInstance(ProviderInterface::class, 'handle');
        $this->assertInstanceOf(FakeHandleProvider::class, $instance);
    }

    public function testBindProviderAsProviderInSingleton(): void
    {
        $container = new Container();
        (new Bind($container, ProviderInterface::class))->annotatedWith('handle')->to(FakeHandleProvider::class)->in(Scope::SINGLETON);
        $instance1 = $container->getInstance(ProviderInterface::class, 'handle');
        $instance2 = $container->getInstance(ProviderInterface::class, 'handle');
        $this->assertSame(spl_object_hash($instance1), spl_object_hash($instance2));
    }

    public function testProviderContext(): void
    {
        $container = new Container();
        (new Bind($container, ProviderInterface::class))->toProvider(FakeContextualProvider::class, 'context_string');
        $instance = $container->getInstance(ProviderInterface::class, Name::ANY);
        assert(property_exists($instance, 'context'));
        $this->assertSame('context_string', $instance->context);
    }
}


================================================
FILE: tests/di/ContainerTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use BadMethodCallException;
use PHPUnit\Framework\TestCase;
use Ray\Aop\Compiler;
use Ray\Aop\Matcher;
use Ray\Aop\Pointcut;
use Ray\Di\Exception\Unbound;
use Throwable;

use function assert;
use function get_class;
use function sys_get_temp_dir;

class ContainerTest extends TestCase
{
    /** @var Container */
    private $container;

    /** @var FakeEngine */
    private $engine;

    protected function setUp(): void
    {
        parent::setUp();
        $this->container = new Container();
        $this->engine = new FakeEngine();
        (new Bind($this->container, FakeEngineInterface::class))->toInstance($this->engine);
    }

    public function testGetDependency(): void
    {
        $dependencyIndex = FakeEngineInterface::class . '-' . Name::ANY;
        $instance = $this->container->getDependency($dependencyIndex);
        $this->assertInstanceOf(FakeEngine::class, $instance);
        $this->assertSame($this->engine, $instance);
    }

    public function testClassGetDependency(): void
    {
        (new Bind($this->container, FakeEngine::class))->toInstance($this->engine);
        $dependencyIndex = FakeEngine::class . '-' . Name::ANY;
        $instance = $this->container->getDependency($dependencyIndex);
        $this->assertInstanceOf(FakeEngine::class, $instance);
        $this->assertSame($this->engine, $instance);
    }

    public function testProviderGetDependency(): void
    {
        (new Bind($this->container, FakeEngine::class))->toProvider(FakeEngineProvider::class);
        $dependencyIndex = FakeEngine::class . '-' . Name::ANY;
        $instance = $this->container->getDependency($dependencyIndex);
        $this->assertInstanceOf(FakeEngine::class, $instance);
    }

    public function testGetInstance(): void
    {
        $instance = $this->container->getInstance(FakeEngineInterface::class, Name::ANY);
        $this->assertInstanceOf(FakeEngine::class, $instance);
        $this->assertSame($this->engine, $instance);
    }

    public function testClassGetInstance(): void
    {
        (new Bind($this->container, FakeEngine::class))->toInstance($this->engine);
        $instance = $this->container->getInstance(FakeEngine::class, Name::ANY);
        $this->assertInstanceOf(FakeEngine::class, $instance);
        $this->assertSame($this->engine, $instance);
    }

    public function testProviderGetInstance(): void
    {
        (new Bind($this->container, FakeEngine::class))->toProvider(FakeEngineProvider::class);
        $instance = $this->container->getInstance(FakeEngine::class, Name::ANY);
        $this->assertInstanceOf(FakeEngine::class, $instance);
    }

    public function testGetContainer(): void
    {
        $array = $this->container->getContainer();
        $dependencyIndex = FakeEngineInterface::class . '-' . Name::ANY;
        $this->assertArrayHasKey($dependencyIndex, $array);
    }

    public function testClassGetContainer(): void
    {
        (new Bind($this->container, FakeEngine::class))->toInstance($this->engine);
        $array = $this->container->getContainer();
        $dependencyIndex = FakeEngine::class . '-' . Name::ANY;
        $this->assertArrayHasKey($dependencyIndex, $array);
    }

    public function testMerge(): void
    {
        $extraContainer = new Container();
        $bind = (new Bind($this->container, FakeRobotInterface::class))->to(FakeRobot::class);
        $this->container->add($bind);
        $this->container->merge($extraContainer);
        $array = $this->container->getContainer();
        $this->assertArrayHasKey(FakeEngineInterface::class . '-' . Name::ANY, $array);
        $this->assertArrayHasKey(FakeRobotInterface::class . '-' . Name::ANY, $array);
    }

    public function testMergePointcuts(): void
    {
        $extraContainer = new Container();
        $pointcut1 = new Pointcut((new Matcher())->any(), (new Matcher())->any(), [FakeDoubleInterceptor::class]);
        $pointcut2 = new Pointcut((new Matcher())->any(), (new Matcher())->any(), [FakeDoubleInterceptor::class]);
        $this->container->addPointcut($pointcut1);
        $extraContainer->addPointcut($pointcut2);
        $this->container->merge($extraContainer);
        $array = [];
        foreach ($this->container->getPointcuts() as $pointcut) {
            $array[] = $pointcut->interceptors[0];
        }

        $this->assertContains(FakeDoubleInterceptor::class, $array);
    }

    public function testMove(): void
    {
        $newName = 'new';
        $this->container->move(FakeEngineInterface::class, Name::ANY, FakeEngineInterface::class, $newName);
        $dependencyIndex = FakeEngineInterface::class . '-' . $newName;
        $instance = $this->container->getDependency($dependencyIndex);
        $this->assertInstanceOf(FakeEngine::class, $instance);
    }

    public function testMoveUnbound(): void
    {
        $this->expectException(Unbound::class);
        $this->container->move(FakeEngineInterface::class, 'invalid', FakeEngineInterface::class, 'new');
    }

    public function testAbstractClassUnbound(): void
    {
        try {
            $this->container->getInstance('_INVALID_INTERFACE_', Name::ANY); // @phpstan-ignore-line
        } catch (Throwable $e) {
            $this->assertSame(Unbound::class, get_class($e));
        }
    }

    public function testAnnotateConstant(): void
    {
        $container = new Container();
        //FakeConstantInterface
        (new Bind($container, ''))->annotatedWith(FakeConstant::class)->toInstance('kuma');
        (new Bind($container, FakeConstantConsumer::class));
        $instance = $container->getInstance(FakeConstantConsumer::class, Name::ANY);
        $this->assertSame('kuma', $instance->constantByConstruct);
        $this->assertSame('kuma', $instance->constantBySetter);
        $this->assertSame('kuma', $instance->setterConstantWithoutVarName);
        $this->assertSame('default_construct', $instance->defaultByConstruct);
        $this->assertSame('default_setter', $instance->defaultBySetter);
    }

    public function testBadMethodCall(): void
    {
        $this->expectException(BadMethodCallException::class);
        $container = new Container();
        //FakeConstantInterface
        (new Bind($container, FakeEngineInterface::class))->toInstance(new FakeEngine());
        $container->getInstanceWithArgs(FakeEngineInterface::class, []);
    }

    /**
     * @covers \Ray\Di\Container::getInstanceWithArgs
     */
    public function testUnbound(): void
    {
        $this->expectException(Unbound::class);
        (new Container())->getInstanceWithArgs(FakeEngineInterface::class, []);
    }

    public function testWeaveAspectsWithEmptyPointcuts(): void
    {
        $container = new Container();
        (new Bind($container, FakeEngine::class));

        // Should work fine even when no pointcuts are defined
        $tmpDir = sys_get_temp_dir();
        assert($tmpDir !== '');
        $container->weaveAspects(new Compiler($tmpDir));

        $instance = $container->getInstance(FakeEngine::class);
        $this->assertInstanceOf(FakeEngine::class, $instance);
    }
}


================================================
FILE: tests/di/ContextualProviderTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use PHPUnit\Framework\TestCase;

class ContextualProviderTest extends TestCase
{
    public function testContextualProviderInjection(): void
    {
        $robot = (new Injector(new FakeContextualModule('main')))->getInstance(FakeRobotInterface::class);
        /** @var FakeContextualRobot $robot */
        $this->assertSame($robot->context, 'main');
    }
}


================================================
FILE: tests/di/DependencyTest.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use PHPUnit\Framework\TestCase;
use Ray\Aop\Compiler;
use Ray\Aop\Matcher;
use Ray\Aop\Pointcut;
use Ray\Aop\WeavedInterface;
use ReflectionClass;
use ReflectionMethod;

use function assert;
use function is_object;
use function property_exists;
use function spl_object_hash;

class DependencyTest extends TestCase
{
    /** @var Dependency */
    private $dependency;

    protected function setUp(): void
    {
        /** @var ReflectionClass<object> $class */
        $class = new ReflectionClass(FakeCar::class);
        $setters = [];
        $name = new Name(Name::ANY);
        $setters[] = new SetterMethod(new ReflectionMethod(FakeCar::class, 'setTires'), $name);
        $setters[] = new SetterMethod(new ReflectionMethod(FakeCar::class, 'setHardtop'), $name);
        $setterMethods = new SetterMethods($setters);
        $newInstance = new NewInstance($class, $setterMethods);
        $this->dependency = new Dependency($newInstance, new ReflectionMethod(FakeCar::class, 'postConstruct'));
    }

    /**
     * @return Container[][]
     * @psalm-return array{0: array{0: Container}}
     */
    public function containerProvider(): array
    {
        $container = new Container();
        (new Bind($container, FakeTyreInterface::class))->to(FakeTyre::class);
        (new Bind($container, FakeEngineInterface::class))->to(FakeEngine::class);
        (new Bind($container, FakeHardtopInterface::class))->to(FakeHardtop::class);

        return [[$container]];
    }

    /**
     * @dataProvider containerProvider
     */
    public function testInject(Container $container): void
    {
        $car = $this->dependency->inject($container);
        /** @var FakeCar $car */
        $this->assertInstanceOf(FakeCar::class, $car);
    }

    /**
     * @dataProvider containerProvider
     */
    public function testSetterInjection(Container $container): void
    {
        $car = $this->dependency->inject($container);
        /** @var FakeCar $car */
        $this->assertInstanceOf(FakeCar::class, $car);
        $this->assertInstanceOf(FakeTyre::class, $car->frontTyre);
    }

    /**
     * @dataProvider containerProvider
     */
    public function testPostConstruct(Container $container): void
    {
        $car = $this->dependency->inject($container);
        /** @var FakeCar $car */
        $this->assertTrue($car->isConstructed);
    }

    /**
     * @dataProvider containerProvider
     */
    public function testPrototype(Container $container): void
    {
        $this->dependency->setScope(Scope::PROTOTYPE);
        $car1 = $this->dependency->inject($container);
        $car2 = $this->dependency->inject($container);
        assert(is_object($car1) && is_object($car2));
        $this->assertNotSame(spl_object_hash($car1), spl_object_hash($car2));
    }

    /**
     * @dataProvider containerProvider
     */
    public function testSingleton(Container $container): void
    {
        $this->dependency->setScope(Scope::SINGLETON);
        $car1 = $this->dependency->inject($container);
        $car2 = $this->dependency->inject($container);
        assert(is_object($car1) && is_object($car2));
        $this->assertSame(spl_object_hash($car1), spl_object_hash($car2));
    }

    public function testInjectInterceptor(): void
    {
        /** @var ReflectionClass<object> $class */
        $class = new ReflectionClass(FakeAop::class);
        $dependency = new Dependency(new NewInstance($class, new SetterMethods([])));
        $pointcut = new Pointcut((new Matcher())->any(), (new Matcher())->any(), [FakeDoubleInterceptor::class]);
        $dependency->weaveAspects(new Compiler(__DIR__ . '/tmp'), [$pointcut]);
        $container = new Container();
        $container->add((new Bind($container, FakeDoubleInterceptor::class))->to(FakeDoubleInterceptor::class));
        $instance = $dependency->inject($container);
        assert(is_object($instance));
        $isWeave = (new ReflectionClass($instance))->implementsInterface(WeavedInterface::class);
        $this->assertTrue($isWeave);
        assert(property_exists($instance, 'bindings'));
        $this->assertArrayHasKey('returnSame', (array) $instance->bindings);
    }

    /**
     * @dataProvider containerProvider
     * @covers \Ray\Di\Dependency::injectWithArgs
     */
    public function testInjectWithArgsPostConstruct(Container $container): void
    {
        $car = $this->dependency->injectWithArgs($container, [new FakeEngine()]);
        $this->assertInstanceOf(FakeCar::class, $car);
    }

    /**
     * @dataProvider containerProvider
     * @covers \Ray\Di\Dependency::injectWithArgs
     */
    public function testInjectWithArgsSingleton(Container $container): void
    {
        $this->dependency->setScope(Scope::SINGLETON);
        $this->dependency->injectWithArgs($container, [new FakeEngine()]);
        $car = $this->dependency->injectWithArgs($container, [new FakeEngine()]);
        $this->assertInstanceOf(FakeCar::class, $car);
    }
}


================================================
FILE: tests/di/Fake/Annotation/FakeInjectOne.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Annotation;

use Attribute;
use Ray\Di\Di\InjectInterface;
use Ray\Di\Di\Qualifier;

#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER), Qualifier]
final class FakeInjectOne implements InjectInterface
{
    public function isOptional()
    {
        return false;
    }
}


================================================
FILE: tests/di/Fake/Annotation/FakeLeft.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Annotation;

use Attribute;
use Ray\Di\Di\Qualifier;

#[Attribute, Qualifier]
final class FakeLeft
{
}


================================================
FILE: tests/di/Fake/Annotation/FakeNotQualifer.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Annotation;

use Attribute;
use Ray\Di\Di\Qualifier;

#[Attribute]
final class FakeNotQualifer
{
}


================================================
FILE: tests/di/Fake/Annotation/FakeQualifierOnly.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Annotation;

use Attribute;
use Ray\Di\Di\Qualifier;

/**
 * Qualifier-only attribute for testing (no InjectInterface)
 */
#[Attribute(Attribute::TARGET_METHOD), Qualifier]
final class FakeQualifierOnly
{
}


================================================
FILE: tests/di/Fake/Annotation/FakeRight.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di\Annotation;

use Attribute;
use Ray\Di\Di\Qualifier;

#[Attribute, Qualifier]
final class FakeRight
{
}


================================================
FILE: tests/di/Fake/FakeAbstractClass.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

abstract class FakeAbstractClass
{
}


================================================
FILE: tests/di/Fake/FakeAbstractDb.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

class FakeAbstractDb
{
    public $dbId;

    public function __construct($id)
    {
        $this->dbId = $id;
    }
}


================================================
FILE: tests/di/Fake/FakeAnnoClass.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS)]
class FakeAnnoClass
{
    public static $order = [];
}


================================================
FILE: tests/di/Fake/FakeAnnoInterceptor1.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\MethodInterceptor;
use Ray\Aop\MethodInvocation;

class FakeAnnoInterceptor1 implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        FakeAnnoClass::$order[] = self::class;

        return $invocation->proceed();
    }
}


================================================
FILE: tests/di/Fake/FakeAnnoInterceptor2.php
================================================
<?php

declare(strict_types=1);

namespace Ray\Di;

use Ray\Aop\MethodInterceptor;
use Ray\Aop\MethodInvocation;

class FakeAnnoInterceptor2 implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        FakeAnnoClass::$order[] = self::class;

        return $invocation->
Download .txt
gitextract_o85d1j_u/

├── .gitattributes
├── .github/
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature.md
│   │   └── question.md
│   ├── SECURITY.md
│   └── workflows/
│       ├── coding-standards.yml
│       ├── continuous-integration.yml
│       ├── demo.yml
│       ├── prefer-lowest.yml
│       ├── static-analysis.yml
│       └── update-copyright-years-in-license-file.yml
├── .gitignore
├── .scrutinizer.yml
├── .sonarcloud.properties
├── CLAUDE.md
├── LICENSE
├── README.md
├── codecov.yml
├── composer-require-checker.json
├── composer.json
├── demo/
│   ├── 01a-linked-binding.php
│   ├── 01b-linked-binding-setter-injection.php
│   ├── 02-provider-binding.php
│   ├── 02a-named-by-qualifier.php
│   ├── 02b-named-by-named.php
│   ├── 03-injection-point.php
│   ├── 04-untarget-binding.php
│   ├── 05a-constructor-binding.php
│   ├── 05b-constructor-binding-setter-injection.php
│   ├── 07-assisted-injection.php
│   ├── 10-cache.php
│   ├── 11-script-injector.php
│   ├── 12-dependency-chain-error-message.php
│   ├── chain-error/
│   │   ├── A.php
│   │   ├── B.php
│   │   ├── C.php
│   │   ├── D.php
│   │   └── EInterface.php
│   ├── finder/
│   │   ├── Db.php
│   │   ├── DbFinder.php
│   │   ├── DbInterface.php
│   │   ├── Finder.php
│   │   ├── FinderInterface.php
│   │   ├── FinderModule.php
│   │   ├── MovieFinder.php
│   │   ├── MovieLister.php
│   │   ├── MovieListerInterface.php
│   │   └── Sorter.php
│   ├── run.php
│   └── tmp/
│       └── .gitkeep
├── phpcs.xml
├── phpmd.xml
├── phpstan.neon
├── phpunit.xml.dist
├── psalm.xml
├── rector.php
├── src/
│   └── di/
│       ├── AbstractModule.php
│       ├── AcceptInterface.php
│       ├── AnnotatedClass.php
│       ├── AnnotatedClassMethods.php
│       ├── Annotation/
│       │   └── ScriptDir.php
│       ├── Argument.php
│       ├── Arguments.php
│       ├── AspectBind.php
│       ├── AssistedInjectInterceptor.php
│       ├── AssistedInjectModule.php
│       ├── AssistedModule.php
│       ├── Bind.php
│       ├── BindValidator.php
│       ├── BuiltinModule.php
│       ├── CompileNullObject.php
│       ├── Container.php
│       ├── ContainerFactory.php
│       ├── Dependency.php
│       ├── DependencyFactory.php
│       ├── DependencyInterface.php
│       ├── DependencyProvider.php
│       ├── Di/
│       │   ├── Assisted.php
│       │   ├── Inject.php
│       │   ├── InjectInterface.php
│       │   ├── Named.php
│       │   ├── PostConstruct.php
│       │   ├── Qualifier.php
│       │   └── Set.php
│       ├── Exception/
│       │   ├── DirectoryNotWritable.php
│       │   ├── ExceptionInterface.php
│       │   ├── InvalidContext.php
│       │   ├── InvalidProvider.php
│       │   ├── InvalidToConstructorNameParameter.php
│       │   ├── InvalidType.php
│       │   ├── MethodInvocationNotAvailable.php
│       │   ├── NoHint.php
│       │   ├── NotFound.php
│       │   ├── SetNotFound.php
│       │   ├── Unbound.php
│       │   └── Untargeted.php
│       ├── Exception.php
│       ├── Grapher.php
│       ├── InjectableInterface.php
│       ├── InjectionPoint.php
│       ├── InjectionPointInterface.php
│       ├── InjectionPoints.php
│       ├── Injector.php
│       ├── InjectorInterface.php
│       ├── Instance.php
│       ├── Matcher/
│       │   └── AssistedInjectMatcher.php
│       ├── MethodInvocationProvider.php
│       ├── ModuleString.php
│       ├── MultiBinder.php
│       ├── MultiBinding/
│       │   ├── LazyInstance.php
│       │   ├── LazyInterface.php
│       │   ├── LazyProvider.php
│       │   ├── LazyTo.php
│       │   ├── Map.php
│       │   ├── MapProvider.php
│       │   ├── MultiBindingModule.php
│       │   └── MultiBindings.php
│       ├── Name.php
│       ├── NewInstance.php
│       ├── NullDependency.php
│       ├── NullModule.php
│       ├── NullObjectDependency.php
│       ├── ProviderInterface.php
│       ├── ProviderProvider.php
│       ├── ProviderSetModule.php
│       ├── ProviderSetProvider.php
│       ├── Scope.php
│       ├── SetContextInterface.php
│       ├── SetterMethod.php
│       ├── SetterMethods.php
│       ├── SpyCompiler.php
│       ├── Types.php
│       ├── Untarget.php
│       └── VisitorInterface.php
├── src-deprecated/
│   └── di/
│       ├── BcParameterQualifier.php
│       ├── BcStringParser.php
│       ├── EmptyModule.php
│       ├── NullCache.php
│       └── Provider.php
├── tests/
│   ├── bootstrap.php
│   ├── di/
│   │   ├── AnnotatedClassTest.php
│   │   ├── ArgumentTest.php
│   │   ├── ArgumentsTest.php
│   │   ├── AssistedTest.php
│   │   ├── BcParameterQualifierIntegrationTest.php
│   │   ├── BcParameterQualifierTest.php
│   │   ├── BcStringParserTest.php
│   │   ├── BindTest.php
│   │   ├── ContainerTest.php
│   │   ├── ContextualProviderTest.php
│   │   ├── DependencyTest.php
│   │   ├── Fake/
│   │   │   ├── Annotation/
│   │   │   │   ├── FakeInjectOne.php
│   │   │   │   ├── FakeLeft.php
│   │   │   │   ├── FakeNotQualifer.php
│   │   │   │   ├── FakeQualifierOnly.php
│   │   │   │   └── FakeRight.php
│   │   │   ├── FakeAbstractClass.php
│   │   │   ├── FakeAbstractDb.php
│   │   │   ├── FakeAnnoClass.php
│   │   │   ├── FakeAnnoInterceptor1.php
│   │   │   ├── FakeAnnoInterceptor2.php
│   │   │   ├── FakeAnnoInterceptor3.php
│   │   │   ├── FakeAnnoInterceptor4.php
│   │   │   ├── FakeAnnoInterceptor5.php
│   │   │   ├── FakeAnnoInterceptorInterface.php
│   │   │   ├── FakeAnnoMethod1.php
│   │   │   ├── FakeAnnoMethod2.php
│   │   │   ├── FakeAnnoMethod3.php
│   │   │   ├── FakeAnnoModule.php
│   │   │   ├── FakeAnnoOrderClass.php
│   │   │   ├── FakeAop.php
│   │   │   ├── FakeAopDoublyInstallModule.php
│   │   │   ├── FakeAopGrapher.php
│   │   │   ├── FakeAopGrapherModule.php
│   │   │   ├── FakeAopInstallModule.php
│   │   │   ├── FakeAopInterceptorModule.php
│   │   │   ├── FakeAopInterface.php
│   │   │   ├── FakeAopInterfaceModule.php
│   │   │   ├── FakeAopModule.php
│   │   │   ├── FakeAssistedConsumer.php
│   │   │   ├── FakeAssistedDb.php
│   │   │   ├── FakeAssistedDbModule.php
│   │   │   ├── FakeAssistedDbProvider.php
│   │   │   ├── FakeAssistedInjectConsumer.php
│   │   │   ├── FakeAssistedInjectDb.php
│   │   │   ├── FakeAssistedParamsConsumer.php
│   │   │   ├── FakeBcConstructorQualifierClass.php
│   │   │   ├── FakeBcParameterQualifierClass.php
│   │   │   ├── FakeBcParameterQualifierModule.php
│   │   │   ├── FakeBuiltin.php
│   │   │   ├── FakeCar.php
│   │   │   ├── FakeCarEngine.php
│   │   │   ├── FakeCarEngineModule.php
│   │   │   ├── FakeCarInterface.php
│   │   │   ├── FakeCarModule.php
│   │   │   ├── FakeClassInstanceBindModule.php
│   │   │   ├── FakeClassWithBcParameterQualifier.php
│   │   │   ├── FakeConcreteClass.php
│   │   │   ├── FakeConstant.php
│   │   │   ├── FakeConstantConsumer.php
│   │   │   ├── FakeConstantInterface.php
│   │   │   ├── FakeConstantModule.php
│   │   │   ├── FakeContextualModule.php
│   │   │   ├── FakeContextualProvider.php
│   │   │   ├── FakeContextualRobot.php
│   │   │   ├── FakeDoubleInterceptor.php
│   │   │   ├── FakeDoubleInterceptorInterface.php
│   │   │   ├── FakeEngine.php
│   │   │   ├── FakeEngine2.php
│   │   │   ├── FakeEngine3.php
│   │   │   ├── FakeEngineInterface.php
│   │   │   ├── FakeEngineProvider.php
│   │   │   ├── FakeEngineToProviderModule.php
│   │   │   ├── FakeFormerBindingHasPriorityModule.php
│   │   │   ├── FakeGearStickInject.php
│   │   │   ├── FakeGearStickInterface.php
│   │   │   ├── FakeGearStickProvider.php
│   │   │   ├── FakeHandle.php
│   │   │   ├── FakeHandleBar.php
│   │   │   ├── FakeHandleBarQualifier.php
│   │   │   ├── FakeHandleInterface.php
│   │   │   ├── FakeHandleProvider.php
│   │   │   ├── FakeHardtop.php
│   │   │   ├── FakeHardtopInterface.php
│   │   │   ├── FakeInjectionPoint.php
│   │   │   ├── FakeInstallModule.php
│   │   │   ├── FakeInstanceBindModule.php
│   │   │   ├── FakeInstanceBindModule2.php
│   │   │   ├── FakeInstanceBindModuleOneTo3.php
│   │   │   ├── FakeInternalTypeModule.php
│   │   │   ├── FakeInternalTypes.php
│   │   │   ├── FakeLeatherGearStick.php
│   │   │   ├── FakeLeft.php
│   │   │   ├── FakeLeftLeg.php
│   │   │   ├── FakeLegInterface.php
│   │   │   ├── FakeLogStringModule.php
│   │   │   ├── FakeMirrorInterface.php
│   │   │   ├── FakeMirrorLeft.php
│   │   │   ├── FakeMirrorRight.php
│   │   │   ├── FakeModuleInModule.php
│   │   │   ├── FakeModuleInModuleOverride.php
│   │   │   ├── FakeMultiBindingAnnotation.php
│   │   │   ├── FakeMultiBindingConsumer.php
│   │   │   ├── FakeOnionInterceptor2.php
│   │   │   ├── FakeOnionInterceptor3.php
│   │   │   ├── FakeOnionInterceptor4.php
│   │   │   ├── FakeOpenCarModule.php
│   │   │   ├── FakeOverrideInstallModule.php
│   │   │   ├── FakePdoModule.php
│   │   │   ├── FakePhp8Car.php
│   │   │   ├── FakePhp8CarModule.php
│   │   │   ├── FakePhp8HandleProvider.php
│   │   │   ├── FakePriorityModule.php
│   │   │   ├── FakePropConstruct.php
│   │   │   ├── FakeRenameModule.php
│   │   │   ├── FakeRight.php
│   │   │   ├── FakeRightLeg.php
│   │   │   ├── FakeRobot.php
│   │   │   ├── FakeRobotInterface.php
│   │   │   ├── FakeRobotProvider.php
│   │   │   ├── FakeRobotTeam.php
│   │   │   ├── FakeSet.php
│   │   │   ├── FakeSetNotFoundWithMap.php
│   │   │   ├── FakeSetNotFoundWithProvider.php
│   │   │   ├── FakeToBindInvalidClassModule.php
│   │   │   ├── FakeToBindModule.php
│   │   │   ├── FakeToBindSingletonModule.php
│   │   │   ├── FakeToConstructorRobot.php
│   │   │   ├── FakeToProviderBindModule.php
│   │   │   ├── FakeToProviderSingletonBindModule.php
│   │   │   ├── FakeTyre.php
│   │   │   ├── FakeTyreInterface.php
│   │   │   ├── FakeUnNamedClass.php
│   │   │   ├── FakeUnNamedModule.php
│   │   │   ├── FakeUntarget.php
│   │   │   ├── FakeUntargetChild.php
│   │   │   ├── FakeUntargetModule.php
│   │   │   ├── FakeUntargetToIntanceModule.php
│   │   │   ├── FakeWalkRobot.php
│   │   │   ├── FakeWalkRobotLegProvider.php
│   │   │   ├── FakeWalkRobotModule.php
│   │   │   ├── FakelNoConstructorCallModule.php
│   │   │   └── NullVisitor.php
│   │   ├── GrapherTest.php
│   │   ├── InjectionPointTest.php
│   │   ├── InjectionPointsTest.php
│   │   ├── InjectorTest.php
│   │   ├── ModuleMergerTest.php
│   │   ├── ModuleTest.php
│   │   ├── MultiBinding/
│   │   │   ├── MultiBinderTest.php
│   │   │   └── MultiBindingModuleTest.php
│   │   ├── NameTest.php
│   │   ├── NewInstanceTest.php
│   │   ├── NoHintTest.php
│   │   ├── NullModuleTest.php
│   │   ├── ProviderProviderTest.php
│   │   ├── SetterMethodTest.php
│   │   ├── SetterMethodsTest.php
│   │   ├── SpyCompilerTest.php
│   │   ├── UnboundTest.php
│   │   ├── VisitorTest.php
│   │   └── script/
│   │       ├── aop.php
│   │       ├── bench.php
│   │       └── grapher.php
│   ├── script/
│   │   └── aop.php.cache
│   ├── stub/
│   │   └── BindInterface.phpstub
│   └── type/
│       └── InjectorInterfaceTest.php
├── tests-php8/
│   ├── AssistedInjectTest.php
│   └── DualReaderTest.php
└── vendor-bin/
    └── tools/
        └── composer.json
Download .txt
SYMBOL INDEX (940 symbols across 273 files)

FILE: demo/01a-linked-binding.php
  type FinderInterface (line 10) | interface FinderInterface
  class Finder (line 14) | class Finder implements FinderInterface
  type MovieListerInterface (line 18) | interface MovieListerInterface
  class MovieLister (line 22) | class MovieLister implements MovieListerInterface
    method __construct (line 24) | public function __construct(
  class FinderModule (line 29) | class FinderModule extends AbstractModule
    method configure (line 31) | protected function configure()

FILE: demo/01b-linked-binding-setter-injection.php
  type FinderInterface (line 11) | interface FinderInterface
  class Finder (line 15) | class Finder implements FinderInterface
  type MovieListerInterface (line 19) | interface MovieListerInterface
  class MovieLister (line 23) | class MovieLister implements MovieListerInterface
    method setFinder (line 27) | #[Inject]
  class FinderModule (line 34) | class FinderModule extends AbstractModule
    method configure (line 36) | protected function configure()

FILE: demo/02-provider-binding.php
  type FinderInterface (line 11) | interface FinderInterface
  class Finder (line 15) | class Finder implements FinderInterface
    method __construct (line 19) | public function __construct(DateTimeInterface $dateTime)
  class FinderProvider (line 25) | class FinderProvider implements ProviderInterface
    method get (line 30) | public function get()
  type MovieListerInterface (line 36) | interface MovieListerInterface
  class MovieLister (line 40) | class MovieLister implements MovieListerInterface
    method __construct (line 42) | public function __construct(
  class FinderModule (line 48) | class FinderModule extends AbstractModule
    method configure (line 50) | protected function configure()

FILE: demo/02a-named-by-qualifier.php
  type FinderInterface (line 11) | interface FinderInterface
  class LegacyFinder (line 15) | class LegacyFinder implements FinderInterface
  type MovieListerInterface (line 19) | interface MovieListerInterface
  class MovieLister (line 23) | class MovieLister implements MovieListerInterface
    method __construct (line 27) | public function __construct(#[Legacy] FinderInterface $finder)
  class Legacy (line 33) | #[Attribute(Attribute::TARGET_PARAMETER), Qualifier]
  class FinderModule (line 38) | class FinderModule extends AbstractModule
    method configure (line 40) | protected function configure()

FILE: demo/02b-named-by-named.php
  type FinderInterface (line 11) | interface FinderInterface
  class LegacyFinder (line 15) | class LegacyFinder implements FinderInterface
  class ModernFinder (line 19) | class ModernFinder implements FinderInterface
  type MovieListerInterface (line 23) | interface MovieListerInterface
  class MovieLister (line 27) | class MovieLister implements MovieListerInterface
    method __construct (line 29) | public function __construct(
  class FinderModule (line 34) | class FinderModule extends AbstractModule
    method configure (line 36) | protected function configure()

FILE: demo/03-injection-point.php
  type FinderInterface (line 12) | interface FinderInterface
  type MovieListerInterface (line 16) | interface MovieListerInterface
  class Finder (line 20) | class Finder implements FinderInterface
    method __construct (line 24) | public function __construct(string $className)
    method find (line 29) | public function find(): string
  class MovieLister (line 35) | class MovieLister implements MovieListerInterface
    method __construct (line 39) | public function __construct(FinderInterface $finder)
  class FinderProvider (line 45) | class FinderProvider implements ProviderInterface
    method __construct (line 47) | public function __construct(
    method get (line 55) | public function get()
  class FinderModule (line 63) | class FinderModule extends AbstractModule
    method configure (line 65) | protected function configure()

FILE: demo/04-untarget-binding.php
  class Finder (line 11) | class Finder
  class FinderModule (line 15) | class FinderModule extends AbstractModule
    method configure (line 17) | protected function configure()

FILE: demo/05a-constructor-binding.php
  class PdoModuleLegacyNaming (line 12) | class PdoModuleLegacyNaming extends AbstractModule
    method configure (line 14) | protected function configure()
  class PdoModule (line 22) | class PdoModule extends AbstractModule
    method configure (line 24) | protected function configure()

FILE: demo/05b-constructor-binding-setter-injection.php
  type FinderInterface (line 17) | interface FinderInterface
  type MovieListerInterface (line 20) | interface MovieListerInterface
  class Finder (line 24) | class Finder implements FinderInterface
  class MovieLister (line 28) | class MovieLister implements MovieListerInterface
    method setFinder (line 35) | public function setFinder(FinderInterface $finder)
  class ListerModule (line 41) | class ListerModule extends AbstractModule
    method configure (line 43) | protected function configure()

FILE: demo/07-assisted-injection.php
  method configure (line 14) | protected function configure()

FILE: demo/12-dependency-chain-error-message.php
  class DeepLinkedClassBindingModule (line 14) | class DeepLinkedClassBindingModule extends AbstractModule
    method configure (line 16) | protected function configure()

FILE: demo/chain-error/A.php
  class A (line 5) | class A
    method __construct (line 7) | public function __construct(B $b)

FILE: demo/chain-error/B.php
  class B (line 5) | class B
    method __construct (line 7) | public function __construct(C $c)

FILE: demo/chain-error/C.php
  class C (line 5) | class C
    method __construct (line 7) | public function __construct(D $d)

FILE: demo/chain-error/D.php
  class D (line 5) | class D
    method __construct (line 7) | public function __construct(EInterface $e)

FILE: demo/chain-error/EInterface.php
  type EInterface (line 5) | interface EInterface

FILE: demo/finder/Db.php
  class Db (line 7) | class Db implements DbInterface
    method __construct (line 9) | public function __construct($dsn, $username, $password)
    method init (line 13) | #[PostConstruct]

FILE: demo/finder/DbFinder.php
  class DbFinder (line 7) | class DbFinder implements FinderInterface
    method __construct (line 9) | public function __construct(DbInterface $db)
    method setDb (line 13) | #[Inject]
    method setSorter (line 18) | #[Inject]

FILE: demo/finder/DbInterface.php
  type DbInterface (line 5) | interface DbInterface

FILE: demo/finder/Finder.php
  class Finder (line 5) | class Finder implements FinderInterface

FILE: demo/finder/FinderInterface.php
  type FinderInterface (line 5) | interface FinderInterface

FILE: demo/finder/FinderModule.php
  class FinderModule (line 8) | class FinderModule extends AbstractModule
    method configure (line 10) | protected function configure()

FILE: demo/finder/MovieFinder.php
  class MovieFinder (line 8) | class MovieFinder
    method find (line 10) | public function find($name, #[Inject] ?FinderInterface $finder = null)

FILE: demo/finder/MovieLister.php
  class MovieLister (line 7) | class MovieLister implements MovieListerInterface
    method __construct (line 9) | public function __construct(FinderInterface $finder)
    method setFinder01 (line 13) | #[Inject]

FILE: demo/finder/MovieListerInterface.php
  type MovieListerInterface (line 5) | interface MovieListerInterface

FILE: demo/finder/Sorter.php
  class Sorter (line 5) | class Sorter

FILE: src-deprecated/di/BcParameterQualifier.php
  class BcParameterQualifier (line 42) | final class BcParameterQualifier
    method getNames (line 57) | public static function getNames(ReflectionMethod $method): array
    method getQualifier (line 87) | private static function getQualifier(ReflectionMethod $method): string
    method hasParameterQualifier (line 136) | private static function hasParameterQualifier(array $attributes): bool

FILE: src-deprecated/di/BcStringParser.php
  class BcStringParser (line 21) | final class BcStringParser
    method parse (line 30) | public static function parse(string $name): array

FILE: src-deprecated/di/EmptyModule.php
  class EmptyModule (line 10) | class EmptyModule extends AbstractModule
    method configure (line 15) | protected function configure() : void

FILE: src-deprecated/di/NullCache.php
  class NullCache (line 13) | final class NullCache extends CacheProvider
    method doFetch (line 18) | protected function doFetch($id)
    method doContains (line 26) | protected function doContains($id)
    method doSave (line 34) | protected function doSave($id, $data, $lifeTime = 0)
    method doDelete (line 42) | protected function doDelete($id)
    method doFlush (line 50) | protected function doFlush()
    method doGetStats (line 58) | protected function doGetStats()

FILE: src-deprecated/di/Provider.php
  type Provider (line 10) | interface Provider extends ProviderInterface

FILE: src/di/AbstractModule.php
  class AbstractModule (line 22) | abstract class AbstractModule implements Stringable
    method __construct (line 31) | public function __construct(
    method __toString (line 41) | public function __toString(): string
    method install (line 49) | public function install(self $module): void
    method override (line 57) | public function override(self $module): void
    method getContainer (line 66) | public function getContainer(): Container
    method bindInterceptor (line 82) | public function bindInterceptor(AbstractMatcher $classMatcher, Abstrac...
    method bindPriorityInterceptor (line 103) | public function bindPriorityInterceptor(AbstractMatcher $classMatcher,...
    method rename (line 120) | public function rename(string $interface, string $newName, string $sou...
    method configure (line 135) | abstract protected function configure();
    method bind (line 142) | protected function bind(string $interface = ''): Bind
    method activate (line 150) | private function activate(): void

FILE: src/di/AcceptInterface.php
  type AcceptInterface (line 10) | interface AcceptInterface
    method accept (line 19) | public function accept(VisitorInterface $visitor);

FILE: src/di/AnnotatedClass.php
  class AnnotatedClass (line 11) | final class AnnotatedClass
    method __construct (line 15) | public function __construct()
    method getNewInstance (line 25) | public function getNewInstance(ReflectionClass $class): NewInstance
    method getPostConstruct (line 47) | public function getPostConstruct(ReflectionClass $class): ?ReflectionM...

FILE: src/di/AnnotatedClassMethods.php
  class AnnotatedClassMethods (line 12) | final class AnnotatedClassMethods
    method getConstructorName (line 17) | public function getConstructorName(ReflectionClass $class): Name
    method getSetterMethod (line 33) | public function getSetterMethod(ReflectionMethod $method): ?SetterMethod
    method getName (line 49) | private function getName(ReflectionMethod $method): Name

FILE: src/di/Annotation/ScriptDir.php
  class ScriptDir (line 15) | #[Attribute, Qualifier]

FILE: src/di/Argument.php
  class Argument (line 20) | final class Argument implements AcceptInterface, Stringable
    method __construct (line 33) | public function __construct(ReflectionParameter $parameter, string $name)
    method __toString (line 60) | public function __toString(): string
    method get (line 68) | public function get(): ReflectionParameter
    method isDefaultAvailable (line 73) | public function isDefaultAvailable(): bool
    method getDefaultValue (line 81) | public function getDefaultValue()
    method getMeta (line 86) | public function getMeta(): string
    method __serialize (line 94) | public function __serialize(): array
    method __unserialize (line 116) | public function __unserialize(array $unserialized): void
    method accept (line 129) | public function accept(VisitorInterface $visitor): void
    method setDefaultValue (line 139) | private function setDefaultValue(ReflectionParameter $parameter): void
    method getType (line 157) | private function getType(ReflectionParameter $parameter): string

FILE: src/di/Arguments.php
  class Arguments (line 17) | final class Arguments implements AcceptInterface
    method __construct (line 22) | public function __construct(ReflectionMethod $method, Name $name)
    method inject (line 37) | public function inject(Container $container): array
    method accept (line 48) | public function accept(VisitorInterface $visitor): void
    method getParameter (line 58) | private function getParameter(Container $container, Argument $argument)
    method bindInjectionPoint (line 76) | private function bindInjectionPoint(Container $container, Argument $ar...
    method getNoHintMsg (line 86) | private function getNoHintMsg(Argument $argument): string

FILE: src/di/AspectBind.php
  class AspectBind (line 15) | final class AspectBind implements AcceptInterface
    method __construct (line 17) | public function __construct(private AopBind $bind)
    method inject (line 26) | public function inject(Container $container): array
    method accept (line 46) | public function accept(VisitorInterface $visitor): void

FILE: src/di/AssistedInjectInterceptor.php
  class AssistedInjectInterceptor (line 32) | final class AssistedInjectInterceptor implements MethodInterceptor
    method __construct (line 34) | public function __construct(private InjectorInterface $injector, priva...
    method invoke (line 41) | public function invoke(MethodInvocation $invocation)
    method getNamedArguments (line 68) | private function getNamedArguments(MethodInvocation $invocation): array
    method getDependency (line 87) | private function getDependency(ReflectionParameter $param)
    method getName (line 99) | private function getName(ReflectionParameter $param): ?string
    method getCustomInject (line 120) | private function getCustomInject(ReflectionParameter $param): ?string

FILE: src/di/AssistedInjectModule.php
  class AssistedInjectModule (line 12) | final class AssistedInjectModule extends AbstractModule
    method configure (line 14) | protected function configure(): void

FILE: src/di/AssistedModule.php
  class AssistedModule (line 16) | final class AssistedModule extends AbstractModule
    method configure (line 18) | protected function configure(): void

FILE: src/di/Bind.php
  class Bind (line 22) | final class Bind implements Stringable
    method __construct (line 36) | public function __construct(
    method __destruct (line 54) | public function __destruct()
    method __toString (line 65) | public function __toString(): string
    method annotatedWith (line 75) | public function annotatedWith(string $name): self
    method to (line 87) | public function to(string $class): self
    method toConstructor (line 107) | public function toConstructor(string $class, string|array $name, ?Inje...
    method toProvider (line 124) | public function toProvider(string $provider, string $context = ''): self
    method toInstance (line 139) | public function toInstance($instance): self
    method toNull (line 151) | public function toNull(): self
    method in (line 164) | public function in(string $scope): self
    method getBound (line 177) | public function getBound(): DependencyInterface
    method setBound (line 182) | public function setBound(DependencyInterface $bound): void
    method isRegistered (line 187) | private function isRegistered(string $interface): bool

FILE: src/di/BindValidator.php
  class BindValidator (line 18) | final class BindValidator
    method constructor (line 20) | public function constructor(string $interface): void
    method to (line 36) | public function to(string $interface, string $class): ReflectionClass
    method toProvider (line 59) | public function toProvider(string $provider): ReflectionClass
    method isNullInterceptorBinding (line 74) | private function isNullInterceptorBinding(string $class, string $inter...

FILE: src/di/BuiltinModule.php
  class BuiltinModule (line 9) | final class BuiltinModule
    method __invoke (line 11) | public function __invoke(AbstractModule $module): AbstractModule

FILE: src/di/CompileNullObject.php
  class CompileNullObject (line 10) | final class CompileNullObject
    method __invoke (line 12) | public function __invoke(Container $container, string $scriptDir): void

FILE: src/di/Container.php
  class Container (line 30) | final class Container implements InjectorInterface
    method __construct (line 41) | public function __construct()
    method __sleep (line 49) | public function __sleep()
    method add (line 57) | public function add(Bind $bind): void
    method addPointcut (line 66) | public function addPointcut(Pointcut $pointcut): void
    method getInstance (line 81) | public function getInstance($interface, $name = Name::ANY)
    method getInstanceWithArgs (line 96) | public function getInstanceWithArgs(string $interface, array $params)
    method getDependency (line 120) | public function getDependency(string $index)
    method move (line 132) | public function move(string $sourceInterface, string $sourceName, stri...
    method unbound (line 149) | public function unbound(string $index): Untargeted|Unbound
    method getContainer (line 169) | public function getContainer(): array
    method getPointcuts (line 180) | public function getPointcuts(): array
    method merge (line 188) | public function merge(self $container): void
    method weaveAspects (line 198) | public function weaveAspects(CompilerInterface $compiler): void
    method weaveAspect (line 214) | public function weaveAspect(Compiler $compiler, Dependency $dependency...
    method map (line 224) | public function map(callable $f): void
    method sort (line 231) | public function sort(): void

FILE: src/di/ContainerFactory.php
  class ContainerFactory (line 15) | final class ContainerFactory
    method __invoke (line 21) | public function __invoke($module, string $classDir): Container
    method getModule (line 39) | private function getModule($module): AbstractModule

FILE: src/di/Dependency.php
  class Dependency (line 22) | final class Dependency implements DependencyInterface, AcceptInterface
    method __construct (line 34) | public function __construct(NewInstance $newInstance, ?ReflectionMetho...
    method __sleep (line 43) | public function __sleep()
    method __toString (line 48) | public function __toString(): string
    method register (line 59) | public function register(array &$container, Bind $bind): void
    method inject (line 67) | public function inject(Container $container)
    method injectWithArgs (line 91) | public function injectWithArgs(Container $container, array $params)
    method setScope (line 113) | public function setScope($scope): void
    method weaveAspects (line 123) | public function weaveAspects(CompilerInterface $compiler, array $point...
    method accept (line 148) | public function accept(VisitorInterface $visitor)
    method isSingleton (line 157) | public function isSingleton(): bool

FILE: src/di/DependencyFactory.php
  class DependencyFactory (line 13) | final class DependencyFactory
    method newAnnotatedDependency (line 20) | public function newAnnotatedDependency(ReflectionClass $class): Depend...
    method newProvider (line 34) | public function newProvider(ReflectionClass $provider, string $context...
    method newToConstructor (line 47) | public function newToConstructor(

FILE: src/di/DependencyInterface.php
  type DependencyInterface (line 12) | interface DependencyInterface
    method __toString (line 17) | public function __toString();
    method inject (line 24) | public function inject(Container $container);
    method register (line 35) | public function register(array &$container, Bind $bind);
    method setScope (line 44) | public function setScope($scope);

FILE: src/di/DependencyProvider.php
  class DependencyProvider (line 10) | final class DependencyProvider implements DependencyInterface, AcceptInt...
    method __construct (line 17) | public function __construct(
    method __sleep (line 29) | public function __sleep()
    method __toString (line 34) | public function __toString(): string
    method register (line 45) | public function register(array &$container, Bind $bind): void
    method inject (line 53) | public function inject(Container $container)
    method setScope (line 73) | public function setScope($scope): void
    method setContext (line 80) | public function setContext(SetContextInterface $provider): void
    method isSingleton (line 85) | public function isSingleton(): bool
    method accept (line 91) | public function accept(VisitorInterface $visitor)

FILE: src/di/Di/Assisted.php
  class Assisted (line 15) | #[Attribute(Attribute::TARGET_PARAMETER)]

FILE: src/di/Di/Inject.php
  class Inject (line 14) | #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER)]
    method __construct (line 20) | public function __construct(
    method isOptional (line 31) | public function isOptional(): bool

FILE: src/di/Di/InjectInterface.php
  type InjectInterface (line 7) | interface InjectInterface
    method isOptional (line 14) | public function isOptional();

FILE: src/di/Di/Named.php
  class Named (line 14) | #[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_METHOD | Att...
    method __construct (line 17) | public function __construct(public string $value)

FILE: src/di/Di/PostConstruct.php
  class PostConstruct (line 21) | #[Attribute(Attribute::TARGET_METHOD)]

FILE: src/di/Di/Qualifier.php
  class Qualifier (line 12) | #[Attribute(Attribute::TARGET_CLASS)]

FILE: src/di/Di/Set.php
  class Set (line 13) | #[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_PROPERTY)]
    method __construct (line 19) | public function __construct(public string $interface, public string $n...

FILE: src/di/Exception.php
  class Exception (line 7) | final class Exception extends \Exception

FILE: src/di/Exception/DirectoryNotWritable.php
  class DirectoryNotWritable (line 9) | final class DirectoryNotWritable extends RuntimeException implements Exc...

FILE: src/di/Exception/ExceptionInterface.php
  type ExceptionInterface (line 7) | interface ExceptionInterface

FILE: src/di/Exception/InvalidContext.php
  class InvalidContext (line 9) | final class InvalidContext extends InvalidArgumentException implements E...

FILE: src/di/Exception/InvalidProvider.php
  class InvalidProvider (line 9) | final class InvalidProvider extends InvalidArgumentException implements ...

FILE: src/di/Exception/InvalidToConstructorNameParameter.php
  class InvalidToConstructorNameParameter (line 12) | final class InvalidToConstructorNameParameter extends InvalidArgumentExc...

FILE: src/di/Exception/InvalidType.php
  class InvalidType (line 9) | final class InvalidType extends InvalidArgumentException implements Exce...

FILE: src/di/Exception/MethodInvocationNotAvailable.php
  class MethodInvocationNotAvailable (line 7) | final class MethodInvocationNotAvailable extends Unbound

FILE: src/di/Exception/NoHint.php
  class NoHint (line 15) | final class NoHint extends Unbound

FILE: src/di/Exception/NotFound.php
  class NotFound (line 9) | final class NotFound extends LogicException implements ExceptionInterface

FILE: src/di/Exception/SetNotFound.php
  class SetNotFound (line 9) | final class SetNotFound extends LogicException implements ExceptionInter...

FILE: src/di/Exception/Unbound.php
  class Unbound (line 25) | class Unbound extends LogicException implements ExceptionInterface
    method __toString (line 27) | public function __toString(): string
    method buildMessage (line 45) | private function buildMessage(self $e, array $msg): string
    method getMainMessage (line 63) | private function getMainMessage(self $e): string

FILE: src/di/Exception/Untargeted.php
  class Untargeted (line 7) | final class Untargeted extends Unbound

FILE: src/di/Grapher.php
  class Grapher (line 18) | final class Grapher
    method __construct (line 26) | public function __construct(AbstractModule $module, private string $cl...
    method __wakeup (line 40) | public function __wakeup()
    method newInstanceArgs (line 60) | public function newInstanceArgs(string $class, array $params)

FILE: src/di/InjectableInterface.php
  type InjectableInterface (line 7) | interface InjectableInterface

FILE: src/di/InjectionPoint.php
  class InjectionPoint (line 15) | final class InjectionPoint implements InjectionPointInterface
    method __construct (line 25) | public function __construct(private ReflectionParameter $parameter)
    method getParameter (line 36) | public function getParameter(): ReflectionParameter
    method getMethod (line 44) | public function getMethod(): ReflectionMethod
    method getClass (line 58) | public function getClass(): ReflectionClass
    method getQualifiers (line 70) | public function getQualifiers(): array
    method __serialize (line 87) | public function __serialize(): array
    method __unserialize (line 95) | public function __unserialize(array $array): void

FILE: src/di/InjectionPointInterface.php
  type InjectionPointInterface (line 14) | interface InjectionPointInterface
    method getParameter (line 19) | public function getParameter(): ReflectionParameter;
    method getMethod (line 24) | public function getMethod(): ReflectionMethod;
    method getClass (line 32) | public function getClass(): ReflectionClass;
    method getQualifiers (line 39) | public function getQualifiers(): array;

FILE: src/di/InjectionPoints.php
  class InjectionPoints (line 14) | final class InjectionPoints
    method __invoke (line 28) | public function __invoke(string $class): SetterMethods
    method addMethod (line 38) | public function addMethod(string $method, string $name = Name::ANY): self
    method addOptionalMethod (line 45) | public function addOptionalMethod(string $method, string $name = Name:...
    method getSetterMethod (line 58) | private function getSetterMethod(string $class, array $point): SetterM...

FILE: src/di/Injector.php
  class Injector (line 25) | final class Injector implements InjectorInterface
    method __construct (line 35) | public function __construct($module = null, string $tmpDir = '')
    method __wakeup (line 53) | public function __wakeup()
    method getInstance (line 68) | public function getInstance($interface, $name = Name::ANY)
    method bind (line 87) | private function bind(string $class): void

FILE: src/di/InjectorInterface.php
  type InjectorInterface (line 14) | interface InjectorInterface
    method getInstance (line 26) | public function getInstance($interface, $name = Name::ANY);

FILE: src/di/Instance.php
  class Instance (line 12) | final class Instance implements DependencyInterface, AcceptInterface
    method __construct (line 17) | public function __construct(public $value)
    method __toString (line 21) | public function __toString(): string
    method register (line 41) | public function register(array &$container, Bind $bind): void
    method inject (line 50) | public function inject(Container $container)
    method setScope (line 60) | public function setScope($scope): void
    method accept (line 65) | public function accept(VisitorInterface $visitor)

FILE: src/di/Matcher/AssistedInjectMatcher.php
  class AssistedInjectMatcher (line 15) | final class AssistedInjectMatcher extends AbstractMatcher
    method matchesClass (line 22) | public function matchesClass(ReflectionClass $class, array $arguments)...
    method matchesMethod (line 30) | public function matchesMethod(ReflectionMethod $method, array $argumen...

FILE: src/di/MethodInvocationProvider.php
  class MethodInvocationProvider (line 13) | final class MethodInvocationProvider implements ProviderInterface
    method set (line 21) | public function set(MethodInvocation $invocation): void
    method get (line 29) | public function get(): MethodInvocation

FILE: src/di/ModuleString.php
  class ModuleString (line 19) | final class ModuleString
    method __invoke (line 24) | public function __invoke(Container $container, array $pointcuts): string

FILE: src/di/MultiBinder.php
  class MultiBinder (line 17) | final class MultiBinder
    method __construct (line 25) | private function __construct(AbstractModule $module, private readonly ...
    method newInstance (line 34) | public static function newInstance(AbstractModule $module, string $int...
    method addBinding (line 39) | public function addBinding(?string $key = null): self
    method setBinding (line 46) | public function setBinding(?string $key = null): self
    method to (line 57) | public function to(string $class): void
    method toProvider (line 67) | public function toProvider(string $provider): void
    method toInstance (line 75) | public function toInstance($instance): void
    method bind (line 80) | private function bind(LazyInterface $lazy, ?string $key): void

FILE: src/di/MultiBinding/LazyInstance.php
  class LazyInstance (line 13) | final class LazyInstance implements LazyInterface
    method __construct (line 18) | public function __construct(private $instance)
    method __invoke (line 25) | public function __invoke(InjectorInterface $injector)

FILE: src/di/MultiBinding/LazyInterface.php
  type LazyInterface (line 9) | interface LazyInterface
    method __invoke (line 14) | public function __invoke(InjectorInterface $injector);

FILE: src/di/MultiBinding/LazyProvider.php
  class LazyProvider (line 13) | final class LazyProvider implements LazyInterface
    method __construct (line 18) | public function __construct(private string $class)
    method __invoke (line 25) | public function __invoke(InjectorInterface $injector)

FILE: src/di/MultiBinding/LazyTo.php
  class LazyTo (line 12) | final class LazyTo implements LazyInterface
    method __construct (line 17) | public function __construct(private string $class)
    method __invoke (line 24) | public function __invoke(InjectorInterface $injector)

FILE: src/di/MultiBinding/Map.php
  class Map (line 24) | final class Map implements IteratorAggregate, ArrayAccess, Countable
    method __construct (line 29) | public function __construct(private array $lazies, private readonly In...
    method offsetExists (line 38) | #[ReturnTypeWillChange]
    method offsetGet (line 51) | #[ReturnTypeWillChange]
    method offsetSet (line 68) | #[ReturnTypeWillChange]
    method offsetUnset (line 83) | #[ReturnTypeWillChange]
    method getIterator (line 92) | public function getIterator(): Iterator
    method count (line 102) | public function count(): int

FILE: src/di/MultiBinding/MapProvider.php
  class MapProvider (line 16) | final class MapProvider implements ProviderInterface
    method __construct (line 18) | public function __construct(private readonly InjectionPointInterface $...
    method get (line 25) | public function get(): Map

FILE: src/di/MultiBinding/MultiBindingModule.php
  class MultiBindingModule (line 13) | final class MultiBindingModule extends AbstractModule
    method configure (line 15) | protected function configure(): void

FILE: src/di/MultiBinding/MultiBindings.php
  class MultiBindings (line 16) | final class MultiBindings extends ArrayObject
    method merge (line 18) | public function merge(self $multiBindings): void

FILE: src/di/Name.php
  class Name (line 22) | final class Name
    method __construct (line 43) | public function __construct(string|array $name)
    method withAttributes (line 59) | public static function withAttributes(ReflectionMethod $method): ?self
    method getName (line 93) | private static function getName(array $attributes): string
    method __invoke (line 109) | public function __invoke(ReflectionParameter $parameter): string
    method setName (line 122) | private function setName(string $name): void

FILE: src/di/NewInstance.php
  class NewInstance (line 18) | final class NewInstance implements Stringable
    method __construct (line 31) | public function __construct(
    method __invoke (line 49) | public function __invoke(Container $container): object
    method __toString (line 61) | public function __toString(): string
    method newInstanceArgs (line 71) | public function newInstanceArgs(Container $container, array $params): ...
    method weaveAspects (line 81) | public function weaveAspects(string $class, AopBind $bind): void
    method accept (line 87) | public function accept(VisitorInterface $visitor): void
    method postNewInstance (line 97) | private function postNewInstance(Container $container, object $instanc...
    method enableAop (line 107) | public function enableAop(object $instance, Container $container): void

FILE: src/di/NullDependency.php
  class NullDependency (line 10) | final class NullDependency implements DependencyInterface
    method __toString (line 15) | public function __toString(): string
    method inject (line 23) | public function inject(Container $container): void
    method register (line 30) | public function register(array &$container, Bind $bind): void
    method setScope (line 38) | public function setScope($scope): void

FILE: src/di/NullModule.php
  class NullModule (line 7) | final class NullModule extends AbstractModule
    method configure (line 9) | protected function configure(): void

FILE: src/di/NullObjectDependency.php
  class NullObjectDependency (line 16) | final class NullObjectDependency implements DependencyInterface
    method __construct (line 21) | public function __construct(private string $interface)
    method __toString (line 28) | public function __toString(): string
    method inject (line 36) | public function inject(Container $container): null
    method register (line 44) | public function register(array &$container, Bind $bind): void
    method setScope (line 52) | public function setScope($scope): void
    method toNull (line 56) | public function toNull(string $scriptDir): Dependency

FILE: src/di/ProviderInterface.php
  type ProviderInterface (line 10) | interface ProviderInterface
    method get (line 15) | public function get();

FILE: src/di/ProviderProvider.php
  class ProviderProvider (line 13) | final class ProviderProvider implements ProviderInterface
    method __construct (line 16) | public function __construct(private InjectorInterface $injector, priva...
    method get (line 21) | public function get()

FILE: src/di/ProviderSetModule.php
  class ProviderSetModule (line 7) | final class ProviderSetModule extends AbstractModule
    method configure (line 9) | protected function configure(): void

FILE: src/di/ProviderSetProvider.php
  class ProviderSetProvider (line 14) | final class ProviderSetProvider implements ProviderInterface
    method __construct (line 16) | public function __construct(private InjectionPointInterface $ip, priva...
    method get (line 21) | public function get(): ProviderProvider

FILE: src/di/Scope.php
  class Scope (line 7) | final class Scope

FILE: src/di/SetContextInterface.php
  type SetContextInterface (line 7) | interface SetContextInterface
    method setContext (line 16) | public function setContext($context);

FILE: src/di/SetterMethod.php
  class SetterMethod (line 15) | final class SetterMethod implements AcceptInterface
    method __construct (line 25) | public function __construct(ReflectionMethod $method, Name $name)
    method __invoke (line 37) | public function __invoke($instance, Container $container): void
    method setOptional (line 57) | public function setOptional(): void
    method accept (line 63) | public function accept(VisitorInterface $visitor): void

FILE: src/di/SetterMethods.php
  class SetterMethods (line 12) | final class SetterMethods implements AcceptInterface
    method __construct (line 17) | public function __construct(private array $setterMethods)
    method __invoke (line 24) | public function __invoke(object $instance, Container $container): void
    method add (line 31) | public function add(?SetterMethod $setterMethod = null): void
    method accept (line 41) | public function accept(VisitorInterface $visitor): void

FILE: src/di/SpyCompiler.php
  class SpyCompiler (line 19) | final class SpyCompiler implements CompilerInterface
    method newInstance (line 29) | public function newInstance(string $class, array $args, BindInterface ...
    method compile (line 45) | public function compile(string $class, BindInterface $bind): string
    method hasNoBinding (line 57) | private function hasNoBinding(string $class, BindInterface $bind): bool
    method hasBoundMethod (line 67) | private function hasBoundMethod(string $class, BindInterface $bind): bool
    method getInterceptors (line 80) | private function getInterceptors(BindInterface $bind): string

FILE: src/di/Types.php
  class Types (line 70) | final class Types

FILE: src/di/Untarget.php
  class Untarget (line 9) | final class Untarget
    method __construct (line 21) | public function __construct(string $class)
    method __invoke (line 29) | public function __invoke(Container $container, Bind $bind): void
    method setScope (line 38) | public function setScope(string $scope): void

FILE: src/di/VisitorInterface.php
  type VisitorInterface (line 16) | interface VisitorInterface
    method visitDependency (line 23) | public function visitDependency(NewInstance $newInstance, ?string $pos...
    method visitProvider (line 30) | public function visitProvider(Dependency $dependency, string $context,...
    method visitInstance (line 39) | public function visitInstance($value);
    method visitAspectBind (line 46) | public function visitAspectBind(AopBind $aopBind);
    method visitNewInstance (line 53) | public function visitNewInstance(
    method visitSetterMethods (line 67) | public function visitSetterMethods(array $setterMethods);
    method visitSetterMethod (line 74) | public function visitSetterMethod(string $method, Arguments $arguments);
    method visitArguments (line 83) | public function visitArguments(array $arguments);
    method visitArgument (line 92) | public function visitArgument(

FILE: tests-php8/AssistedInjectTest.php
  class AssistedInjectTest (line 10) | class AssistedInjectTest extends TestCase
    method setUp (line 15) | protected function setUp(): void
    method testAssisted (line 20) | public function testAssisted(): void
    method testAssistedWithName (line 29) | public function testAssistedWithName(): void
    method testAssistedAnyWithName (line 39) | public function testAssistedAnyWithName(): void
    method testAssistedMethodInvocation (line 50) | public function testAssistedMethodInvocation(): void
    method testAssistedCustomeInject (line 60) | public function testAssistedCustomeInject(): void
    method testConstructorPropertyPromotion (line 73) | public function testConstructorPropertyPromotion(): void

FILE: tests-php8/DualReaderTest.php
  class DualReaderTest (line 11) | class DualReaderTest extends TestCase
    method testPhp8Attribute (line 13) | public function testPhp8Attribute(): FakePhp8Car
    method testNamedParameterInMethod (line 25) | public function testNamedParameterInMethod(FakePhp8Car $car): void
    method testNamedParameterInConstructor (line 36) | public function testNamedParameterInConstructor(FakePhp8Car $car): void
    method testPostConstruct (line 44) | public function testPostConstruct(FakePhp8Car $car): void
    method testCunstomInjectAnnotation (line 52) | public function testCunstomInjectAnnotation(FakePhp8Car $car): void
    method testProviderAttribute (line 60) | public function testProviderAttribute(FakePhp8Car $car): void
    method testCumstomInject (line 69) | public function testCumstomInject(FakePhp8Car $car): void

FILE: tests/di/AnnotatedClassTest.php
  class AnnotatedClassTest (line 11) | class AnnotatedClassTest extends TestCase
    method setUp (line 16) | protected function setUp(): void
    method testInvoke (line 22) | public function testInvoke(): void
    method testAnnotatedByAnnotation (line 50) | public function testAnnotatedByAnnotation(string $class): void
    method classProvider (line 68) | public function classProvider(): array

FILE: tests/di/ArgumentTest.php
  class ArgumentTest (line 15) | class ArgumentTest extends TestCase
    method setUp (line 20) | protected function setUp(): void
    method testToString (line 25) | public function testToString(): void
    method testToStringScalar (line 30) | public function testToStringScalar(): void
    method testSerializable (line 36) | public function testSerializable(): void

FILE: tests/di/ArgumentsTest.php
  class ArgumentsTest (line 13) | class ArgumentsTest extends TestCase
    method setUp (line 18) | protected function setUp(): void
    method testInject (line 23) | public function testInject(): void
    method testParameterDefaultValue (line 33) | public function testParameterDefaultValue(): void

FILE: tests/di/AssistedTest.php
  class AssistedTest (line 10) | class AssistedTest extends TestCase
    method setUp (line 15) | protected function setUp(): void
    method testAssisted (line 20) | public function testAssisted(): void
    method testAssistedWithName (line 28) | public function testAssistedWithName(): void
    method testAssistedAnyWithName (line 37) | public function testAssistedAnyWithName(): void
    method testAssistedMethodInvocation (line 47) | public function testAssistedMethodInvocation(): void
    method testAssistedMethodInvocationNotAvailable (line 56) | public function testAssistedMethodInvocationNotAvailable(): void
    method testAssistedCustomInject (line 63) | public function testAssistedCustomInject(): void

FILE: tests/di/BcParameterQualifierIntegrationTest.php
  class BcParameterQualifierIntegrationTest (line 9) | class BcParameterQualifierIntegrationTest extends TestCase
    method testBcParameterQualifierWorks (line 11) | public function testBcParameterQualifierWorks(): void

FILE: tests/di/BcParameterQualifierTest.php
  class BcParameterQualifierTest (line 12) | class BcParameterQualifierTest extends TestCase
    method testGetNamesFromMethodLevelAttribute (line 14) | public function testGetNamesFromMethodLevelAttribute(): void
    method testNoNamesForMultipleParameters (line 23) | public function testNoNamesForMultipleParameters(): void
    method testNoNamesWhenParameterHasQualifier (line 32) | public function testNoNamesWhenParameterHasQualifier(): void
    method testNoNamesForNonQualifierAttribute (line 41) | public function testNoNamesForNonQualifierAttribute(): void
    method testNoNamesForMethodWithoutInjectInterface (line 50) | public function testNoNamesForMethodWithoutInjectInterface(): void
    method testNamesForTargetMethodOnly (line 59) | public function testNamesForTargetMethodOnly(): void
    method testConstructorWithQualifierOnly (line 70) | public function testConstructorWithQualifierOnly(): void

FILE: tests/di/BcStringParserTest.php
  class BcStringParserTest (line 9) | class BcStringParserTest extends TestCase
    method testParseStringFormat (line 11) | public function testParseStringFormat(): void
    method testParseStringFormatWithSpaces (line 18) | public function testParseStringFormatWithSpaces(): void
    method testParseStringFormatWithDollarPrefix (line 25) | public function testParseStringFormatWithDollarPrefix(): void

FILE: tests/di/BindTest.php
  class BindTest (line 16) | class BindTest extends TestCase
    method setUp (line 21) | protected function setUp(): void
    method testGetBound (line 27) | public function testGetBound(): void
    method testToString (line 34) | public function testToString(): void
    method testInvalidToTest (line 39) | public function testInvalidToTest(): void
    method testInvalidToProviderTest (line 45) | public function testInvalidToProviderTest(): void
    method testInValidInterfaceBinding (line 51) | public function testInValidInterfaceBinding(): void
    method testUntargetedBind (line 57) | public function testUntargetedBind(): void
    method testUntargetedBindSingleton (line 66) | public function testUntargetedBindSingleton(): void
    method nameProvider (line 79) | public function nameProvider(): array
    method testToConstructor (line 91) | public function testToConstructor($name): void
    method testToConstructorWithMethodInjection (line 103) | public function testToConstructorWithMethodInjection(): void
    method testToValidation (line 121) | public function testToValidation(): void
    method testToProvider (line 127) | public function testToProvider(): void
    method testBindProviderAsProvider (line 133) | public function testBindProviderAsProvider(): void
    method testBindProviderAsProviderInSingleton (line 141) | public function testBindProviderAsProviderInSingleton(): void
    method testProviderContext (line 150) | public function testProviderContext(): void

FILE: tests/di/ContainerTest.php
  class ContainerTest (line 19) | class ContainerTest extends TestCase
    method setUp (line 27) | protected function setUp(): void
    method testGetDependency (line 35) | public function testGetDependency(): void
    method testClassGetDependency (line 43) | public function testClassGetDependency(): void
    method testProviderGetDependency (line 52) | public function testProviderGetDependency(): void
    method testGetInstance (line 60) | public function testGetInstance(): void
    method testClassGetInstance (line 67) | public function testClassGetInstance(): void
    method testProviderGetInstance (line 75) | public function testProviderGetInstance(): void
    method testGetContainer (line 82) | public function testGetContainer(): void
    method testClassGetContainer (line 89) | public function testClassGetContainer(): void
    method testMerge (line 97) | public function testMerge(): void
    method testMergePointcuts (line 108) | public function testMergePointcuts(): void
    method testMove (line 124) | public function testMove(): void
    method testMoveUnbound (line 133) | public function testMoveUnbound(): void
    method testAbstractClassUnbound (line 139) | public function testAbstractClassUnbound(): void
    method testAnnotateConstant (line 148) | public function testAnnotateConstant(): void
    method testBadMethodCall (line 162) | public function testBadMethodCall(): void
    method testUnbound (line 174) | public function testUnbound(): void
    method testWeaveAspectsWithEmptyPointcuts (line 180) | public function testWeaveAspectsWithEmptyPointcuts(): void

FILE: tests/di/ContextualProviderTest.php
  class ContextualProviderTest (line 9) | class ContextualProviderTest extends TestCase
    method testContextualProviderInjection (line 11) | public function testContextualProviderInjection(): void

FILE: tests/di/DependencyTest.php
  class DependencyTest (line 20) | class DependencyTest extends TestCase
    method setUp (line 25) | protected function setUp(): void
    method containerProvider (line 42) | public function containerProvider(): array
    method testInject (line 55) | public function testInject(Container $container): void
    method testSetterInjection (line 65) | public function testSetterInjection(Container $container): void
    method testPostConstruct (line 76) | public function testPostConstruct(Container $container): void
    method testPrototype (line 86) | public function testPrototype(Container $container): void
    method testSingleton (line 98) | public function testSingleton(Container $container): void
    method testInjectInterceptor (line 107) | public function testInjectInterceptor(): void
    method testInjectWithArgsPostConstruct (line 128) | public function testInjectWithArgsPostConstruct(Container $container):...
    method testInjectWithArgsSingleton (line 138) | public function testInjectWithArgsSingleton(Container $container): void

FILE: tests/di/Fake/Annotation/FakeInjectOne.php
  class FakeInjectOne (line 11) | #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER), Qua...
    method isOptional (line 14) | public function isOptional()

FILE: tests/di/Fake/Annotation/FakeLeft.php
  class FakeLeft (line 10) | #[Attribute, Qualifier]

FILE: tests/di/Fake/Annotation/FakeNotQualifer.php
  class FakeNotQualifer (line 10) | #[Attribute]

FILE: tests/di/Fake/Annotation/FakeQualifierOnly.php
  class FakeQualifierOnly (line 13) | #[Attribute(Attribute::TARGET_METHOD), Qualifier]

FILE: tests/di/Fake/Annotation/FakeRight.php
  class FakeRight (line 10) | #[Attribute, Qualifier]

FILE: tests/di/Fake/FakeAbstractClass.php
  class FakeAbstractClass (line 7) | abstract class FakeAbstractClass

FILE: tests/di/Fake/FakeAbstractDb.php
  class FakeAbstractDb (line 7) | class FakeAbstractDb
    method __construct (line 11) | public function __construct($id)

FILE: tests/di/Fake/FakeAnnoClass.php
  class FakeAnnoClass (line 9) | #[Attribute(Attribute::TARGET_CLASS)]

FILE: tests/di/Fake/FakeAnnoInterceptor1.php
  class FakeAnnoInterceptor1 (line 10) | class FakeAnnoInterceptor1 implements MethodInterceptor
    method invoke (line 12) | public function invoke(MethodInvocation $invocation)

FILE: tests/di/Fake/FakeAnnoInterceptor2.php
  class FakeAnnoInterceptor2 (line 10) | class FakeAnnoInterceptor2 implements MethodInterceptor
    method invoke (line 12) | public function invoke(MethodInvocation $invocation)

FILE: tests/di/Fake/FakeAnnoInterceptor3.php
  class FakeAnnoInterceptor3 (line 10) | class FakeAnnoInterceptor3 implements MethodInterceptor
    method invoke (line 12) | public function invoke(MethodInvocation $invocation)

FILE: tests/di/Fake/FakeAnnoInterceptor4.php
  class FakeAnnoInterceptor4 (line 10) | class FakeAnnoInterceptor4 implements MethodInterceptor
    method invoke (line 12) | public function invoke(MethodInvocation $invocation)

FILE: tests/di/Fake/FakeAnnoInterceptor5.php
  class FakeAnnoInterceptor5 (line 10) | class FakeAnnoInterceptor5 implements MethodInterceptor
    method invoke (line 12) | public function invoke(MethodInvocation $invocation)

FILE: tests/di/Fake/FakeAnnoInterceptorInterface.php
  type FakeAnnoInterceptorInterface (line 10) | interface FakeAnnoInterceptorInterface extends MethodInterceptor

FILE: tests/di/Fake/FakeAnnoMethod1.php
  class FakeAnnoMethod1 (line 9) | #[Attribute(Attribute::TARGET_METHOD)]

FILE: tests/di/Fake/FakeAnnoMethod2.php
  class FakeAnnoMethod2 (line 9) | #[Attribute(Attribute::TARGET_METHOD)]

FILE: tests/di/Fake/FakeAnnoMethod3.php
  class FakeAnnoMethod3 (line 9) | #[Attribute(Attribute::TARGET_METHOD)]

FILE: tests/di/Fake/FakeAnnoModule.php
  class FakeAnnoModule (line 7) | class FakeAnnoModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeAnnoOrderClass.php
  class FakeAnnoOrderClass (line 7) | #[FakeAnnoClass]
    method get (line 10) | #[FakeAnnoMethod1]

FILE: tests/di/Fake/FakeAop.php
  class FakeAop (line 7) | class FakeAop implements FakeAopInterface
    method returnSame (line 9) | public function returnSame($a)

FILE: tests/di/Fake/FakeAopDoublyInstallModule.php
  class FakeAopDoublyInstallModule (line 7) | class FakeAopDoublyInstallModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeAopGrapher.php
  class FakeAopGrapher (line 7) | class FakeAopGrapher implements FakeAopInterface
    method __construct (line 11) | public function __construct($a)
    method returnSame (line 16) | public function returnSame($a)

FILE: tests/di/Fake/FakeAopGrapherModule.php
  class FakeAopGrapherModule (line 7) | class FakeAopGrapherModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeAopInstallModule.php
  class FakeAopInstallModule (line 7) | class FakeAopInstallModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeAopInterceptorModule.php
  class FakeAopInterceptorModule (line 7) | class FakeAopInterceptorModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeAopInterface.php
  type FakeAopInterface (line 7) | interface FakeAopInterface
    method returnSame (line 9) | public function returnSame($a);

FILE: tests/di/Fake/FakeAopInterfaceModule.php
  class FakeAopInterfaceModule (line 7) | class FakeAopInterfaceModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeAopModule.php
  class FakeAopModule (line 7) | class FakeAopModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeAssistedConsumer.php
  class FakeAssistedConsumer (line 10) | class FakeAssistedConsumer
    method assistOne (line 15) | public function assistOne($a, $b, #[Assisted] ?FakeRobotInterface $rob...
    method assistWithName (line 22) | public function assistWithName($a, #[Assisted] #[Named('one')] $var1 =...
    method assistAny (line 33) | public function assistAny(#[Assisted] #[Named('one')] $var2 = null, #[...

FILE: tests/di/Fake/FakeAssistedDb.php
  class FakeAssistedDb (line 7) | class FakeAssistedDb extends FakeAbstractDb

FILE: tests/di/Fake/FakeAssistedDbModule.php
  class FakeAssistedDbModule (line 7) | class FakeAssistedDbModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeAssistedDbProvider.php
  class FakeAssistedDbProvider (line 7) | class FakeAssistedDbProvider implements ProviderInterface
    method __construct (line 12) | public function __construct(MethodInvocationProvider $invocationProvider)
    method get (line 17) | public function get()

FILE: tests/di/Fake/FakeAssistedInjectConsumer.php
  class FakeAssistedInjectConsumer (line 12) | class FakeAssistedInjectConsumer
    method assistOne (line 14) | public function assistOne($a, $b, #[Assisted] ?FakeRobotInterface $rob...
    method assistWithName (line 21) | public function assistWithName($a, #[Assisted, Named('one')] $var1 = n...
    method assistAny (line 32) | public function assistAny(#[Assisted, Named('one')] $var2 = null, #[In...
    method assistCustomeAssistedInject (line 37) | public function assistCustomeAssistedInject(#[FakeInjectOne] int $one ...

FILE: tests/di/Fake/FakeAssistedInjectDb.php
  class FakeAssistedInjectDb (line 10) | class FakeAssistedInjectDb
    method getUser (line 15) | public function getUser($id, #[Inject] ?FakeAbstractDb $db = null)

FILE: tests/di/Fake/FakeAssistedParamsConsumer.php
  class FakeAssistedParamsConsumer (line 9) | class FakeAssistedParamsConsumer
    method getUser (line 14) | public function getUser($id, #[Assisted] ?FakeAbstractDb $db = null)

FILE: tests/di/Fake/FakeBcConstructorQualifierClass.php
  class FakeBcConstructorQualifierClass (line 9) | class FakeBcConstructorQualifierClass
    method __construct (line 15) | #[FakeQualifierOnly]

FILE: tests/di/Fake/FakeBcParameterQualifierClass.php
  class FakeBcParameterQualifierClass (line 10) | class FakeBcParameterQualifierClass
    method setSingleParam (line 23) | #[FakeInjectOne]
    method setMultipleParams (line 32) | #[FakeGearStickInject('test')]
    method setSingleParamWithQualifier (line 42) | #[FakeGearStickInject('test')]
    method setSingleParamWithInjectOnly (line 51) | #[Inject]
    method setSingleParamNoInject (line 60) | public function setSingleParamNoInject(FakeGearStickInterface $param):...
    method setSingleParamMethodOnly (line 69) | #[FakeGearStickInject('test')]

FILE: tests/di/Fake/FakeBcParameterQualifierModule.php
  class FakeBcParameterQualifierModule (line 9) | class FakeBcParameterQualifierModule extends AbstractModule
    method configure (line 11) | protected function configure(): void

FILE: tests/di/Fake/FakeBuiltin.php
  class FakeBuiltin (line 7) | class FakeBuiltin
    method __construct (line 11) | public function __construct(InjectorInterface $Injector)

FILE: tests/di/Fake/FakeCar.php
  class FakeCar (line 11) | class FakeCar implements FakeCarInterface
    method __construct (line 29) | public function __construct(FakeEngineInterface $engine)
    method setTires (line 34) | #[Inject]
    method setHardtop (line 41) | #[Inject(optional: true)]
    method setMirrors (line 47) | #[Inject]
    method setSpareMirror (line 54) | #[Inject]
    method setHandle (line 60) | #[Inject]
    method setGearStick (line 66) | #[FakeGearStickInject('leather')]
    method postConstruct (line 72) | #[PostConstruct]

FILE: tests/di/Fake/FakeCarEngine.php
  class FakeCarEngine (line 7) | class FakeCarEngine extends FakeEngine

FILE: tests/di/Fake/FakeCarEngineModule.php
  class FakeCarEngineModule (line 7) | class FakeCarEngineModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeCarInterface.php
  type FakeCarInterface (line 7) | interface FakeCarInterface

FILE: tests/di/Fake/FakeCarModule.php
  class FakeCarModule (line 9) | class FakeCarModule extends AbstractModule
    method configure (line 11) | protected function configure()

FILE: tests/di/Fake/FakeClassInstanceBindModule.php
  class FakeClassInstanceBindModule (line 7) | class FakeClassInstanceBindModule extends AbstractModule
    method __construct (line 11) | public function __construct($object)
    method configure (line 17) | protected function configure()

FILE: tests/di/Fake/FakeClassWithBcParameterQualifier.php
  class FakeClassWithBcParameterQualifier (line 9) | class FakeClassWithBcParameterQualifier
    method setGearStick (line 17) | #[FakeInjectOne]

FILE: tests/di/Fake/FakeConcreteClass.php
  class FakeConcreteClass (line 7) | class FakeConcreteClass
    method __construct (line 9) | public function __construct(FakeAbstractClass $class)

FILE: tests/di/Fake/FakeConstant.php
  class FakeConstant (line 10) | #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribu...
    method __construct (line 14) | public function __construct(

FILE: tests/di/Fake/FakeConstantConsumer.php
  class FakeConstantConsumer (line 9) | class FakeConstantConsumer
    method __construct (line 17) | public function __construct(#[FakeConstant('constant')] $constant, $de...
    method setFakeConstant (line 23) | #[Inject]
    method setFakeConstantWithoutVarName (line 30) | #[Inject]

FILE: tests/di/Fake/FakeConstantInterface.php
  type FakeConstantInterface (line 7) | interface FakeConstantInterface

FILE: tests/di/Fake/FakeConstantModule.php
  class FakeConstantModule (line 7) | class FakeConstantModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeContextualModule.php
  class FakeContextualModule (line 7) | class FakeContextualModule extends AbstractModule
    method __construct (line 11) | public function __construct($context)
    method configure (line 17) | protected function configure()

FILE: tests/di/Fake/FakeContextualProvider.php
  class FakeContextualProvider (line 7) | class FakeContextualProvider implements ProviderInterface, SetContextInt...
    method setContext (line 14) | public function setContext($context)
    method get (line 19) | public function get()

FILE: tests/di/Fake/FakeContextualRobot.php
  class FakeContextualRobot (line 7) | class FakeContextualRobot implements FakeRobotInterface
    method __construct (line 11) | public function __construct($context)

FILE: tests/di/Fake/FakeDoubleInterceptor.php
  class FakeDoubleInterceptor (line 10) | class FakeDoubleInterceptor implements MethodInterceptor, FakeDoubleInte...
    method invoke (line 12) | public function invoke(MethodInvocation $invocation)

FILE: tests/di/Fake/FakeDoubleInterceptorInterface.php
  type FakeDoubleInterceptorInterface (line 10) | interface FakeDoubleInterceptorInterface extends MethodInterceptor

FILE: tests/di/Fake/FakeEngine.php
  class FakeEngine (line 7) | class FakeEngine implements FakeEngineInterface
    method foo (line 9) | public function foo()

FILE: tests/di/Fake/FakeEngine2.php
  class FakeEngine2 (line 7) | class FakeEngine2 implements FakeEngineInterface
    method foo (line 9) | public function foo()

FILE: tests/di/Fake/FakeEngine3.php
  class FakeEngine3 (line 7) | class FakeEngine3 implements FakeEngineInterface
    method foo (line 9) | public function foo()

FILE: tests/di/Fake/FakeEngineInterface.php
  type FakeEngineInterface (line 7) | interface FakeEngineInterface
    method foo (line 9) | public function foo();

FILE: tests/di/Fake/FakeEngineProvider.php
  class FakeEngineProvider (line 7) | class FakeEngineProvider implements ProviderInterface
    method get (line 9) | public function get()

FILE: tests/di/Fake/FakeEngineToProviderModule.php
  class FakeEngineToProviderModule (line 7) | class FakeEngineToProviderModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeFormerBindingHasPriorityModule.php
  class FakeFormerBindingHasPriorityModule (line 7) | class FakeFormerBindingHasPriorityModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeGearStickInject.php
  class FakeGearStickInject (line 11) | #[Attribute(Attribute::TARGET_METHOD)]
    method isOptional (line 17) | public function isOptional()
    method __construct (line 22) | public function __construct($value)

FILE: tests/di/Fake/FakeGearStickInterface.php
  type FakeGearStickInterface (line 7) | interface FakeGearStickInterface

FILE: tests/di/Fake/FakeGearStickProvider.php
  class FakeGearStickProvider (line 9) | class FakeGearStickProvider implements ProviderInterface
    method __construct (line 14) | public function __construct(InjectionPointInterface $ip)
    method get (line 19) | public function get()

FILE: tests/di/Fake/FakeHandle.php
  class FakeHandle (line 7) | class FakeHandle implements FakeHandleInterface

FILE: tests/di/Fake/FakeHandleBar.php
  class FakeHandleBar (line 9) | class FakeHandleBar
    method setMirrors (line 14) | #[Inject]
    method setLeftMirror (line 20) | #[Inject]

FILE: tests/di/Fake/FakeHandleBarQualifier.php
  class FakeHandleBarQualifier (line 7) | class FakeHandleBarQualifier
    method __construct (line 9) | public function __construct(

FILE: tests/di/Fake/FakeHandleInterface.php
  type FakeHandleInterface (line 7) | interface FakeHandleInterface

FILE: tests/di/Fake/FakeHandleProvider.php
  class FakeHandleProvider (line 10) | class FakeHandleProvider implements ProviderInterface
    method __construct (line 14) | #[Inject]
    method get (line 20) | public function get()

FILE: tests/di/Fake/FakeHardtop.php
  class FakeHardtop (line 7) | class FakeHardtop implements FakeHardtopInterface

FILE: tests/di/Fake/FakeHardtopInterface.php
  type FakeHardtopInterface (line 7) | interface FakeHardtopInterface

FILE: tests/di/Fake/FakeInjectionPoint.php
  class FakeInjectionPoint (line 10) | class FakeInjectionPoint implements ProviderInterface
    method __construct (line 14) | public function __construct(#[Named('aa')] ReflectionParameter $ip)
    method get (line 19) | public function get()

FILE: tests/di/Fake/FakeInstallModule.php
  class FakeInstallModule (line 7) | class FakeInstallModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeInstanceBindModule.php
  class FakeInstanceBindModule (line 9) | class FakeInstanceBindModule extends AbstractModule
    method configure (line 11) | protected function configure()

FILE: tests/di/Fake/FakeInstanceBindModule2.php
  class FakeInstanceBindModule2 (line 7) | class FakeInstanceBindModule2 extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeInstanceBindModuleOneTo3.php
  class FakeInstanceBindModuleOneTo3 (line 7) | class FakeInstanceBindModuleOneTo3 extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeInternalTypeModule.php
  class FakeInternalTypeModule (line 11) | class FakeInternalTypeModule extends AbstractModule
    method configure (line 13) | protected function configure()

FILE: tests/di/Fake/FakeInternalTypes.php
  class FakeInternalTypes (line 9) | class FakeInternalTypes
    method __construct (line 17) | public function __construct(
    method stringId (line 31) | public function stringId(string $id): void

FILE: tests/di/Fake/FakeLeatherGearStick.php
  class FakeLeatherGearStick (line 7) | class FakeLeatherGearStick implements FakeGearStickInterface

FILE: tests/di/Fake/FakeLeft.php
  class FakeLeft (line 10) | #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER)]
    method __construct (line 14) | public function __construct(

FILE: tests/di/Fake/FakeLeftLeg.php
  class FakeLeftLeg (line 7) | class FakeLeftLeg implements FakeLegInterface

FILE: tests/di/Fake/FakeLegInterface.php
  type FakeLegInterface (line 7) | interface FakeLegInterface

FILE: tests/di/Fake/FakeLogStringModule.php
  class FakeLogStringModule (line 9) | class FakeLogStringModule extends AbstractModule
    method configure (line 11) | protected function configure()

FILE: tests/di/Fake/FakeMirrorInterface.php
  type FakeMirrorInterface (line 7) | interface FakeMirrorInterface

FILE: tests/di/Fake/FakeMirrorLeft.php
  class FakeMirrorLeft (line 7) | class FakeMirrorLeft implements FakeMirrorInterface

FILE: tests/di/Fake/FakeMirrorRight.php
  class FakeMirrorRight (line 7) | class FakeMirrorRight implements FakeMirrorInterface

FILE: tests/di/Fake/FakeModuleInModule.php
  class FakeModuleInModule (line 7) | class FakeModuleInModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeModuleInModuleOverride.php
  class FakeModuleInModuleOverride (line 7) | class FakeModuleInModuleOverride extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeMultiBindingAnnotation.php
  class FakeMultiBindingAnnotation (line 11) | final class FakeMultiBindingAnnotation
    method __construct (line 19) | public function __construct(

FILE: tests/di/Fake/FakeMultiBindingConsumer.php
  class FakeMultiBindingConsumer (line 11) | final class FakeMultiBindingConsumer
    method __construct (line 17) | public function __construct(
    method testValid (line 22) | public function testValid(): void
    method testInvalid (line 37) | public function testInvalid(): void

FILE: tests/di/Fake/FakeOnionInterceptor2.php
  class FakeOnionInterceptor2 (line 10) | class FakeOnionInterceptor2 implements MethodInterceptor
    method invoke (line 12) | public function invoke(MethodInvocation $invocation)

FILE: tests/di/Fake/FakeOnionInterceptor3.php
  class FakeOnionInterceptor3 (line 10) | class FakeOnionInterceptor3 implements MethodInterceptor
    method invoke (line 12) | public function invoke(MethodInvocation $invocation)

FILE: tests/di/Fake/FakeOnionInterceptor4.php
  class FakeOnionInterceptor4 (line 10) | class FakeOnionInterceptor4 implements MethodInterceptor
    method invoke (line 12) | public function invoke(MethodInvocation $invocation)

FILE: tests/di/Fake/FakeOpenCarModule.php
  class FakeOpenCarModule (line 7) | class FakeOpenCarModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeOverrideInstallModule.php
  class FakeOverrideInstallModule (line 7) | class FakeOverrideInstallModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakePdoModule.php
  class FakePdoModule (line 9) | class FakePdoModule extends AbstractModule
    method configure (line 11) | protected function configure()

FILE: tests/di/Fake/FakePhp8Car.php
  class FakePhp8Car (line 15) | class FakePhp8Car implements FakeCarInterface
    method __construct (line 32) | public function __construct(FakeEngineInterface $engine, #[Named('righ...
    method setTires (line 38) | #[Inject]
    method setHardtop (line 45) | #[Inject(optional: true)]
    method setMirrors (line 51) | #[Inject]
    method setQualiferMirrors (line 58) | #[Inject]
    method notQualifer (line 65) | #[Inject]
    method setHandle (line 75) | #[Inject]
    method setGearStick (line 83) | #[FakeGearStickInject('leather')]
    method postConstruct (line 89) | #[PostConstruct]
    method setOne (line 99) | #[Inject]

FILE: tests/di/Fake/FakePhp8CarModule.php
  class FakePhp8CarModule (line 11) | class FakePhp8CarModule extends AbstractModule
    method configure (line 13) | protected function configure()

FILE: tests/di/Fake/FakePhp8HandleProvider.php
  class FakePhp8HandleProvider (line 10) | class FakePhp8HandleProvider implements ProviderInterface
    method __construct (line 14) | public function __construct(#[Named('logo')] $logo = 'nardi')
    method get (line 19) | public function get()

FILE: tests/di/Fake/FakePriorityModule.php
  class FakePriorityModule (line 7) | class FakePriorityModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakePropConstruct.php
  class FakePropConstruct (line 9) | class FakePropConstruct
    method __construct (line 11) | public function __construct(

FILE: tests/di/Fake/FakeRenameModule.php
  class FakeRenameModule (line 7) | class FakeRenameModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeRight.php
  class FakeRight (line 10) | #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER)]
    method __construct (line 14) | public function __construct(

FILE: tests/di/Fake/FakeRightLeg.php
  class FakeRightLeg (line 7) | class FakeRightLeg implements FakeLegInterface

FILE: tests/di/Fake/FakeRobot.php
  class FakeRobot (line 7) | class FakeRobot implements FakeRobotInterface

FILE: tests/di/Fake/FakeRobotInterface.php
  type FakeRobotInterface (line 7) | interface FakeRobotInterface

FILE: tests/di/Fake/FakeRobotProvider.php
  class FakeRobotProvider (line 7) | class FakeRobotProvider implements ProviderInterface
    method get (line 9) | public function get()

FILE: tests/di/Fake/FakeRobotTeam.php
  class FakeRobotTeam (line 7) | class FakeRobotTeam
    method __construct (line 12) | public function __construct(FakeRobot $robot1, FakeRobot $robot2)

FILE: tests/di/Fake/FakeSet.php
  class FakeSet (line 10) | final class FakeSet
    method __construct (line 15) | public function __construct(#[Set(FakeEngineInterface::class)] public ...
    method warn (line 20) | public function warn(): void

FILE: tests/di/Fake/FakeSetNotFoundWithMap.php
  class FakeSetNotFoundWithMap (line 11) | final class FakeSetNotFoundWithMap
    method __construct (line 24) | public function __construct(

FILE: tests/di/Fake/FakeSetNotFoundWithProvider.php
  class FakeSetNotFoundWithProvider (line 11) | final class FakeSetNotFoundWithProvider
    method __construct (line 19) | public function __construct(

FILE: tests/di/Fake/FakeToBindInvalidClassModule.php
  class FakeToBindInvalidClassModule (line 7) | class FakeToBindInvalidClassModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeToBindModule.php
  class FakeToBindModule (line 7) | class FakeToBindModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeToBindSingletonModule.php
  class FakeToBindSingletonModule (line 7) | class FakeToBindSingletonModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeToConstructorRobot.php
  class FakeToConstructorRobot (line 7) | class FakeToConstructorRobot implements FakeRobotInterface
    method __construct (line 13) | public function __construct(FakeLegInterface $leg, $tmpDir)
    method setEngine (line 19) | public function setEngine(FakeEngineInterface $engine): void

FILE: tests/di/Fake/FakeToProviderBindModule.php
  class FakeToProviderBindModule (line 7) | class FakeToProviderBindModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeToProviderSingletonBindModule.php
  class FakeToProviderSingletonBindModule (line 7) | class FakeToProviderSingletonBindModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakeTyre.php
  class FakeTyre (line 7) | class FakeTyre implements FakeTyreInterface

FILE: tests/di/Fake/FakeTyreInterface.php
  type FakeTyreInterface (line 7) | interface FakeTyreInterface

FILE: tests/di/Fake/FakeUnNamedClass.php
  class FakeUnNamedClass (line 7) | class FakeUnNamedClass
    method __construct (line 9) | public function __construct(string $value)

FILE: tests/di/Fake/FakeUnNamedModule.php
  class FakeUnNamedModule (line 7) | class FakeUnNamedModule extends AbstractModule
    method configure (line 9) | protected function configure(): void

FILE: tests/di/Fake/FakeUntarget.php
  class FakeUntarget (line 11) | class FakeUntarget
    method __construct (line 15) | public function __construct(FakeUntargetChild $child)

FILE: tests/di/Fake/FakeUntargetChild.php
  class FakeUntargetChild (line 11) | class FakeUntargetChild
    method __construct (line 15) | public function __construct($val)

FILE: tests/di/Fake/FakeUntargetModule.php
  class FakeUntargetModule (line 11) | class FakeUntargetModule extends AbstractModule
    method configure (line 13) | protected function configure()

FILE: tests/di/Fake/FakeUntargetToIntanceModule.php
  class FakeUntargetToIntanceModule (line 11) | class FakeUntargetToIntanceModule extends AbstractModule
    method configure (line 13) | protected function configure()

FILE: tests/di/Fake/FakeWalkRobot.php
  class FakeWalkRobot (line 7) | #[FakeConstant('class_constant_val')]
    method __construct (line 16) | #[FakeConstant(10)]

FILE: tests/di/Fake/FakeWalkRobotLegProvider.php
  class FakeWalkRobotLegProvider (line 9) | class FakeWalkRobotLegProvider implements ProviderInterface
    method __construct (line 14) | public function __construct(InjectionPointInterface $ip)
    method get (line 19) | public function get()

FILE: tests/di/Fake/FakeWalkRobotModule.php
  class FakeWalkRobotModule (line 7) | class FakeWalkRobotModule extends AbstractModule
    method configure (line 9) | protected function configure()

FILE: tests/di/Fake/FakelNoConstructorCallModule.php
  class FakelNoConstructorCallModule (line 7) | class FakelNoConstructorCallModule extends AbstractModule
    method __construct (line 9) | public function __construct()
    method configure (line 13) | public function configure()

FILE: tests/di/Fake/NullVisitor.php
  class NullVisitor (line 10) | final class NullVisitor implements VisitorInterface
    method visitDependency (line 13) | public function visitDependency(
    method visitProvider (line 24) | public function visitProvider(
    method visitInstance (line 33) | public function visitInstance($value)
    method visitAspectBind (line 39) | public function visitAspectBind(Bind $aopBind)
    method visitNewInstance (line 45) | public function visitNewInstance(
    method visitSetterMethods (line 61) | public function visitSetterMethods(
    method visitSetterMethod (line 70) | public function visitSetterMethod(string $method, Arguments $arguments)
    method visitArguments (line 76) | public function visitArguments(array $arguments)
    method visitArgument (line 84) | public function visitArgument(

FILE: tests/di/GrapherTest.php
  class GrapherTest (line 16) | class GrapherTest extends TestCase
    method testNew (line 18) | public function testNew(): void
    method testGetInstanceWithArgs (line 24) | public function testGetInstanceWithArgs(): void
    method testAopClassAutoloader (line 32) | public function testAopClassAutoloader(): void

FILE: tests/di/InjectionPointTest.php
  class InjectionPointTest (line 10) | class InjectionPointTest extends TestCase
    method setUp (line 18) | protected function setUp(): void
    method testGetParameter (line 24) | public function testGetParameter(): void
    method testGetMethod (line 30) | public function testGetMethod(): void
    method testGetClass (line 36) | public function testGetClass(): void
    method testGetQualifiers (line 42) | public function testGetQualifiers(): void

FILE: tests/di/InjectionPointsTest.php
  class InjectionPointsTest (line 11) | class InjectionPointsTest extends TestCase
    method setUp (line 16) | protected function setUp(): void
    method testNew (line 22) | public function testNew(): void
    method testInvoke (line 27) | public function testInvoke(): SetterMethods
    method testSetterMethod (line 39) | public function testSetterMethod(SetterMethods $setterMethod): void
    method testSetterMethodOptional (line 52) | public function testSetterMethodOptional(SetterMethods $setterMethod):...

FILE: tests/di/InjectorTest.php
  class InjectorTest (line 23) | class InjectorTest extends TestCase
    method testNew (line 25) | public function testNew(): void
    method testGetToInstance (line 31) | public function testGetToInstance(): void
    method testToInstance (line 38) | public function testToInstance(): void
    method testUnbound (line 45) | public function testUnbound(): void
    method testInstall (line 52) | public function testInstall(): void
    method testFormerBindingHasPriority (line 61) | public function testFormerBindingHasPriority(): void
    method testLatterBindingHasPriorityWithThisParameter (line 68) | public function testLatterBindingHasPriorityWithThisParameter(): void
    method testModuleInModule (line 75) | public function testModuleInModule(): void
    method testModuleInModuleOverride (line 84) | public function testModuleInModuleOverride(): void
    method testToBinding (line 91) | public function testToBinding(): void
    method testClassToClassBinding (line 98) | public function testClassToClassBinding(): void
    method testToBindingPrototype (line 105) | public function testToBindingPrototype(): void
    method testToBindingSingleton (line 113) | public function testToBindingSingleton(): void
    method testToProviderBinding (line 121) | public function testToProviderBinding(): void
    method testClassToProviderBinding (line 129) | public function testClassToProviderBinding(): void
    method testToProviderBindingSingleton (line 136) | public function testToProviderBindingSingleton(): void
    method testGetConcreteClass (line 144) | public function testGetConcreteClass(): void
    method testGetConcreteHavingDependency (line 151) | public function testGetConcreteHavingDependency(): void
    method testGetConcreteClassWithModule (line 165) | public function testGetConcreteClassWithModule(): void
    method testAnnotationBasedInjection (line 172) | public function testAnnotationBasedInjection(): Injector
    method testSerialize (line 193) | public function testSerialize(Injector $injector): void
    method testAop (line 201) | public function testAop(): void
    method testIntefaceBindingAop (line 210) | public function testIntefaceBindingAop(): void
    method testBuiltinBinding (line 231) | public function testBuiltinBinding(): void
    method testSerializeBuiltinBinding (line 237) | public function testSerializeBuiltinBinding(): void
    method testAopBoundInDifferentModule (line 245) | public function testAopBoundInDifferentModule(): void
    method testAopBoundInDifferentModuleAfterAnotherBinding (line 254) | public function testAopBoundInDifferentModuleAfterAnotherBinding(): void
    method testAopBoundDoublyInDifferentModule (line 263) | public function testAopBoundDoublyInDifferentModule(): void
    method testAopClassAutoloader (line 272) | public function testAopClassAutoloader(): void
    method testAopOnDemandByUnboundConcreteClass (line 293) | public function testAopOnDemandByUnboundConcreteClass(): void
    method testBindOrder (line 301) | public function testBindOrder(): void
    method testAnnotateConstant (line 310) | public function testAnnotateConstant(): void
    method testContextualDependencyInjection (line 316) | public function testContextualDependencyInjection(): void
    method testNewAbstract (line 324) | public function testNewAbstract(): void
    method testIsOptionalValue (line 331) | public function testIsOptionalValue(): void
    method testInternalTypes (line 339) | public function testInternalTypes(): void
    method testToConstructor (line 347) | public function testToConstructor(): void
    method testNullObject (line 364) | public function testNullObject(): void
    method testBindInterfeceInterceptor (line 376) | public function testBindInterfeceInterceptor(): void
    method testBindInterfeceNullInterceptor (line 395) | public function testBindInterfeceNullInterceptor(): void
    method testModuleArray (line 415) | public function testModuleArray(): void
    method testSingleArray (line 441) | public function testSingleArray(): void
    method testProviderInjectWithSet (line 460) | public function testProviderInjectWithSet(): void

FILE: tests/di/ModuleMergerTest.php
  class ModuleMergerTest (line 9) | class ModuleMergerTest extends TestCase
    method testInvoke (line 11) | public function testInvoke(): void

FILE: tests/di/ModuleTest.php
  class ModuleTest (line 12) | class ModuleTest extends TestCase
    method testNew (line 14) | public function testNew(): void
    method testInstall (line 20) | public function testInstall(): void
    method testToInvalidClass (line 26) | public function testToInvalidClass(): void
    method testRename (line 32) | public function testRename(): void
    method testConstructorCallModule (line 39) | public function testConstructorCallModule(): void
    method testActivate (line 46) | public function testActivate(): void
    method testtoString (line 52) | public function testtoString(): void

FILE: tests/di/MultiBinding/MultiBinderTest.php
  class MultiBinderTest (line 17) | class MultiBinderTest extends TestCase
    method testAdd (line 19) | public function testAdd(): void
    method testSet (line 31) | public function testSet(): void

FILE: tests/di/MultiBinding/MultiBindingModuleTest.php
  class MultiBindingModuleTest (line 32) | class MultiBindingModuleTest extends TestCase
    method setUp (line 37) | protected function setUp(): void
    method testInjectMap (line 57) | public function testInjectMap(): Map
    method testMapInstance (line 71) | public function testMapInstance(Map $map): void
    method testMapIteration (line 82) | public function testMapIteration(Map $map): void
    method testIsSet (line 94) | public function testIsSet(Map $map): void
    method testOffsetSet (line 105) | public function testOffsetSet(Map $map): void
    method testOffsetUnset (line 116) | public function testOffsetUnset(Map $map): void
    method testAnotherBinder (line 122) | public function testAnotherBinder(): void
    method testMultipileModule (line 131) | public function testMultipileModule(): void
    method testAnnotation (line 153) | public function testAnnotation(): void
    method testSetNotFoundInMap (line 163) | public function testSetNotFoundInMap(): void
    method testSetNotFoundInProvider (line 170) | public function testSetNotFoundInProvider(): void

FILE: tests/di/NameTest.php
  class NameTest (line 10) | class NameTest extends TestCase
    method testUnName (line 12) | public function testUnName(): void
    method testSingleName (line 20) | public function testSingleName(): void
    method testSetName (line 28) | public function testSetName(): void
    method testKeyValuePairName (line 40) | public function testKeyValuePairName(string $keyPairValueString): void
    method keyPairStringProvider (line 52) | public function keyPairStringProvider(): array
    method testKeyValuePairButNotFound (line 62) | public function testKeyValuePairButNotFound(): void
    method testKeyValuePairWithDollarPrefix (line 70) | public function testKeyValuePairWithDollarPrefix(): void

FILE: tests/di/NewInstanceTest.php
  class NewInstanceTest (line 13) | class NewInstanceTest extends TestCase
    method setUp (line 18) | protected function setUp(): void
    method testInvoke (line 30) | public function testInvoke(): void

FILE: tests/di/NoHintTest.php
  class NoHintTest (line 11) | class NoHintTest extends TestCase
    method testNoHintIsUnbound (line 13) | public function testNoHintIsUnbound(): void
    method testNoHintThrownForNoTypeNoName (line 19) | public function testNoHintThrownForNoTypeNoName(): void
    method testNoHintMessageFormat (line 27) | public function testNoHintMessageFormat(): void

FILE: tests/di/NullModuleTest.php
  class NullModuleTest (line 9) | class NullModuleTest extends TestCase
    method testEmpty (line 11) | public function testEmpty(): void

FILE: tests/di/ProviderProviderTest.php
  class ProviderProviderTest (line 10) | class ProviderProviderTest extends TestCase
    method testGet (line 12) | public function testGet(): void

FILE: tests/di/SetterMethodTest.php
  class SetterMethodTest (line 15) | class SetterMethodTest extends TestCase
    method setUp (line 20) | protected function setUp(): void
    method testInvoke (line 27) | public function testInvoke(): void
    method testUnbound (line 39) | public function testUnbound(): void
    method testAcceptWithUnboundException (line 47) | public function testAcceptWithUnboundException(): void
    method testAcceptWithUnboundExceptionOptional (line 102) | public function testAcceptWithUnboundExceptionOptional(): void

FILE: tests/di/SetterMethodsTest.php
  class SetterMethodsTest (line 10) | class SetterMethodsTest extends TestCase
    method setUp (line 15) | protected function setUp(): void
    method testInvoke (line 21) | public function testInvoke(): void

FILE: tests/di/SpyCompilerTest.php
  class SpyCompilerTest (line 10) | class SpyCompilerTest extends TestCase
    method testNoBindng (line 12) | public function testNoBindng(): void

FILE: tests/di/UnboundTest.php
  class UnboundTest (line 11) | class UnboundTest extends TestCase
    method testGetBound (line 13) | public function testGetBound(): void
    method testNoPrevious (line 23) | public function testNoPrevious(): void
    method testNonUnboundPrevious (line 30) | public function testNonUnboundPrevious(): void

FILE: tests/di/VisitorTest.php
  class VisitorTest (line 16) | class VisitorTest extends TestCase
    method setUp (line 30) | public function setUp(): void
    method testNullVisit (line 43) | public function testNullVisit(): void
    method testCollectVisit (line 49) | public function testCollectVisit(): void
    method testVisitDependencyProvider (line 181) | public function testVisitDependencyProvider(): void
    method testVisitInsntace (line 187) | public function testVisitInsntace(): void

FILE: tests/type/InjectorInterfaceTest.php
  class InjectorInterfaceTest (line 10) | final class InjectorInterfaceTest
    method __construct (line 16) | public function __construct()
Condensed preview — 311 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (317K chars).
[
  {
    "path": ".gitattributes",
    "chars": 702,
    "preview": "# Normalize line endings\n* text=auto eol=lf\n\n/tests              export-ignore\n/tests-php8         export-ignore\n/demo  "
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 723,
    "preview": "# CONTRIBUTING\n\n## Code Contributions\n\n## Installation\n\nInstall project dependencies and test tools by running the follo"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 284,
    "preview": "---\nname: Bug Report\nabout: Create a bug report\nlabels: Bug\n---\n\n### Bug Report\n\n<!-- Provide a summary describing the p"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature.md",
    "chars": 120,
    "preview": "---\nname: Feature\nabout: Suggest a new feature or enhancement\nlabels: Feature\n---\n\n<!-- Write your suggestion here. -->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "chars": 122,
    "preview": "---\nname: Question\nabout: Ask a question regarding software usage\nlabels: Support\n---\n\n<!-- Write your question here. --"
  },
  {
    "path": ".github/SECURITY.md",
    "chars": 200,
    "preview": "# Reporting a vulnerability\n\nIf you have found any issues that might have security implications,\nplease send a report pr"
  },
  {
    "path": ".github/workflows/coding-standards.yml",
    "chars": 186,
    "preview": "name: Coding Standards\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  cs:\n    uses: ray-di/.github/.github/w"
  },
  {
    "path": ".github/workflows/continuous-integration.yml",
    "chars": 270,
    "preview": "name: Continuous Integration\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  ci:\n    uses: ray-di/.github/.gi"
  },
  {
    "path": ".github/workflows/demo.yml",
    "chars": 917,
    "preview": "name: Demo\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  demo:\n    name: Demo\n    runs-on: ubuntu-latest\n  "
  },
  {
    "path": ".github/workflows/prefer-lowest.yml",
    "chars": 957,
    "preview": "name: Prefer Lowest\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  prefer-lowest:\n    name: Prefer Lowest\n  "
  },
  {
    "path": ".github/workflows/static-analysis.yml",
    "chars": 184,
    "preview": "name: Static Analysis\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  sa:\n    uses: ray-di/.github/.github/wo"
  },
  {
    "path": ".github/workflows/update-copyright-years-in-license-file.yml",
    "chars": 353,
    "preview": "name: Update copyright year(s) in license file\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 3 1 1 *\"\n\njobs:\n  ru"
  },
  {
    "path": ".gitignore",
    "chars": 414,
    "preview": "/vendor/\n/build/\n/tests/compiler/tmp/car/\n/tests/compiler/tmp/injector_cache/\n/tests/compiler/tmp/logger/\n/tests/compile"
  },
  {
    "path": ".scrutinizer.yml",
    "chars": 212,
    "preview": "build:\n    image: default-jammy\n    environment:\n        php: 8.4\n    nodes:\n        analysis:\n            tests:\n      "
  },
  {
    "path": ".sonarcloud.properties",
    "chars": 221,
    "preview": "# Exclude files from SonarCloud analysis\n# - GitHub workflow files: version tags are best practice\n# - Demo files: examp"
  },
  {
    "path": "CLAUDE.md",
    "chars": 3126,
    "preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## "
  },
  {
    "path": "LICENSE",
    "chars": 1087,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2012-2026 Akihito Koriyama\n\nPermission is hereby granted, free of charge, to any pe"
  },
  {
    "path": "README.md",
    "chars": 981,
    "preview": "# Ray.Di\n\n## A dependency injection framework for PHP\n\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/ray-di/"
  },
  {
    "path": "codecov.yml",
    "chars": 164,
    "preview": "codecov:\n  notify:\n    require_ci_to_pass: yes\n\ncoverage:\n  status:\n    project:\n      default:\n        target: 100%\n   "
  },
  {
    "path": "composer-require-checker.json",
    "chars": 267,
    "preview": "{\n  \"symbol-whitelist\": [\n    \"null\", \"true\", \"false\",\n    \"static\", \"self\", \"parent\",\n    \"array\", \"string\", \"int\", \"fl"
  },
  {
    "path": "composer.json",
    "chars": 2968,
    "preview": "{\n    \"name\": \"ray/di\",\n    \"description\": \"Guice style dependency injection framework\",\n    \"keywords\": [\"di\", \"aop\"],\n"
  },
  {
    "path": "demo/01a-linked-binding.php",
    "chars": 876,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\Injector;\n\nrequire dirname(__DIR__) . '/vendor/au"
  },
  {
    "path": "demo/01b-linked-binding-setter-injection.php",
    "chars": 969,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\Di\\Inject;\nuse Ray\\Di\\Injector;\n\nrequire dirname("
  },
  {
    "path": "demo/02-provider-binding.php",
    "chars": 1268,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\Injector;\nuse Ray\\Di\\ProviderInterface;\n\nrequire "
  },
  {
    "path": "demo/02a-named-by-qualifier.php",
    "chars": 1089,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\Di\\Qualifier;\nuse Ray\\Di\\Injector;\n\nrequire dirna"
  },
  {
    "path": "demo/02b-named-by-named.php",
    "chars": 1010,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\Di\\Named;\nuse Ray\\Di\\Injector;\n\nrequire dirname(_"
  },
  {
    "path": "demo/03-injection-point.php",
    "chars": 1615,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\InjectionPointInterface;\nuse Ray\\Di\\Injector;\nuse"
  },
  {
    "path": "demo/04-untarget-binding.php",
    "chars": 595,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\Injector;\nuse Ray\\Di\\Scope;\n\nrequire dirname(__DI"
  },
  {
    "path": "demo/05a-constructor-binding.php",
    "chars": 1150,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nrequire dirname(__DIR__) . '/vendor/autoload.php';\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\"
  },
  {
    "path": "demo/05b-constructor-binding-setter-injection.php",
    "chars": 1256,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Demo;\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\InjectionPoints;\nuse Ray\\"
  },
  {
    "path": "demo/07-assisted-injection.php",
    "chars": 656,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Composer\\Autoload\\ClassLoader;\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\Injector;\n\n$lo"
  },
  {
    "path": "demo/10-cache.php",
    "chars": 1008,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Composer\\Autoload\\ClassLoader;\nuse Ray\\Di\\Injector;\n\n$loader = require dirname(__DI"
  },
  {
    "path": "demo/11-script-injector.php",
    "chars": 1140,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Composer\\Autoload\\ClassLoader;\nuse Ray\\Compiler\\DiCompiler;\nuse Ray\\Compiler\\Script"
  },
  {
    "path": "demo/12-dependency-chain-error-message.php",
    "chars": 2412,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Composer\\Autoload\\ClassLoader;\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\Exception\\Unbo"
  },
  {
    "path": "demo/chain-error/A.php",
    "chars": 95,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nclass A\n{\n    public function __construct(B $b)\n    {\n    }\n}\n"
  },
  {
    "path": "demo/chain-error/B.php",
    "chars": 95,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nclass B\n{\n    public function __construct(C $c)\n    {\n    }\n}\n"
  },
  {
    "path": "demo/chain-error/C.php",
    "chars": 95,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nclass C\n{\n    public function __construct(D $d)\n    {\n    }\n}\n"
  },
  {
    "path": "demo/chain-error/D.php",
    "chars": 104,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nclass D\n{\n    public function __construct(EInterface $e)\n    {\n    }\n}\n"
  },
  {
    "path": "demo/chain-error/EInterface.php",
    "chars": 58,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\ninterface EInterface\n{\n}\n"
  },
  {
    "path": "demo/finder/Db.php",
    "chars": 232,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\Di\\PostConstruct;\n\nclass Db implements DbInterface\n{\n    public function __c"
  },
  {
    "path": "demo/finder/DbFinder.php",
    "chars": 322,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\Di\\Inject;\n\nclass DbFinder implements FinderInterface\n{\n    public function "
  },
  {
    "path": "demo/finder/DbInterface.php",
    "chars": 59,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\ninterface DbInterface\n{\n}\n"
  },
  {
    "path": "demo/finder/Finder.php",
    "chars": 77,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nclass Finder implements FinderInterface\n{\n}\n"
  },
  {
    "path": "demo/finder/FinderInterface.php",
    "chars": 63,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\ninterface FinderInterface\n{\n}\n"
  },
  {
    "path": "demo/finder/FinderModule.php",
    "chars": 712,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\Scope;\n\nclass FinderModule extends AbstractModule"
  },
  {
    "path": "demo/finder/MovieFinder.php",
    "chars": 266,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\Di\\Assisted;\nuse Ray\\Di\\Di\\Inject;\n\nclass MovieFinder\n{\n    public function "
  },
  {
    "path": "demo/finder/MovieLister.php",
    "chars": 315,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Ray\\Di\\Di\\Inject;\n\nclass MovieLister implements MovieListerInterface\n{\n    public f"
  },
  {
    "path": "demo/finder/MovieListerInterface.php",
    "chars": 68,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\ninterface MovieListerInterface\n{\n}\n"
  },
  {
    "path": "demo/finder/Sorter.php",
    "chars": 50,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nclass Sorter\n{\n}\n"
  },
  {
    "path": "demo/run.php",
    "chars": 858,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nputenv('TMPDIR=' . __DIR__ . '/tmp');\n\npassthru('php ' . __DIR__ . '/01a-linked-binding"
  },
  {
    "path": "demo/tmp/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "phpcs.xml",
    "chars": 4146,
    "preview": "<?xml version=\"1.0\"?>\n<ruleset\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" name=\"bearcs\"\n    xsi:noNamespa"
  },
  {
    "path": "phpmd.xml",
    "chars": 2070,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ruleset name=\"PHPMD rule set\"\n         xmlns=\"http://pmd.sf.net/ruleset/1.0.0\"\n "
  },
  {
    "path": "phpstan.neon",
    "chars": 421,
    "preview": "parameters:\n  errorFormat: raw\n  level: max\n  paths:\n    - src/di\n    - tests/di\n    - tests/type\n  excludePaths:\n    - "
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 738,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xs"
  },
  {
    "path": "psalm.xml",
    "chars": 537,
    "preview": "<?xml version=\"1.0\"?>\n<psalm\n    errorLevel=\"1\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns=\"htt"
  },
  {
    "path": "rector.php",
    "chars": 544,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Rector\\Config\\RectorConfig;\nuse Rector\\Set\\ValueObject\\LevelSetList;\nuse Rector\\Set"
  },
  {
    "path": "src/di/AbstractModule.php",
    "chars": 4267,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\AbstractMatcher;\nuse Ray\\Aop\\Matcher;\nuse Ray\\Aop\\Pointc"
  },
  {
    "path": "src/di/AcceptInterface.php",
    "chars": 425,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\n/**\n * This interface defines the accept method, which allows an obj"
  },
  {
    "path": "src/di/AnnotatedClass.php",
    "chars": 1492,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\ReflectionClass;\nuse Ray\\Di\\Di\\PostConstruct;\nuse Reflec"
  },
  {
    "path": "src/di/AnnotatedClassMethods.php",
    "chars": 1485,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\ReflectionClass;\nuse Ray\\Aop\\ReflectionMethod;\nuse Ray\\D"
  },
  {
    "path": "src/di/Annotation/ScriptDir.php",
    "chars": 242,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Annotation;\n\nuse Attribute;\nuse Ray\\Di\\Di\\Qualifier;\n\n/**\n * Script di"
  },
  {
    "path": "src/di/Argument.php",
    "chars": 3979,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse ReflectionException;\nuse ReflectionMethod;\nuse ReflectionNamedTy"
  },
  {
    "path": "src/di/Arguments.php",
    "chars": 2602,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Exception\\NoHint;\nuse Ray\\Di\\Exception\\Unbound;\nuse Refle"
  },
  {
    "path": "src/di/AspectBind.php",
    "chars": 1276,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\Bind as AopBind;\nuse Ray\\Aop\\MethodInterceptor;\n\nuse fun"
  },
  {
    "path": "src/di/AssistedInjectInterceptor.php",
    "chars": 4035,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInterceptor;\nuse Ray\\Aop\\MethodInvocation;\nuse Ray"
  },
  {
    "path": "src/di/AssistedInjectModule.php",
    "chars": 423,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Matcher\\AssistedInjectMatcher;\n\n/**\n * Assisted module fo"
  },
  {
    "path": "src/di/AssistedModule.php",
    "chars": 725,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInvocation;\n\n/**\n * Module for assisted injection "
  },
  {
    "path": "src/di/Bind.php",
    "chars": 5086,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInterceptor;\nuse Ray\\Aop\\ReflectionClass;\nuse Refl"
  },
  {
    "path": "src/di/BindValidator.php",
    "chars": 2188,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInterceptor;\nuse Ray\\Aop\\NullInterceptor;\nuse Ray\\"
  },
  {
    "path": "src/di/BuiltinModule.php",
    "chars": 384,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\MultiBinding\\MultiBindingModule;\n\nfinal class BuiltinModu"
  },
  {
    "path": "src/di/CompileNullObject.php",
    "chars": 531,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\n/**\n * Convert NullObjectDependency to Dependency\n */\nfinal class Co"
  },
  {
    "path": "src/di/Container.php",
    "chars": 5774,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse BadMethodCallException;\nuse Ray\\Aop\\Compiler;\nuse Ray\\Aop\\Compil"
  },
  {
    "path": "src/di/ContainerFactory.php",
    "chars": 1392,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\Compiler;\n\nuse function array_shift;\n\n/**\n * @psalm-impo"
  },
  {
    "path": "src/di/Dependency.php",
    "chars": 4013,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\Bind as AopBind;\nuse Ray\\Aop\\CompilerInterface;\nuse Ray\\"
  },
  {
    "path": "src/di/DependencyFactory.php",
    "chars": 1650,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\ReflectionClass;\nuse ReflectionMethod;\n\nuse function ass"
  },
  {
    "path": "src/di/DependencyInterface.php",
    "chars": 843,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\n/**\n * @psalm-import-type DependencyContainer from Types\n * @psalm-i"
  },
  {
    "path": "src/di/DependencyProvider.php",
    "chars": 2062,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse function assert;\nuse function sprintf;\n\nfinal class DependencyPr"
  },
  {
    "path": "src/di/Di/Assisted.php",
    "chars": 348,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Di;\n\nuse Attribute;\n\n/**\n * Marks a parameter for assisted injection\n "
  },
  {
    "path": "src/di/Di/Inject.php",
    "chars": 747,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Di;\n\nuse Attribute;\n\n/**\n * Annotates your class methods into which th"
  },
  {
    "path": "src/di/Di/InjectInterface.php",
    "chars": 210,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Di;\n\ninterface InjectInterface\n{\n    /**\n     * Whether or not to use "
  },
  {
    "path": "src/di/Di/Named.php",
    "chars": 314,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Di;\n\nuse Attribute;\n\n/**\n * Annotates named things\n *\n * @psalm-immuta"
  },
  {
    "path": "src/di/Di/PostConstruct.php",
    "chars": 769,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Di;\n\nuse Attribute;\n\n/**\n * PostConstruct\n *\n * The PostConstruct anno"
  },
  {
    "path": "src/di/Di/Qualifier.php",
    "chars": 179,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Di;\n\nuse Attribute;\n\n/**\n * Identifies qualifier annotations\n */\n#[Att"
  },
  {
    "path": "src/di/Di/Set.php",
    "chars": 371,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Di;\n\nuse Attribute;\n\n/**\n * @template T of object\n * @psalm-immutable\n"
  },
  {
    "path": "src/di/Exception/DirectoryNotWritable.php",
    "chars": 177,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\nuse RuntimeException;\n\nfinal class DirectoryNotWritable ex"
  },
  {
    "path": "src/di/Exception/ExceptionInterface.php",
    "chars": 95,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\ninterface ExceptionInterface\n{\n}\n"
  },
  {
    "path": "src/di/Exception/InvalidContext.php",
    "chars": 187,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\nuse InvalidArgumentException;\n\nfinal class InvalidContext "
  },
  {
    "path": "src/di/Exception/InvalidProvider.php",
    "chars": 188,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\nuse InvalidArgumentException;\n\nfinal class InvalidProvider"
  },
  {
    "path": "src/di/Exception/InvalidToConstructorNameParameter.php",
    "chars": 276,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\nuse InvalidArgumentException;\n\n/**\n * @see https://github."
  },
  {
    "path": "src/di/Exception/InvalidType.php",
    "chars": 184,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\nuse InvalidArgumentException;\n\nfinal class InvalidType ext"
  },
  {
    "path": "src/di/Exception/MethodInvocationNotAvailable.php",
    "chars": 123,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\nfinal class MethodInvocationNotAvailable extends Unbound\n{"
  },
  {
    "path": "src/di/Exception/NoHint.php",
    "chars": 392,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\n/**\n * Exception thrown when a parameter has no type hint "
  },
  {
    "path": "src/di/Exception/NotFound.php",
    "chars": 161,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\nuse LogicException;\n\nfinal class NotFound extends LogicExc"
  },
  {
    "path": "src/di/Exception/SetNotFound.php",
    "chars": 164,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\nuse LogicException;\n\nfinal class SetNotFound extends Logic"
  },
  {
    "path": "src/di/Exception/Unbound.php",
    "chars": 1710,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\nuse Exception;\nuse LogicException;\n\nuse function array_pop"
  },
  {
    "path": "src/di/Exception/Untargeted.php",
    "chars": 105,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Exception;\n\nfinal class Untargeted extends Unbound\n{\n}\n"
  },
  {
    "path": "src/di/Exception.php",
    "chars": 97,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nfinal class Exception extends \\Exception\n{\n}\n"
  },
  {
    "path": "src/di/Grapher.php",
    "chars": 1679,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\Compiler;\n\nuse function file_exists;\nuse function spl_au"
  },
  {
    "path": "src/di/InjectableInterface.php",
    "chars": 86,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\ninterface InjectableInterface\n{\n}\n"
  },
  {
    "path": "src/di/InjectionPoint.php",
    "chars": 2467,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\ReflectionClass;\nuse Ray\\Aop\\ReflectionMethod;\nuse Ray\\D"
  },
  {
    "path": "src/di/InjectionPointInterface.php",
    "chars": 759,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\ReflectionClass;\nuse Ray\\Aop\\ReflectionMethod;\nuse Refle"
  },
  {
    "path": "src/di/InjectionPoints.php",
    "chars": 1502,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse ReflectionException;\nuse ReflectionMethod;\n\n/**\n * @psalm-import"
  },
  {
    "path": "src/di/Injector.php",
    "chars": 2825,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\Compiler;\nuse Ray\\Di\\Exception\\DirectoryNotWritable;\nuse"
  },
  {
    "path": "src/di/InjectorInterface.php",
    "chars": 736,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\n/**\n * Builds the graphs of objects that make up your application\n *"
  },
  {
    "path": "src/di/Instance.php",
    "chars": 1321,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse function gettype;\nuse function is_object;\nuse function is_scalar"
  },
  {
    "path": "src/di/Matcher/AssistedInjectMatcher.php",
    "chars": 1240,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Matcher;\n\nuse LogicException;\nuse Ray\\Aop\\AbstractMatcher;\nuse Ray\\Di\\"
  },
  {
    "path": "src/di/MethodInvocationProvider.php",
    "chars": 790,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInvocation;\nuse Ray\\Di\\Exception\\MethodInvocationN"
  },
  {
    "path": "src/di/ModuleString.php",
    "chars": 1094,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse function assert;\nuse function implode;\nuse function serialize;\nu"
  },
  {
    "path": "src/di/MultiBinder.php",
    "chars": 2418,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\MultiBinding\\LazyInstance;\nuse Ray\\Di\\MultiBinding\\LazyIn"
  },
  {
    "path": "src/di/MultiBinding/LazyInstance.php",
    "chars": 470,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\MultiBinding;\n\nuse Ray\\Di\\InjectorInterface;\n\n/**\n * @template T of mi"
  },
  {
    "path": "src/di/MultiBinding/LazyInterface.php",
    "chars": 220,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\MultiBinding;\n\nuse Ray\\Di\\InjectorInterface;\n\ninterface LazyInterface\n"
  },
  {
    "path": "src/di/MultiBinding/LazyProvider.php",
    "chars": 544,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\MultiBinding;\n\nuse Ray\\Di\\InjectorInterface;\nuse Ray\\Di\\ProviderInterf"
  },
  {
    "path": "src/di/MultiBinding/LazyTo.php",
    "chars": 454,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\MultiBinding;\n\nuse Ray\\Di\\InjectorInterface;\n\n/**\n * @template T of ob"
  },
  {
    "path": "src/di/MultiBinding/Map.php",
    "chars": 2119,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\MultiBinding;\n\nuse ArrayAccess;\nuse Countable;\nuse Generator;\nuse Iter"
  },
  {
    "path": "src/di/MultiBinding/MapProvider.php",
    "chars": 1037,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\MultiBinding;\n\nuse Ray\\Di\\Di\\Set;\nuse Ray\\Di\\Exception\\SetNotFound;\nus"
  },
  {
    "path": "src/di/MultiBinding/MultiBindingModule.php",
    "chars": 389,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\MultiBinding;\n\nuse Ray\\Di\\AbstractModule;\nuse Ray\\Di\\Types;\n\n/**\n * @p"
  },
  {
    "path": "src/di/MultiBinding/MultiBindings.php",
    "chars": 489,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\MultiBinding;\n\nuse ArrayObject;\nuse Ray\\Di\\Types;\n\nuse function array_"
  },
  {
    "path": "src/di/Name.php",
    "chars": 3460,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Di\\Named;\nuse Ray\\Di\\Di\\Qualifier;\nuse ReflectionAttribut"
  },
  {
    "path": "src/di/NewInstance.php",
    "chars": 2957,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\Bind as AopBind;\nuse Ray\\Aop\\WeavedInterface;\nuse Reflec"
  },
  {
    "path": "src/di/NullDependency.php",
    "chars": 624,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\n/**\n * @codeCoverageIgnore\n */\nfinal class NullDependency implements"
  },
  {
    "path": "src/di/NullModule.php",
    "chars": 155,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nfinal class NullModule extends AbstractModule\n{\n    protected functi"
  },
  {
    "path": "src/di/NullObjectDependency.php",
    "chars": 1190,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Koriym\\NullObject\\NullObject;\nuse ReflectionClass;\n\nuse function"
  },
  {
    "path": "src/di/ProviderInterface.php",
    "chars": 176,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\n/**\n * @template T of mixed\n */\ninterface ProviderInterface\n{\n    /*"
  },
  {
    "path": "src/di/ProviderProvider.php",
    "chars": 486,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Di\\Set;\n\n/**\n * @implements ProviderInterface<mixed>\n * @"
  },
  {
    "path": "src/di/ProviderSetModule.php",
    "chars": 249,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nfinal class ProviderSetModule extends AbstractModule\n{\n    protected"
  },
  {
    "path": "src/di/ProviderSetProvider.php",
    "chars": 806,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Di\\Set;\nuse Ray\\Di\\Exception\\SetNotFound;\n\n/**\n * @implem"
  },
  {
    "path": "src/di/Scope.php",
    "chars": 237,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nfinal class Scope\n{\n    /**\n     * Singleton scope\n     */\n    publi"
  },
  {
    "path": "src/di/SetContextInterface.php",
    "chars": 236,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\ninterface SetContextInterface\n{\n    /**\n     * Set provider context\n"
  },
  {
    "path": "src/di/SetterMethod.php",
    "chars": 1871,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Exception;\nuse LogicException;\nuse Ray\\Di\\Exception\\Unbound;\nuse"
  },
  {
    "path": "src/di/SetterMethods.php",
    "chars": 939,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Exception;\n\n/**\n * @psalm-import-type SetterMethodsList from Typ"
  },
  {
    "path": "src/di/SpyCompiler.php",
    "chars": 2550,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse LogicException;\nuse Ray\\Aop\\BindInterface;\nuse Ray\\Aop\\CompilerI"
  },
  {
    "path": "src/di/Types.php",
    "chars": 3158,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInterceptor;\nuse Ray\\Aop\\Pointcut;\n\n/**\n * Ray.Di "
  },
  {
    "path": "src/di/Untarget.php",
    "chars": 865,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\ReflectionClass;\n\nfinal class Untarget\n{\n    /**\n     * "
  },
  {
    "path": "src/di/VisitorInterface.php",
    "chars": 1954,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\Bind as AopBind;\nuse ReflectionParameter;\n\n/**\n * Visits"
  },
  {
    "path": "src-deprecated/di/BcParameterQualifier.php",
    "chars": 4726,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Di\\InjectInterface;\nuse Ray\\Di\\Di\\Qualifier;\nuse Reflecti"
  },
  {
    "path": "src-deprecated/di/BcStringParser.php",
    "chars": 1299,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse function explode;\nuse function substr;\nuse function trim;\n\n/**\n "
  },
  {
    "path": "src-deprecated/di/EmptyModule.php",
    "chars": 235,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\n/**\n * @deprecated User NullModule instead\n */\nclass EmptyModule ext"
  },
  {
    "path": "src-deprecated/di/NullCache.php",
    "chars": 867,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Doctrine\\Common\\Cache\\CacheProvider;\n\n/**\n * @psalm-suppress Dep"
  },
  {
    "path": "src-deprecated/di/Provider.php",
    "chars": 155,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\n/**\n * @deprecated User ProviderInterface instead\n */\ninterface Prov"
  },
  {
    "path": "tests/bootstrap.php",
    "chars": 436,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nputenv('TMPDIR=' . __DIR__ . '/tmp');\n\nrequire_once dirname(__DIR__) . '/vendor/autoloa"
  },
  {
    "path": "tests/di/AnnotatedClassTest.php",
    "chars": 2860,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse LogicException;\nuse PHPUnit\\Framework\\TestCase;\nuse Ray\\Aop\\Refl"
  },
  {
    "path": "tests/di/ArgumentTest.php",
    "chars": 1223,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse PHPUnit\\Framework\\TestCase;\nuse ReflectionMethod;\nuse Reflection"
  },
  {
    "path": "tests/di/ArgumentsTest.php",
    "chars": 1311,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse PHPUnit\\Framework\\TestCase;\nuse ReflectionMethod;\nuse Reflection"
  },
  {
    "path": "tests/di/AssistedTest.php",
    "chars": 2392,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Ray\\Di\\Exception\\MethodInvocatio"
  },
  {
    "path": "tests/di/BcParameterQualifierIntegrationTest.php",
    "chars": 651,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass BcParameterQualifierIntegrati"
  },
  {
    "path": "tests/di/BcParameterQualifierTest.php",
    "chars": 2934,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Ray\\Di\\Annotation\\FakeInjectOne;"
  },
  {
    "path": "tests/di/BcStringParserTest.php",
    "chars": 927,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass BcStringParserTest extends Te"
  },
  {
    "path": "tests/di/BindTest.php",
    "chars": 5828,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Ray\\Di\\Exception\\InvalidProvider"
  },
  {
    "path": "tests/di/ContainerTest.php",
    "chars": 7160,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse BadMethodCallException;\nuse PHPUnit\\Framework\\TestCase;\nuse Ray\\"
  },
  {
    "path": "tests/di/ContextualProviderTest.php",
    "chars": 413,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass ContextualProviderTest extend"
  },
  {
    "path": "tests/di/DependencyTest.php",
    "chars": 5023,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Ray\\Aop\\Compiler;\nuse Ray\\Aop\\Ma"
  },
  {
    "path": "tests/di/Fake/Annotation/FakeInjectOne.php",
    "chars": 339,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Annotation;\n\nuse Attribute;\nuse Ray\\Di\\Di\\InjectInterface;\nuse Ray\\Di\\"
  },
  {
    "path": "tests/di/Fake/Annotation/FakeLeft.php",
    "chars": 153,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Annotation;\n\nuse Attribute;\nuse Ray\\Di\\Di\\Qualifier;\n\n#[Attribute, Qua"
  },
  {
    "path": "tests/di/Fake/Annotation/FakeNotQualifer.php",
    "chars": 149,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Annotation;\n\nuse Attribute;\nuse Ray\\Di\\Di\\Qualifier;\n\n#[Attribute]\nfin"
  },
  {
    "path": "tests/di/Fake/Annotation/FakeQualifierOnly.php",
    "chars": 257,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Annotation;\n\nuse Attribute;\nuse Ray\\Di\\Di\\Qualifier;\n\n/**\n * Qualifier"
  },
  {
    "path": "tests/di/Fake/Annotation/FakeRight.php",
    "chars": 154,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di\\Annotation;\n\nuse Attribute;\nuse Ray\\Di\\Di\\Qualifier;\n\n#[Attribute, Qua"
  },
  {
    "path": "tests/di/Fake/FakeAbstractClass.php",
    "chars": 89,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nabstract class FakeAbstractClass\n{\n}\n"
  },
  {
    "path": "tests/di/Fake/FakeAbstractDb.php",
    "chars": 172,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAbstractDb\n{\n    public $dbId;\n\n    public function __cons"
  },
  {
    "path": "tests/di/Fake/FakeAnnoClass.php",
    "chars": 161,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_CLASS)]\nclass FakeAnno"
  },
  {
    "path": "tests/di/Fake/FakeAnnoInterceptor1.php",
    "chars": 330,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInterceptor;\nuse Ray\\Aop\\MethodInvocation;\n\nclass "
  },
  {
    "path": "tests/di/Fake/FakeAnnoInterceptor2.php",
    "chars": 330,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInterceptor;\nuse Ray\\Aop\\MethodInvocation;\n\nclass "
  },
  {
    "path": "tests/di/Fake/FakeAnnoInterceptor3.php",
    "chars": 330,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInterceptor;\nuse Ray\\Aop\\MethodInvocation;\n\nclass "
  },
  {
    "path": "tests/di/Fake/FakeAnnoInterceptor4.php",
    "chars": 330,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInterceptor;\nuse Ray\\Aop\\MethodInvocation;\n\nclass "
  },
  {
    "path": "tests/di/Fake/FakeAnnoInterceptor5.php",
    "chars": 330,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInterceptor;\nuse Ray\\Aop\\MethodInvocation;\n\nclass "
  },
  {
    "path": "tests/di/Fake/FakeAnnoInterceptorInterface.php",
    "chars": 183,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\MethodInterceptor;\nuse Ray\\Aop\\MethodInvocation;\n\ninterf"
  },
  {
    "path": "tests/di/Fake/FakeAnnoMethod1.php",
    "chars": 133,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nclass FakeAnn"
  },
  {
    "path": "tests/di/Fake/FakeAnnoMethod2.php",
    "chars": 133,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nclass FakeAnn"
  },
  {
    "path": "tests/di/Fake/FakeAnnoMethod3.php",
    "chars": 133,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Attribute;\n\n#[Attribute(Attribute::TARGET_METHOD)]\nclass FakeAnn"
  },
  {
    "path": "tests/di/Fake/FakeAnnoModule.php",
    "chars": 1086,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAnnoModule extends AbstractModule\n{\n    protected function"
  },
  {
    "path": "tests/di/Fake/FakeAnnoOrderClass.php",
    "chars": 205,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\n#[FakeAnnoClass]\nclass FakeAnnoOrderClass\n{\n    #[FakeAnnoMethod1]\n "
  },
  {
    "path": "tests/di/Fake/FakeAop.php",
    "chars": 164,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAop implements FakeAopInterface\n{\n    public function retu"
  },
  {
    "path": "tests/di/Fake/FakeAopDoublyInstallModule.php",
    "chars": 325,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAopDoublyInstallModule extends AbstractModule\n{\n    protec"
  },
  {
    "path": "tests/di/Fake/FakeAopGrapher.php",
    "chars": 259,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAopGrapher implements FakeAopInterface\n{\n    public $a;\n\n "
  },
  {
    "path": "tests/di/Fake/FakeAopGrapherModule.php",
    "chars": 382,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAopGrapherModule extends AbstractModule\n{\n    protected fu"
  },
  {
    "path": "tests/di/Fake/FakeAopInstallModule.php",
    "chars": 263,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAopInstallModule extends AbstractModule\n{\n    protected fu"
  },
  {
    "path": "tests/di/Fake/FakeAopInterceptorModule.php",
    "chars": 313,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAopInterceptorModule extends AbstractModule\n{\n    protecte"
  },
  {
    "path": "tests/di/Fake/FakeAopInterface.php",
    "chars": 119,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\ninterface FakeAopInterface\n{\n    public function returnSame($a);\n}\n"
  },
  {
    "path": "tests/di/Fake/FakeAopInterfaceModule.php",
    "chars": 221,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAopInterfaceModule extends AbstractModule\n{\n    protected "
  },
  {
    "path": "tests/di/Fake/FakeAopModule.php",
    "chars": 368,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAopModule extends AbstractModule\n{\n    protected function "
  },
  {
    "path": "tests/di/Fake/FakeAssistedConsumer.php",
    "chars": 758,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Di\\Assisted;\nuse Ray\\Di\\Di\\Named;\n\nclass FakeAssistedCons"
  },
  {
    "path": "tests/di/Fake/FakeAssistedDb.php",
    "chars": 118,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAssistedDb extends FakeAbstractDb\n{\n    public $dbId;\n}\n"
  },
  {
    "path": "tests/di/Fake/FakeAssistedDbModule.php",
    "chars": 240,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAssistedDbModule extends AbstractModule\n{\n    protected fu"
  },
  {
    "path": "tests/di/Fake/FakeAssistedDbProvider.php",
    "chars": 533,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeAssistedDbProvider implements ProviderInterface\n{\n    /** "
  },
  {
    "path": "tests/di/Fake/FakeAssistedInjectConsumer.php",
    "chars": 901,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Annotation\\FakeInjectOne;\nuse Ray\\Di\\Di\\Assisted;\nuse Ray"
  },
  {
    "path": "tests/di/Fake/FakeAssistedInjectDb.php",
    "chars": 299,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Di\\Assisted;\nuse Ray\\Di\\Di\\Inject;\n\nclass FakeAssistedInj"
  },
  {
    "path": "tests/di/Fake/FakeAssistedParamsConsumer.php",
    "chars": 285,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Di\\Assisted;\n\nclass FakeAssistedParamsConsumer\n{\n    /**\n"
  },
  {
    "path": "tests/di/Fake/FakeBcConstructorQualifierClass.php",
    "chars": 386,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Annotation\\FakeQualifierOnly;\n\nclass FakeBcConstructorQua"
  },
  {
    "path": "tests/di/Fake/FakeBcParameterQualifierClass.php",
    "chars": 2008,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Annotation\\FakeInjectOne;\nuse Ray\\Di\\Di\\Inject;\n\nclass Fa"
  },
  {
    "path": "tests/di/Fake/FakeBcParameterQualifierModule.php",
    "chars": 400,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Annotation\\FakeInjectOne;\n\nclass FakeBcParameterQualifier"
  },
  {
    "path": "tests/di/Fake/FakeBuiltin.php",
    "chars": 207,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeBuiltin\n{\n    public $injector;\n\n    public function __con"
  },
  {
    "path": "tests/di/Fake/FakeCar.php",
    "chars": 1992,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Di\\Inject;\nuse Ray\\Di\\Di\\Named;\nuse Ray\\Di\\Di\\PostConstru"
  },
  {
    "path": "tests/di/Fake/FakeCarEngine.php",
    "chars": 95,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeCarEngine extends FakeEngine\n{\n}\n"
  },
  {
    "path": "tests/di/Fake/FakeCarEngineModule.php",
    "chars": 218,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeCarEngineModule extends AbstractModule\n{\n    protected fun"
  },
  {
    "path": "tests/di/Fake/FakeCarInterface.php",
    "chars": 83,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\ninterface FakeCarInterface\n{\n}\n"
  },
  {
    "path": "tests/di/Fake/FakeCarModule.php",
    "chars": 1232,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Aop\\NullInterceptor;\n\nclass FakeCarModule extends AbstractMo"
  },
  {
    "path": "tests/di/Fake/FakeClassInstanceBindModule.php",
    "chars": 367,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeClassInstanceBindModule extends AbstractModule\n{\n    priva"
  },
  {
    "path": "tests/di/Fake/FakeClassWithBcParameterQualifier.php",
    "chars": 463,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Ray\\Di\\Annotation\\FakeInjectOne;\n\nclass FakeClassWithBcParameter"
  },
  {
    "path": "tests/di/Fake/FakeConcreteClass.php",
    "chars": 173,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nclass FakeConcreteClass\n{\n    public function __construct(FakeAbstra"
  },
  {
    "path": "tests/di/Fake/FakeConstant.php",
    "chars": 306,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Ray\\Di;\n\nuse Attribute;\nuse Ray\\Di\\Di\\Qualifier;\n\n#[Attribute(Attribute::TARG"
  }
]

// ... and 111 more files (download for full content)

About this extraction

This page contains the full source code of the ray-di/Ray.Di GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 311 files (279.0 KB), approximately 77.0k tokens, and a symbol index with 940 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!