Showing preview only (253K chars total). Download the full file or copy to clipboard to get everything.
Repository: yiisoft/di
Branch: master
Commit: 10ffafe94415
Files: 105
Total size: 227.9 KB
Directory structure:
gitextract_8vkzrttm/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── SECURITY.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── bc.yml
│ ├── bechmark.yml
│ ├── build.yml
│ ├── composer-require-checker.yml
│ ├── mutation.yml
│ ├── rector-cs.yml
│ └── static.yml
├── .gitignore
├── .php-cs-fixer.dist.php
├── .phpstorm.meta.php
├── .phpunit-watcher.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── benchmarks.md
├── composer.json
├── docs/
│ └── internals.md
├── infection.json.dist
├── phpbench.json
├── phpcs.xml.dist
├── phpunit.xml.dist
├── psalm.xml
├── rector.php
├── src/
│ ├── BuildingException.php
│ ├── CompositeContainer.php
│ ├── CompositeNotFoundException.php
│ ├── Container.php
│ ├── ContainerConfig.php
│ ├── ContainerConfigInterface.php
│ ├── ExtensibleService.php
│ ├── Helpers/
│ │ ├── DefinitionNormalizer.php
│ │ └── DefinitionParser.php
│ ├── NotFoundException.php
│ ├── Reference/
│ │ └── TagReference.php
│ ├── ServiceProviderInterface.php
│ └── StateResetter.php
├── tests/
│ ├── Benchmark/
│ │ ├── ContainerBench.php
│ │ └── ContainerMethodHasBench.php
│ ├── Support/
│ │ ├── A.php
│ │ ├── B.php
│ │ ├── Car.php
│ │ ├── CarExtensionProvider.php
│ │ ├── CarFactory.php
│ │ ├── CarProvider.php
│ │ ├── ColorInterface.php
│ │ ├── ColorPink.php
│ │ ├── ColorRed.php
│ │ ├── ConstructorTestClass.php
│ │ ├── ContainerInterfaceExtensionProvider.php
│ │ ├── Cycle/
│ │ │ ├── Chicken.php
│ │ │ └── Egg.php
│ │ ├── EngineFactory.php
│ │ ├── EngineInterface.php
│ │ ├── EngineMarkOne.php
│ │ ├── EngineMarkTwo.php
│ │ ├── EngineStorage.php
│ │ ├── Garage.php
│ │ ├── GearBox.php
│ │ ├── InvokableCarFactory.php
│ │ ├── MethodTestClass.php
│ │ ├── NonPsrContainer.php
│ │ ├── NullCarExtensionProvider.php
│ │ ├── NullableConcreteDependency.php
│ │ ├── OptionalConcreteDependency.php
│ │ ├── PropertyTestClass.php
│ │ ├── SportCar.php
│ │ ├── StaticFactory.php
│ │ ├── TreeItem.php
│ │ ├── UnionTypeInConstructorFirstTypeInParamResolvable.php
│ │ ├── UnionTypeInConstructorParamNotResolvable.php
│ │ ├── UnionTypeInConstructorSecondParamNotResolvable.php
│ │ ├── UnionTypeInConstructorSecondTypeInParamResolvable.php
│ │ └── VariadicConstructor.php
│ └── Unit/
│ ├── BuildingExceptionTest.php
│ ├── CompositeContainerTest.php
│ ├── CompositePsrContainerOverLeagueTest.php
│ ├── CompositePsrContainerOverYiisoftTest.php
│ ├── CompositePsrContainerTestAbstract.php
│ ├── Container/
│ │ └── DependencyFromDelegate/
│ │ ├── Car.php
│ │ ├── DependencyFromDelegateTest.php
│ │ ├── Engine.php
│ │ └── EngineInterface.php
│ ├── ContainerTest.php
│ ├── Helpers/
│ │ └── DefinitionParserTest.php
│ ├── LeaguePsrContainerTest.php
│ ├── NotFoundExceptionTest.php
│ ├── PsrContainerTestAbstract.php
│ ├── Reference/
│ │ └── TagReference/
│ │ ├── Resolve/
│ │ │ ├── A.php
│ │ │ ├── B.php
│ │ │ ├── Main.php
│ │ │ └── TagReferenceResolveTest.php
│ │ └── TagReferenceTest.php
│ ├── ServiceProviderTest.php
│ ├── StateResetterTest.php
│ └── YiisoftPsrContainerTest.php
└── tools/
├── .gitignore
├── infection/
│ └── composer.json
└── psalm/
└── composer.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.php]
ij_php_space_before_short_closure_left_parenthesis = true
ij_php_space_after_type_cast = true
[*.yml]
indent_size = 2
================================================
FILE: .gitattributes
================================================
# Autodetect text files
* text=auto eol=lf
# ...Unless the name matches the following overriding patterns
# Definitively text files
*.php text
*.css text
*.js text
*.txt text
*.md text
*.xml text
*.json text
*.bat text
*.sql text
*.yml text
# Ensure those won't be messed up with
*.png binary
*.jpg binary
*.gif binary
*.ttf binary
# Ignore some meta files when creating an archive of this repository
/.github export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/phpunit.xml.dist export-ignore
/tests export-ignore
/docs export-ignore
# Avoid merge conflicts in CHANGELOG
# https://about.gitlab.com/2015/02/10/gitlab-reduced-merge-conflicts-by-90-percent-with-changelog-placeholders/
/CHANGELOG.md merge=union
================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Yii Contributor Code of Conduct
## Our Pledge
As contributors and maintainers of this project, and in order to keep Yii community open and welcoming, we ask to
respect all community members.
## Our Standards
Examples of behavior that contributes to a positive environment for our community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery, and sexual attention or advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
Core team members are responsible for clarifying and enforcing our standards of acceptable behavior and will take
appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Core team members have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits,
issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for
moderation decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing
the community in public spaces. Examples of representing a project or community include using an official e-mail
address, posting via an official social media account, within project GitHub, official forum or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting core team members. All
complaints will be reviewed and investigated promptly and fairly.
All core team members are obligated to respect the privacy and security of the reporter of any incident.
## Enforcement Guidelines
Core team members will follow these Community Impact Guidelines in determining the consequences for any action they
deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in
the community.
**Consequence**: A private, written warning from core team members, providing clarity around the nature of the violation
and an explanation of why the behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of actions.
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including
unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding
interactions in community spaces as well as external channels like social media. Violating these terms may lead to
a temporary or permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified
period of time. No public or private interaction with the people involved, including unsolicited interaction with those
enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate
behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Prerequisites
- [Yii goal and values](https://github.com/yiisoft/docs/blob/master/001-yii-values.md)
- [Namespaces](https://github.com/yiisoft/docs/blob/master/004-namespaces.md)
- [Git commit messages](https://github.com/yiisoft/docs/blob/master/006-git-commit-messages.md)
- [Exceptions](https://github.com/yiisoft/docs/blob/master/007-exceptions.md)
- [Interfaces](https://github.com/yiisoft/docs/blob/master/008-interfaces.md)
# Getting started
Since Yii 3 consists of many packages, we have a [special development tool](https://github.com/yiisoft/docs/blob/master/005-development-tool.md).
1. [Clone the repository](https://github.com/yiisoft/yii-dev-tool).
2. [Set up your own fork](https://github.com/yiisoft/yii-dev-tool#using-your-own-fork).
3. Now you are ready. Fork any package listed in `packages.php` and do `./yii-dev install username/package`.
If you don't have any particular package in mind to start with:
- [Check roadmap](https://github.com/yiisoft/docs/blob/master/003-roadmap.md).
- Check package issues at github. Usually there are some.
- Ask @samdark.
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
| Q | A
| ------------- | ---
| Is bugfix? | ✔️/❌
| New feature? | ✔️/❌
| Breaks BC? | ✔️/❌
| Fixed issues | comma-separated list of tickets # fixed by the PR, if any
================================================
FILE: .github/SECURITY.md
================================================
# Security Policy
Please use the [security issue form](https://www.yiiframework.com/security) to report to us any security issue you
find in Yii. DO NOT use the issue tracker or discuss it in the public forum as it will cause more damage than help.
Please note that as a non-commercial OpenSource project we are not able to pay bounties at the moment.
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
# Maintain dependencies for GitHub Actions.
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
# Too noisy. See https://github.community/t/increase-if-necessary-for-github-actions-in-dependabot/179581
open-pull-requests-limit: 0
# Maintain dependencies for Composer
- package-ecosystem: "composer"
directory: "/"
schedule:
interval: "daily"
versioning-strategy: increase-if-necessary
================================================
FILE: .github/workflows/bc.yml
================================================
on:
pull_request:
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'infection.json.dist'
- 'phpunit.xml.dist'
- 'psalm.xml'
push:
branches: ['master']
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'infection.json.dist'
- 'phpunit.xml.dist'
- 'psalm.xml'
name: backwards compatibility
jobs:
roave_bc_check:
uses: yiisoft/actions/.github/workflows/bc.yml@master
with:
os: >-
['ubuntu-latest']
php: >-
['8.4']
================================================
FILE: .github/workflows/bechmark.yml
================================================
on:
pull_request:
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'infection.json.dist'
- 'psalm.xml'
push:
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'infection.json.dist'
- 'psalm.xml'
name: bechmark
jobs:
phpbench:
uses: yiisoft/actions/.github/workflows/phpbench.yml@master
with:
os: >-
['ubuntu-latest', 'windows-latest']
php: >-
['8.1']
================================================
FILE: .github/workflows/build.yml
================================================
on:
pull_request:
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'infection.json.dist'
- 'psalm.xml'
push:
branches: ['master']
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'infection.json.dist'
- 'psalm.xml'
name: build
jobs:
phpunit:
uses: yiisoft/actions/.github/workflows/phpunit.yml@master
secrets:
codecovToken: ${{ secrets.CODECOV_TOKEN }}
with:
os: >-
['ubuntu-latest', 'windows-latest']
php: >-
['8.1', '8.2', '8.3', '8.4', '8.5']
================================================
FILE: .github/workflows/composer-require-checker.yml
================================================
on:
pull_request:
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'infection.json.dist'
- 'phpunit.xml.dist'
- 'psalm.xml'
push:
branches: ['master']
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'infection.json.dist'
- 'phpunit.xml.dist'
- 'psalm.xml'
name: Composer require checker
jobs:
composer-require-checker:
uses: yiisoft/actions/.github/workflows/composer-require-checker.yml@master
with:
os: >-
['ubuntu-latest']
php: >-
['8.1', '8.2', '8.3', '8.4', '8.5']
================================================
FILE: .github/workflows/mutation.yml
================================================
on:
pull_request:
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'psalm.xml'
push:
branches: ['master']
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'psalm.xml'
name: mutation test
jobs:
mutation:
uses: yiisoft/actions/.github/workflows/infection.yml@master
with:
os: >-
['ubuntu-latest']
php: >-
['8.5']
infection-args: "--ignore-msi-with-no-mutations"
secrets:
STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }}
================================================
FILE: .github/workflows/rector-cs.yml
================================================
name: Rector + PHP CS Fixer
on:
pull_request_target:
paths:
- 'src/**'
- 'tests/**'
- '.github/workflows/rector-cs.yml'
- 'composer.json'
- 'rector.php'
- '.php-cs-fixer.dist.php'
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
rector:
uses: yiisoft/actions/.github/workflows/rector-cs.yml@master
secrets:
token: ${{ secrets.YIISOFT_GITHUB_TOKEN }}
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
php: '8.1'
================================================
FILE: .github/workflows/static.yml
================================================
on:
pull_request:
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'infection.json.dist'
- 'phpunit.xml.dist'
push:
branches: ['master']
paths-ignore:
- 'docs/**'
- 'README.md'
- 'CHANGELOG.md'
- '.gitignore'
- '.gitattributes'
- 'infection.json.dist'
- 'phpunit.xml.dist'
name: static analysis
jobs:
psalm:
uses: yiisoft/actions/.github/workflows/psalm.yml@master
with:
os: >-
['ubuntu-latest']
php: >-
['8.1', '8.2', '8.3', '8.4']
================================================
FILE: .gitignore
================================================
# phpstorm project files
.idea
# netbeans project files
nbproject
# zend studio for eclipse project files
.buildpath
.project
.settings
# windows thumbnail cache
Thumbs.db
# composer vendor dir
/vendor
/composer.lock
# composer itself is not needed
composer.phar
# Mac DS_Store Files
.DS_Store
# PhpUnit
/phpunit.phar
/phpunit.xml
/.phpunit.cache
# Static analysis
analysis.txt
# Code coverage HTML
/coverage
# PHP CS Fixer
/.php-cs-fixer.cache
/.php-cs-fixer.php
================================================
FILE: .php-cs-fixer.dist.php
================================================
<?php
declare(strict_types=1);
use PhpCsFixer\Config;
use PhpCsFixer\Finder;
use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;
$finder = (new Finder())->in([
__DIR__ . '/src',
__DIR__ . '/tests',
]);
return (new Config())
->setRiskyAllowed(true)
->setParallelConfig(ParallelConfigFactory::detect())
->setRules([
'@PER-CS3.0' => true,
'no_unused_imports' => true,
'ordered_class_elements' => true,
'class_attributes_separation' => ['elements' => ['method' => 'one']],
'declare_strict_types' => true,
'native_function_invocation' => true,
'native_constant_invocation' => true,
'fully_qualified_strict_types' => [
'import_symbols' => true
],
'global_namespace_import' => [
'import_classes' => true,
'import_constants' => true,
'import_functions' => true,
],
])
->setFinder($finder);
================================================
FILE: .phpstorm.meta.php
================================================
<?php
// .phpstorm.meta.php
namespace PHPSTORM_META {
override(
\Psr\Container\ContainerInterface::get(0),
map([
'' => '@',
])
);
}
================================================
FILE: .phpunit-watcher.yml
================================================
watch:
directories:
- src
- tests
fileMask: '*.php'
notifications:
passingTests: false
failingTests: false
phpunit:
binaryPath: vendor/bin/phpunit
timeout: 180
================================================
FILE: CHANGELOG.md
================================================
# Yii Dependency Injection Change Log
## 1.4.2 under development
- Enh #397: Explicitly import functions in "use" section (@mspirkov)
## 1.4.1 December 01, 2025
- Enh #393: Add PHP 8.5 support (@vjik)
## 1.4.0 May 30, 2025
- New #380: Add `TagReference::id()` method (@vjik)
- Chg #390: Change PHP constraint in `composer.json` to `8.1 - 8.4` (@vjik)
- Enh #324: Make `BuildingException` and `NotFoundException` friendly (@np25071984)
- Enh #384: Make `$config` parameter in `Container` constructor optional (@np25071984)
- Enh #387: Improve container performance (@samdark)
- Bug #390: Explicitly mark nullable parameters (@vjik)
## 1.3.0 October 14, 2024
- Enh #353: Add shortcut for tag reference #333 (@xepozz)
- Enh #356: Improve usage `NotFoundException` for cases with definitions (@vjik)
- Enh #364: Minor refactoring to improve performance of container (@samdark)
- Enh #375: Raise minimum PHP version to `^8.1` and refactor code (@vjik)
- Enh #376: Add default value `true` for parameter of `ContainerConfig::withStrictMode()` and
`ContainerConfig::withValidate()` methods (@vjik)
## 1.2.1 December 23, 2022
- Chg #316: Fix exception messages (@xepozz)
- Bug #317: Fix delegated container (@xepozz)
## 1.2.0 November 05, 2022
- Chg #310: Adopt to `yiisoft/definition` version `^3.0` (@vjik)
- Enh #308: Raise minimum PHP version to `^8.0` and refactor code (@xepozz, @vjik)
## 1.1.0 June 24, 2022
- Chg #263: Raise minimal required version of `psr/container` to `^1.1|^2.0` (@xepozz, @vjik)
## 1.0.3 June 17, 2022
- Enh #302: Improve performance collecting tags (samdark)
- Enh #303: Add support for `yiisoft/definitions` version `^2.0` (@vjik)
## 1.0.2 February 14, 2022
- Bug #297: Fix method name `TagHelper::extractTagFromAlias` (@rustamwin)
## 1.0.1 December 21, 2021
- Bug #293: Fix `ExtensibleService` normalization bug (@yiiliveext)
## 1.0.0 December 03, 2021
- Initial release.
================================================
FILE: LICENSE.md
================================================
Copyright © 2008 by Yii Software (<https://www.yiiframework.com/>)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Yii Software nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
<p align="center">
<a href="https://github.com/yiisoft" target="_blank">
<img src="https://yiisoft.github.io/docs/images/yii_logo.svg" height="100px" alt="Yii">
</a>
<h1 align="center">Yii Dependency Injection</h1>
<br>
</p>
[](https://packagist.org/packages/yiisoft/di)
[](https://packagist.org/packages/yiisoft/di)
[](https://github.com/yiisoft/di/actions/workflows/build.yml)
[](https://codecov.io/gh/yiisoft/di)
[](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/di/master)
[](https://github.com/yiisoft/di/actions?query=workflow%3A%22static+analysis%22)
[](https://shepherd.dev/github/yiisoft/di)
[PSR-11](https://www.php-fig.org/psr/psr-11/) compatible
[dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container that's able to instantiate
and configure classes resolving dependencies.
## Features
- [PSR-11](https://www.php-fig.org/psr/psr-11/) compatible.
- Supports property injection, constructor injection, and method injection.
- Detects circular references.
- Accepts array definitions. You can use it with mergeable configs.
- Provides optional autoload fallback for classes without explicit definition.
- Allows delegated lookup and has a composite container.
- Supports aliasing.
- Supports service providers.
- Has state resetter for long-running workers serving many requests, such as [RoadRunner](https://roadrunner.dev/)
or [Swoole](https://www.swoole.co.uk/).
- Supports container delegates.
- Does auto-wiring.
> [!NOTE]
> The container contains only shared instances. If you need a factory, use the dedicated [yiisoft/factory](https://github.com/yiisoft/factory) package.
## Requirements
- PHP 8.1 - 8.5.
- `Multibyte String` PHP extension.
## Installation
You could install the package with composer:
```shell
composer require yiisoft/di
```
## Using the container
Usage of the DI container is simple: You first initialize it with an
array of *definitions*. The array keys are usually interface names. It will
then use these definitions to create an object whenever the application requests that type.
This happens, for example, when fetching a type directly from the container
somewhere in the application. But objects are also created implicitly if a
definition has a dependency on another definition.
Usually one uses a single container for the whole application. It's often
configured either in the entry script such as `index.php` or a configuration
file:
```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$config = ContainerConfig::create()
->withDefinitions($definitions);
$container = new Container($config);
```
You could store the definitions in a `.php` file that returns an array:
```php
return [
// resolve EngineMarkOne dependencies automatically
EngineInterface::class => EngineMarkOne::class,
// full definition
MyServiceInterface::class => [
'class' => MyService::class,
// call the constructor, pass named argument "amount"
'__construct()' => [
'amount' => 42,
'db' => Reference::to(SecondaryConnection::class), // instance of another dependency
],
// set a public property
'$name' => 'Alex',
// call a public method
'setDiscount()' => [10],
],
// closure for complicated cases
AnotherServiceInterface::class => static function(ConnectionInterface $db) {
return new AnotherService($db);
},
// factory
MyObjectInterface::class => fn () => MyFactory::create('args'),
// static call
MyObjectInterface2::class => [MyFactory::class, 'create'],
// direct instance
MyInterface::class => new MyClass(),
];
```
You can define an object in several ways:
- In the simple case, an interface definition maps an id to a particular class.
- A full definition describes how to instantiate a class in more detail:
- `class` has the name of the class to instantiate.
- `__construct()` holds an array of constructor arguments.
- The rest of the config is property values (prefixed with `$`) and method calls, postfixed with `()`. They're
set/called in the order they appear in the array.
- Closures are useful if instantiation is tricky and can be better done in code. When using these, arguments are
auto-wired by type. `ContainerInterface` could be used to get current container instance.
- If it's even more complicated, it's a good idea to move such a code into a
factory and reference it as a static call.
- While it's usually not a good idea, you can also set an already
instantiated object into the container.
See [yiisoft/definitions](https://github.com/yiisoft/definitions) for more information.
After you configure the container, you can obtain a service via `get()`:
```php
/** @var \Yiisoft\Di\Container $container */
$object = $container->get('interface_name');
```
Note, however, that it's bad practice using a container directly. It's much
better to rely on auto-wiring as provided by the Injector available from the
[yiisoft/injector](https://github.com/yiisoft/injector) package.
## Using aliases
The DI container supports aliases via the `Yiisoft\Definitions\Reference` class.
This way you can retrieve objects by a more handy name:
```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
'engine_one' => EngineInterface::class,
]);
$container = new Container($config);
$object = $container->get('engine_one');
```
## Using class aliases for specific configuration
To define another instance of a class with specific configuration, you can
use native PHP `class_alias()`:
```php
class_alias(Yiisoft\Db\Pgsql\Connection::class, 'MyPgSql');
$config = ContainerConfig::create()
->withDefinitions([
MyPgSql::class => [ ... ]
]);
$container = new Container($config);
$object = $container->get(MyPgSql::class);
```
It could be then conveniently used by type-hinting:
```php
final class MyService
{
public function __construct(MyPgSql $myPgSql)
{
// ...
}
}
```
## Composite containers
A composite container combines many containers in a single container. When
using this approach, you should fetch objects only from the composite
container.
```php
use Yiisoft\Di\CompositeContainer;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$composite = new CompositeContainer();
$carConfig = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
CarInterface::class => Car::class
]);
$carContainer = new Container($carConfig);
$bikeConfig = ContainerConfig::create()
->withDefinitions([
BikeInterface::class => Bike::class
]);
$bikeContainer = new Container($bikeConfig);
$composite->attach($carContainer);
$composite->attach($bikeContainer);
// Returns an instance of a `Car` class.
$car = $composite->get(CarInterface::class);
// Returns an instance of a `Bike` class.
$bike = $composite->get(BikeInterface::class);
```
Note that containers attached earlier override dependencies of containers attached later.
```php
use Yiisoft\Di\CompositeContainer;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$carConfig = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
CarInterface::class => Car::class
]);
$carContainer = new Container($carConfig);
$composite = new CompositeContainer();
$composite->attach($carContainer);
// Returns an instance of a `Car` class.
$car = $composite->get(CarInterface::class);
// Returns an instance of a `EngineMarkOne` class.
$engine = $car->getEngine();
$engineConfig = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkTwo::class,
]);
$engineContainer = new Container($engineConfig);
$composite = new CompositeContainer();
$composite->attach($engineContainer);
$composite->attach($carContainer);
// Returns an instance of a `Car` class.
$car = $composite->get(CarInterface::class);
// Returns an instance of a `EngineMarkTwo` class.
$engine = $composite->get(EngineInterface::class);
```
## Using service providers
A service provider is a special class that's responsible for providing complex
services or groups of dependencies for the container and extensions of existing services.
A provider should extend from `Yiisoft\Di\ServiceProviderInterface` and must
contain a `getDefinitions()` and `getExtensions()` methods. It should only provide services for the container
and therefore should only contain code related to this task. It should *never*
implement any business logic or other functionality such as environment bootstrap or applying changes to a database.
The `getExtensions()` method allows implementing the decorator pattern by wrapping existing services
with additional functionality.
A typical service provider could look like:
```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ServiceProviderInterface;
class CarFactoryProvider extends ServiceProviderInterface
{
public function getDefinitions(): array
{
return [
CarFactory::class => [
'class' => CarFactory::class,
'$color' => 'red',
],
EngineInterface::class => SolarEngine::class,
WheelInterface::class => [
'class' => Wheel::class,
'$color' => 'black',
],
CarInterface::class => [
'class' => BMW::class,
'$model' => 'X5',
],
];
}
public function getExtensions(): array
{
return [
// Note that Garage should already be defined in a container
Garage::class => function(ContainerInterface $container, Garage $garage) {
$car = $container
->get(CarFactory::class)
->create();
$garage->setCar($car);
return $garage;
}
];
}
}
```
Here you created a service provider responsible for bootstrapping of a car factory with all its dependencies.
An extension is callable that returns a modified service object.
In this case you get existing `Garage` service
and put a car into the garage by calling the method `setCar()`.
Thus, before applying this provider, you had
an empty garage and with the help of the extension you fill it.
To add this service provider to a container, you can pass either its class or a
configuration array in the extra config:
```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$config = ContainerConfig::create()
->withProviders([CarFactoryProvider::class]);
$container = new Container($config);
```
When you add a service provider, DI calls its `getDefinitions()` and `getExtensions()` methods
*immediately* and both services and their extensions get registered into the container.
### Using service providers for decorator pattern
Service provider extensions are a powerful feature that allows implementing the decorator pattern.
This lets you wrap existing services with additional functionality without modifying their original implementation.
Here's an example of using the decorator pattern to add logging to an existing mailer service:
```php
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Yiisoft\Di\ServiceProviderInterface;
interface MailerInterface
{
public function send(string $to, string $subject, string $body): void;
}
class Mailer implements MailerInterface
{
public function send(string $to, string $subject, string $body): void
{
// Original mailer implementation
// Sends email via SMTP or external service
}
}
class LoggingMailerDecorator implements MailerInterface
{
public function __construct(
private MailerInterface $mailer,
private LoggerInterface $logger
) {
}
public function send(string $to, string $subject, string $body): void
{
$this->logger->info("Sending email to {$to}");
$this->mailer->send($to, $subject, $body);
$this->logger->info("Email sent to {$to}");
}
}
class MailerDecoratorProvider implements ServiceProviderInterface
{
public function getDefinitions(): array
{
return [];
}
public function getExtensions(): array
{
return [
MailerInterface::class => static function (ContainerInterface $container, MailerInterface $mailer) {
// Wrap the original mailer with logging decorator
return new LoggingMailerDecorator($mailer, $container->get(LoggerInterface::class));
}
];
}
}
```
In this example, the extension receives the original `MailerInterface` instance and wraps it with
`LoggingMailerDecorator`, which adds logging before and after sending emails. The decorator pattern
allows you to add cross-cutting concerns like logging, caching, or monitoring without changing the
original service implementation.
## Container tags
You can tag services in the following way:
```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$config = ContainerConfig::create()
->withDefinitions([
BlueCarService::class => [
'class' => BlueCarService::class,
'tags' => ['car'],
],
RedCarService::class => [
'definition' => fn () => new RedCarService(),
'tags' => ['car'],
],
]);
$container = new Container($config);
```
Another way to tag services is setting tags via container constructor:
```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$config = ContainerConfig::create()
->withDefinitions([
BlueCarService::class => [
'class' => BlueCarService::class,
],
RedCarService::class => fn () => new RedCarService(),
])
->withTags([
// "car" tag has references to both blue and red cars
'car' => [BlueCarService::class, RedCarService::class]
]);
$container = new Container($config);
```
### Getting tagged services
You can get tagged services from the container in the following way:
```php
$container->get(\Yiisoft\Di\Reference\TagReference::id('car'));
```
The result is an array that has two instances: `BlueCarService` and `RedCarService`.
### Using tagged services in configuration
Use `TagReference` to get tagged services in configuration:
```php
[
Garage::class => [
'__construct()' => [
\Yiisoft\Di\Reference\TagReference::to('car'),
],
],
],
```
## Resetting services state
Despite stateful services isn't a great practice, these are often inevitable. When you build long-running
applications with tools like [Swoole](https://www.swoole.co.uk/) or [RoadRunner](https://roadrunner.dev/) you should
reset the state of such services every request. For this purpose you can use `StateResetter` with resetters callbacks:
```php
$resetter = new StateResetter($container);
$resetter->setResetters([
MyServiceInterface::class => function () {
$this->reset(); // a method of MyServiceInterface
},
]);
```
The callback has access to the private and protected properties of the service instance,
so you can set the initial state of the service efficiently without creating a new instance.
You should trigger the reset itself after each request-response cycle. For RoadRunner, it would look like the following:
```php
while ($request = $psr7->acceptRequest()) {
$response = $application->handle($request);
$psr7->respond($response);
$application->afterEmit($response);
$container
->get(\Yiisoft\Di\StateResetter::class)
->reset();
gc_collect_cycles();
}
```
### Setting resetters in definitions
You define the reset state for each service by providing "reset" callback in the following way:
```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
EngineMarkOne::class => [
'class' => EngineMarkOne::class,
'setNumber()' => [42],
'reset' => function () {
$this->number = 42;
},
],
]);
$container = new Container($config);
```
Note: resetters from definitions work only if you don't set `StateResetter` in definition or service providers.
### Configuring `StateResetter` manually
To manually add resetters or in case you use Yii DI composite container with a third party container that doesn't support state reset natively, you could configure state resetter separately. The following example is PHP-DI:
```php
MyServiceInterface::class => function () {
// ...
},
StateResetter::class => function (ContainerInterface $container) {
$resetter = new StateResetter($container);
$resetter->setResetters([
MyServiceInterface::class => function () {
$this->reset(); // a method of MyServiceInterface
},
]);
return $resetter;
}
```
## Specifying metadata for non-array definitions
To specify some metadata, such as in cases of "resetting services state" or "container tags," for non-array
definitions, you could use the following syntax:
```php
LogTarget::class => [
'definition' => static function (LoggerInterface $logger) use ($params) {
$target = ...
return $target;
},
'reset' => function () use ($params) {
...
},
],
```
Now you've explicitly moved the definition itself to "definition" key.
## Delegates
Each delegate is a callable returning a container instance that's used in case DI
can't find a service in a primary container:
```php
function (ContainerInterface $container): ContainerInterface
{
}
```
To configure delegates use extra config:
```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$config = ContainerConfig::create()
->withDelegates([
function (ContainerInterface $container): ContainerInterface {
// ...
}
]);
$container = new Container($config);
```
## Tuning for production
By default, the container validates definitions right when they're set. In the production environment, it makes sense to
turn it off:
```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$config = ContainerConfig::create()
->withValidate(false);
$container = new Container($config);
```
## Strict mode
Container may work in a strict mode, that's when you should define everything in the container explicitly.
To turn it on, use the following code:
```php
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
$config = ContainerConfig::create()
->withStrictMode(true);
$container = new Container($config);
```
## Documentation
- [Internals](docs/internals.md)
If you need help or have a question, the [Yii Forum](https://forum.yiiframework.com/c/yii-3-0/63) is a good place for that.
You may also check out other [Yii Community Resources](https://www.yiiframework.com/community).
## License
The Yii Dependency Injection is free software. It is released under the terms of the BSD License.
Please see [`LICENSE`](./LICENSE.md) for more information.
Maintained by [Yii Software](https://www.yiiframework.com/).
## Support the project
[](https://opencollective.com/yiisoft)
## Follow updates
[](https://www.yiiframework.com/)
[](https://twitter.com/yiiframework)
[](https://t.me/yii3en)
[](https://www.facebook.com/groups/yiitalk)
[](https://yiiframework.com/go/slack)
================================================
FILE: benchmarks.md
================================================
DI benchmark report
===================
### suite: 1343bd6191c6668ccf8fb4cf1a51a7b61159c825, date: 2020-04-06, stime: 20:48:28
benchmark | subject | set | revs | its | mem_peak | best | mean | mode | worst | stdev | rstdev | diff
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
ContainerBench | benchSequentialLookups | 0 | 1000 | 5 | 1,460,456b | 615.235μs | 625.586μs | 621.205μs | 636.719μs | 7.948μs | 1.27% | 1.00x
ContainerBench | benchSequentialLookups | 1 | 1000 | 5 | 1,467,608b | 634.765μs | 645.507μs | 642.453μs | 661.132μs | 8.691μs | 1.35% | 1.03x
ContainerBench | benchSequentialLookups | 2 | 1000 | 5 | 1,467,752b | 632.813μs | 638.086μs | 636.211μs | 646.484μs | 4.605μs | 0.72% | 1.02x
ContainerBench | benchRandomLookups | 0 | 1000 | 5 | 1,460,456b | 609.375μs | 625.000μs | 633.784μs | 639.649μs | 12.628μs | 2.02% | 1.00x
ContainerBench | benchRandomLookups | 1 | 1000 | 5 | 1,467,608b | 644.531μs | 650.391μs | 652.084μs | 657.227μs | 4.744μs | 0.73% | 1.04x
ContainerBench | benchRandomLookups | 2 | 1000 | 5 | 1,467,752b | 638.672μs | 649.219μs | 650.291μs | 658.203μs | 6.250μs | 0.96% | 1.04x
ContainerBench | benchRandomLookupsComposite | 0 | 1000 | 5 | 24,786,136b | 699.219μs | 708.398μs | 711.499μs | 715.820μs | 5.943μs | 0.84% | 1.13x
ContainerBench | benchRandomLookupsComposite | 1 | 1000 | 5 | 24,780,496b | 743.164μs | 753.125μs | 748.496μs | 772.461μs | 10.268μs | 1.36% | 1.20x
ContainerBench | benchRandomLookupsComposite | 2 | 1000 | 5 | 24,780,640b | 739.257μs | 747.071μs | 744.033μs | 755.860μs | 6.020μs | 0.81% | 1.20x
================================================
FILE: composer.json
================================================
{
"name": "yiisoft/di",
"type": "library",
"description": "Yii DI container",
"keywords": [
"di",
"dependency",
"injection",
"container",
"injector",
"autowiring",
"psr-11"
],
"homepage": "https://www.yiiframework.com/",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/di/issues?state=open",
"source": "https://github.com/yiisoft/di",
"forum": "https://www.yiiframework.com/forum/",
"wiki": "https://www.yiiframework.com/wiki/",
"irc": "ircs://irc.libera.chat:6697/yii",
"chat": "https://t.me/yii3en"
},
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/yiisoft"
},
{
"type": "github",
"url": "https://github.com/sponsors/yiisoft"
}
],
"require": {
"php": "8.1 - 8.5",
"ext-mbstring": "*",
"psr/container": "^1.1 || ^2.0",
"yiisoft/definitions": "^3.0",
"yiisoft/friendly-exception": "^1.1.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.92.5",
"bamarni/composer-bin-plugin": "^1.8.3",
"league/container": "^5.1.0",
"maglnet/composer-require-checker": "^4.7.1",
"phpbench/phpbench": "^1.4.1",
"phpunit/phpunit": "^10.5.46",
"rector/rector": "^2.0.17",
"spatie/phpunit-watcher": "^1.24",
"yiisoft/injector": "^1.2.1",
"yiisoft/test-support": "^3.1"
},
"suggest": {
"yiisoft/injector": "^1.0",
"phpbench/phpbench": "To run benchmarks."
},
"provide": {
"psr/container-implementation": "1.0.0"
},
"autoload": {
"psr-4": {
"Yiisoft\\Di\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Yiisoft\\Di\\Tests\\": "tests"
}
},
"scripts": {
"test": "phpunit --testdox --no-interaction",
"test-watch": "phpunit-watcher watch"
},
"scripts-descriptions": {
"test": "Run all tests"
},
"extra": {
"bamarni-bin": {
"bin-links": true,
"target-directory": "tools",
"forward-command": true
}
},
"config": {
"sort-packages": true,
"allow-plugins": {
"bamarni/composer-bin-plugin": true,
"composer/package-versions-deprecated": true
}
}
}
================================================
FILE: docs/internals.md
================================================
# Internals
## Further reading
- [Martin Fowler's article](https://martinfowler.com/articles/injection.html).
## Benchmarks
To run benchmarks execute the next command
```shell
./vendor/bin/phpbench run
```
Result example
```text
\Yiisoft\Di\Tests\Benchmark\ContainerBench
benchConstructStupid....................I4 [μ Mo]/r: 438.566 435.190 (μs) [μSD μRSD]/r: 9.080μs 2.07%
benchConstructSmart.....................I4 [μ Mo]/r: 470.958 468.942 (μs) [μSD μRSD]/r: 2.848μs 0.60%
benchSequentialLookups # 0..............R5 I4 [μ Mo]/r: 2,837.000 2,821.636 (μs) [μSD μRSD]/r: 34.123μs 1.20%
benchSequentialLookups # 1..............R1 I0 [μ Mo]/r: 12,253.600 12,278.859 (μs) [μSD μRSD]/r: 69.087μs 0.56%
benchRandomLookups # 0..................R5 I4 [μ Mo]/r: 3,142.200 3,111.290 (μs) [μSD μRSD]/r: 87.639μs 2.79%
benchRandomLookups # 1..................R1 I2 [μ Mo]/r: 13,298.800 13,337.170 (μs) [μSD μRSD]/r: 103.891μs 0.78%
benchRandomLookupsComposite # 0.........R1 I3 [μ Mo]/r: 3,351.600 3,389.104 (μs) [μSD μRSD]/r: 72.516μs 2.16%
benchRandomLookupsComposite # 1.........R1 I4 [μ Mo]/r: 13,528.200 13,502.881 (μs) [μSD μRSD]/r: 99.997μs 0.74%
\Yiisoft\Di\Tests\Benchmark\ContainerMethodHasBench
benchPredefinedExisting.................R1 I4 [μ Mo]/r: 0.115 0.114 (μs) [μSD μRSD]/r: 0.001μs 1.31%
benchUndefinedExisting..................R5 I4 [μ Mo]/r: 0.436 0.432 (μs) [μSD μRSD]/r: 0.008μs 1.89%
benchUndefinedNonexistent...............R5 I4 [μ Mo]/r: 0.946 0.942 (μs) [μSD μRSD]/r: 0.006μs 0.59%
8 subjects, 55 iterations, 5,006 revs, 0 rejects, 0 failures, 0 warnings
(best [mean mode] worst) = 0.113 [4,483.856 4,486.051] 0.117 (μs)
⅀T: 246,612.096μs μSD/r 43.563μs μRSD/r: 1.336%
```
> **Warning!**
>
> These summary statistics can be misleading.
> You should always verify the individual subject statistics before drawing any conclusions.
> **Legend**
>
> - μ: Mean time taken by all iterations in variant.
> - Mo: Mode of all iterations in variant.
> - μSD: μ standard deviation.
> - μRSD: μ relative standard deviation.
> - best: Maximum time of all iterations (minimal of all iterations).
> - mean: Mean time taken by all iterations.
> - mode: Mode of all iterations.
> - worst: Minimum time of all iterations (minimal of all iterations).
### Command examples
- Default report for all benchmarks that outputs the result to `CSV-file`
```shell
./vendor/bin/phpbench run --report=default --progress=dots --output=csv_file
```
Generated MD-file example
```text
>DI benchmark report
>===================
>
>### suite: 1343b1dc0589cb4e985036d14b3e12cb430a975b, date: 2020-02-21, stime: 16:02:45
>
>benchmark | subject | set | revs | iter | mem_peak | time_rev | comp_z_value | comp_deviation
> --- | --- | --- | --- | --- | --- | --- | --- | ---
>ContainerBench | benchConstructStupid | 0 | 1000 | 0 | 1,416,784b | 210.938μs | -1.48σ | -1.1%
>ContainerBench | benchConstructStupid | 0 | 1000 | 1 | 1,416,784b | 213.867μs | +0.37σ | +0.27%
>ContainerBench | benchConstructStupid | 0 | 1000 | 2 | 1,416,784b | 212.890μs | -0.25σ | -0.18%
>ContainerBench | benchConstructStupid | 0 | 1000 | 3 | 1,416,784b | 215.820μs | +1.60σ | +1.19%
>ContainerBench | benchConstructStupid | 0 | 1000 | 4 | 1,416,784b | 212.891μs | -0.25σ | -0.18%
>ContainerBench | benchConstructSmart | 0 | 1000 | 0 | 1,426,280b | 232.422μs | -1.03σ | -0.5%
>ContainerBench | benchConstructSmart | 0 | 1000 | 1 | 1,426,280b | 232.422μs | -1.03σ | -0.5%
>ContainerBench | benchConstructSmart | 0 | 1000 | 2 | 1,426,280b | 233.398μs | -0.17σ | -0.08%
>ContainerBench | benchConstructSmart | 0 | 1000 | 3 | 1,426,280b | 234.375μs | +0.69σ | +0.33%
>ContainerBench | benchConstructSmart | 0 | 1000 | 4 | 1,426,280b | 235.351μs | +1.54σ | +0.75%
>`... skipped` | `...` | `...` | `...` | `...` | `...` | `...` | `...` | `...`
>ContainerMethodHasBench | benchPredefinedExisting | 0 | 1000 | 0 | 1,216,144b | 81.055μs | -0.91σ | -1.19%
>ContainerMethodHasBench | benchPredefinedExisting | 0 | 1000 | 1 | 1,216,144b | 83.985μs | +1.83σ | +2.38%
>ContainerMethodHasBench | benchPredefinedExisting | 0 | 1000 | 2 | 1,216,144b | 82.032μs | 0.00σ | 0.00%
>ContainerMethodHasBench | benchPredefinedExisting | 0 | 1000 | 3 | 1,216,144b | 82.031μs | 0.00σ | 0.00%
>ContainerMethodHasBench | benchPredefinedExisting | 0 | 1000 | 4 | 1,216,144b | 81.055μs | -0.91σ | -1.19%
>`... skipped` | `...` | `...` | `...` | `...` | `...` | `...` | `...` | `...`
```
> **Legend**
>
> - benchmark: Benchmark class.
> - subject: Benchmark class method.
> - set: Set of data (provided by ParamProvider).
> - revs: Number of revolutions (represent the number of times that the code is executed).
> - iter: Number of iteration.
> - mem_peak: (mean) Peak memory used by iteration as retrieved by memory_get_peak_usage.
> - time_rev: Mean time taken by all iterations in variant.
> - comp_z_value: Z-score.
> - comp_deviation: Relative deviation (margin of error).
- Aggregate report for the `lookup` group that outputs the result to `console` and `CSV-file`
```shell
./vendor/bin/phpbench run --report=aggregate --progress=dots --output=csv_file --output=console --group=lookup
```
> **Notice**
>
> Available groups: `construct` `lookup` `has`
Generated MD-file example
```text
> DI benchmark report
> ===================
>
>### suite: 1343b1d2654a3819c72a96d236302b70a504dac7, date: 2020-02-21, stime: 13:27:32
>
>benchmark | subject | set | revs | its | mem_peak | best | mean | mode | worst | stdev | rstdev | diff
> --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
>ContainerBench | benchSequentialLookups | 0 | 1000 | 5 | 1,454,024b | 168.945μs | 170.117μs | 169.782μs | 171.875μs | 0.957μs | 0.56% | 1.00x
>ContainerBench | benchSequentialLookups | 1 | 1000 | 5 | 1,445,296b | 3,347.656μs | 3,384.961μs | 3,390.411μs | 3,414.062μs | 21.823μs | 0.64% | 19.90x
>ContainerBench | benchSequentialLookups | 2 | 1000 | 5 | 1,445,568b | 3,420.898μs | 3,488.477μs | 3,447.260μs | 3,657.227μs | 85.705μs | 2.46% | 20.51x
>ContainerBench | benchRandomLookups | 0 | 1000 | 5 | 1,454,024b | 169.922μs | 171.875μs | 171.871μs | 173.828μs | 1.381μs | 0.80% | 1.01x
>ContainerBench | benchRandomLookups | 1 | 1000 | 5 | 1,445,296b | 3,353.515μs | 3,389.844μs | 3,377.299μs | 3,446.289μs | 31.598μs | 0.93% | 19.93x
>ContainerBench | benchRandomLookups | 2 | 1000 | 5 | 1,445,568b | 3,445.313μs | 3,587.696μs | 3,517.823μs | 3,749.023μs | 115.850μs | 3.23% | 21.09x
>ContainerBench | benchRandomLookupsComposite | 0 | 1000 | 5 | 1,454,032b | 297.852μs | 299.610μs | 298.855μs | 302.734μs | 1.680μs | 0.56% | 1.76x
>ContainerBench | benchRandomLookupsComposite | 1 | 1000 | 5 | 1,445,880b | 3,684.570μs | 3,708.984μs | 3,695.731μs | 3,762.695μs | 28.297μs | 0.76% | 21.80x
>ContainerBench | benchRandomLookupsComposite | 2 | 1000 | 5 | 1,446,152b | 3,668.946μs | 3,721.680μs | 3,727.407μs | 3,765.625μs | 30.881μs | 0.83% | 21.88x
```
> **Legend**
>
> * benchmark: Benchmark class.
> * subject: Benchmark class method.
> * set: Set of data (provided by ParamProvider).
> * revs: Number of revolutions (represent the number of times that the code is executed).
> * its: Number of iterations (one measurement for each iteration).
> * mem_peak: (mean) Peak memory used by each iteration as retrieved by memory_get_peak_usage.
> * best: Maximum time of all iterations in variant.
> * mean: Mean time taken by all iterations in variant.
> * mode: Mode of all iterations in variant.
> * worst: Minimum time of all iterations in variant.
> * stdev: Standard deviation.
> * rstdev: The relative standard deviation.
> * diff: Difference between variants in a single group.
## Unit testing
The package is tested with [PHPUnit](https://phpunit.de/). To run tests:
```shell
./vendor/bin/phpunit
```
## Mutation testing
The package tests are checked with [Infection](https://infection.github.io/) mutation framework with
[Infection Static Analysis Plugin](https://github.com/Roave/infection-static-analysis-plugin). To run it:
```shell
./vendor/bin/roave-infection-static-analysis-plugin
```
## Static analysis
The code is statically analyzed with [Psalm](https://psalm.dev/). To run static analysis:
```shell
./vendor/bin/psalm
```
## Code style
Use [Rector](https://github.com/rectorphp/rector) to make codebase follow some specific rules or
use either newest or any specific version of PHP:
```shell
./vendor/bin/rector
```
## Dependencies
Use [ComposerRequireChecker](https://github.com/maglnet/ComposerRequireChecker) to detect transitive
[Composer](https://getcomposer.org/) dependencies. To run the checker, execute the following command:
```shell
./vendor/bin/composer-require-checker
```
================================================
FILE: infection.json.dist
================================================
{
"source": {
"directories": [
"src"
]
},
"logs": {
"text": "php:\/\/stderr",
"stryker": {
"report": "master"
}
},
"mutators": {
"@default": true
}
}
================================================
FILE: phpbench.json
================================================
{
"runner.bootstrap": "vendor/autoload.php",
"runner.path": "tests/Benchmark",
"runner.retry_threshold": 3,
"report.outputs": {
"csv_file": {
"extends": "delimited",
"delimiter": ",",
"file": "benchmarks.csv"
}
}
}
================================================
FILE: phpcs.xml.dist
================================================
<?xml version="1.0"?>
<ruleset>
<file>./</file>
<exclude-pattern>./vendor/*</exclude-pattern>
<rule ref="PSR2" />
</ruleset>
================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
cacheDirectory=".phpunit.cache"
requireCoverageMetadata="false"
beStrictAboutCoverageMetadata="true"
beStrictAboutOutputDuringTests="true"
executionOrder="random"
failOnRisky="true"
failOnWarning="true"
stopOnFailure="false"
colors="true"
displayDetailsOnPhpunitDeprecations="true"
>
<php>
<ini name="error_reporting" value="-1"/>
</php>
<testsuites>
<testsuite name="Yii DI Container tests">
<directory>./tests/Unit</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>
================================================
FILE: psalm.xml
================================================
<?xml version="1.0"?>
<psalm
errorLevel="1"
findUnusedBaselineEntry="true"
findUnusedCode="false"
ensureOverrideAttribute="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<issueHandlers>
<MixedAssignment errorLevel="suppress" />
</issueHandlers>
</psalm>
================================================
FILE: rector.php
================================================
<?php
declare(strict_types=1);
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\Config\RectorConfig;
use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector;
use Rector\Php81\Rector\Array_\ArrayToFirstClassCallableRector;
return RectorConfig::configure()
->withPaths([
__DIR__ . '/src',
__DIR__ . '/tests',
])
->withPhpSets(php81: true)
->withRules([
InlineConstructorDefaultToPropertyRector::class,
])
->withSkip([
ClosureToArrowFunctionRector::class,
ArrayToFirstClassCallableRector::class => [
__DIR__ . '/tests/Unit/Helpers/DefinitionParserTest.php',
],
]);
================================================
FILE: src/BuildingException.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di;
use Exception;
use Psr\Container\ContainerExceptionInterface;
use Throwable;
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
use function sprintf;
/**
* It wraps all exceptions that don't implement `ContainerExceptionInterface` during the build process.
* Also adds building context for more understanding.
*/
final class BuildingException extends Exception implements ContainerExceptionInterface, FriendlyExceptionInterface
{
/**
* @param string $id ID of the definition or name of the class that wasn't found.
* @param string[] $buildStack Stack of IDs of services requested definition or class that wasn't found.
*/
public function __construct(
private readonly string $id,
Throwable $error,
array $buildStack = [],
?Throwable $previous = null,
) {
$message = sprintf(
'Caught unhandled error "%s" while building "%s".',
$error->getMessage() === '' ? $error::class : $error->getMessage(),
implode('" -> "', $buildStack === [] ? [$id] : $buildStack),
);
parent::__construct($message, 0, $previous);
}
public function getName(): string
{
return sprintf('Unable to build "%s" object.', $this->id);
}
public function getSolution(): ?string
{
$solution = <<<SOLUTION
Ensure that either a service with ID "%1\$s" is defined or such class exists and is autoloadable.
Ensure that configuration for service with ID "%1\$s" is correct.
SOLUTION;
return sprintf($solution, $this->id);
}
}
================================================
FILE: src/CompositeContainer.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di;
use InvalidArgumentException;
use Psr\Container\ContainerInterface;
use RuntimeException;
use Throwable;
use Yiisoft\Di\Reference\TagReference;
use function is_string;
use function sprintf;
/**
* A composite container for use with containers that support the delegate lookup feature.
*/
final class CompositeContainer implements ContainerInterface
{
/**
* Containers to look into starting from the beginning of the array.
*
* @var ContainerInterface[] The list of containers.
*/
private array $containers = [];
/**
* @psalm-template T
* @psalm-param string|class-string<T> $id
* @psalm-return ($id is class-string ? T : mixed)
*/
public function get($id)
{
/** @psalm-suppress TypeDoesNotContainType */
if (!is_string($id)) {
throw new InvalidArgumentException(
sprintf(
'ID must be a string, %s given.',
get_debug_type($id),
),
);
}
if ($id === StateResetter::class) {
$resetters = [];
foreach ($this->containers as $container) {
if ($container->has(StateResetter::class)) {
$resetters[] = $container->get(StateResetter::class);
}
}
$stateResetter = new StateResetter($this);
$stateResetter->setResetters($resetters);
return $stateResetter;
}
if (TagReference::isTagAlias($id)) {
$tags = [];
foreach ($this->containers as $container) {
if (!$container instanceof Container) {
continue;
}
if ($container->has($id)) {
/** @psalm-suppress MixedArgument `Container::get()` always return array for tag */
array_unshift($tags, $container->get($id));
}
}
/** @psalm-suppress MixedArgument `Container::get()` always return array for tag */
return array_merge(...$tags);
}
foreach ($this->containers as $container) {
if ($container->has($id)) {
/** @psalm-suppress MixedReturnStatement */
return $container->get($id);
}
}
// Collect details from containers
$exceptions = [];
foreach ($this->containers as $container) {
$hasException = false;
try {
$container->get($id);
} catch (Throwable $t) {
$hasException = true;
$exceptions[] = [$t, $container];
} finally {
if (!$hasException) {
$exceptions[] = [
new RuntimeException(
'Container "has()" returned false, but no exception was thrown from "get()".',
),
$container,
];
}
}
}
throw new CompositeNotFoundException($exceptions);
}
public function has($id): bool
{
/** @psalm-suppress TypeDoesNotContainType */
if (!is_string($id)) {
throw new InvalidArgumentException(
sprintf(
'ID must be a string, %s given.',
get_debug_type($id),
),
);
}
if ($id === StateResetter::class) {
return true;
}
if (TagReference::isTagAlias($id)) {
foreach ($this->containers as $container) {
if (!$container instanceof Container) {
continue;
}
if ($container->has($id)) {
return true;
}
}
return false;
}
foreach ($this->containers as $container) {
if ($container->has($id)) {
return true;
}
}
return false;
}
/**
* Attaches a container to the composite container.
*/
public function attach(ContainerInterface $container): void
{
$this->containers[] = $container;
}
/**
* Removes a container from the list of containers.
*/
public function detach(ContainerInterface $container): void
{
foreach ($this->containers as $i => $c) {
if ($container === $c) {
unset($this->containers[$i]);
}
}
}
}
================================================
FILE: src/CompositeNotFoundException.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di;
use Exception;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use function sprintf;
/**
* `CompositeNotFoundException` is thrown when no definition or class was found in the composite container
* for a given ID. It contains all exceptions thrown by containers registered in the composite container.
*/
final class CompositeNotFoundException extends Exception implements NotFoundExceptionInterface
{
/**
* @param array $exceptions Container exceptions in [throwable, container] format.
*
* @psalm-param list<array{\Throwable,ContainerInterface}> $exceptions
*/
public function __construct(array $exceptions)
{
$message = '';
foreach ($exceptions as $i => [$exception, $container]) {
$containerClass = $container::class;
$containerId = spl_object_id($container);
$number = $i + 1;
$message .= "\n $number. Container $containerClass #$containerId: {$exception->getMessage()}";
}
parent::__construct(sprintf('No definition or class found or resolvable in composite container:%s', $message));
}
}
================================================
FILE: src/Container.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di;
use Closure;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
use Yiisoft\Definitions\ArrayDefinition;
use Yiisoft\Definitions\DefinitionStorage;
use Yiisoft\Definitions\Exception\CircularReferenceException;
use Yiisoft\Definitions\Exception\InvalidConfigException;
use Yiisoft\Definitions\Exception\NotInstantiableException;
use Yiisoft\Definitions\Helpers\DefinitionValidator;
use Yiisoft\Di\Helpers\DefinitionNormalizer;
use Yiisoft\Di\Helpers\DefinitionParser;
use Yiisoft\Di\Reference\TagReference;
use function array_key_exists;
use function array_keys;
use function implode;
use function in_array;
use function is_array;
use function is_callable;
use function is_object;
use function is_string;
use function sprintf;
/**
* Container implements a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container.
*/
final class Container implements ContainerInterface
{
private const META_TAGS = 'tags';
private const META_RESET = 'reset';
private const ALLOWED_META = [self::META_TAGS, self::META_RESET];
/**
* @var DefinitionStorage Storage of object definitions.
*/
private readonly DefinitionStorage $definitions;
/**
* @var array Used to collect IDs of objects instantiated during build
* to detect circular references.
*/
private array $building = [];
/**
* @var bool $validate If definitions should be validated.
*/
private readonly bool $validate;
/**
* @var array Cached instances.
* @psalm-var array<string, mixed>
*/
private array $instances = [];
private CompositeContainer $delegates;
/**
* @var array Tagged service IDs. The structure is `['tagID' => ['service1', 'service2']]`.
* @psalm-var array<string, list<string>>
*/
private array $tags;
/**
* @var Closure[]
* @psalm-var array<string, Closure>
*/
private array $resetters = [];
private bool $useResettersFromMeta = true;
/**
* @param ?ContainerConfigInterface $config Container configuration.
*
* @throws InvalidConfigException If configuration is not valid.
*/
public function __construct(?ContainerConfigInterface $config = null)
{
$config ??= ContainerConfig::create();
$this->definitions = new DefinitionStorage(
[
ContainerInterface::class => $this,
StateResetter::class => StateResetter::class,
],
$config->useStrictMode(),
);
$this->validate = $config->shouldValidate();
$this->setTags($config->getTags());
$this->addDefinitions($config->getDefinitions());
$this->addProviders($config->getProviders());
$this->setDelegates($config->getDelegates());
}
/**
* Returns a value indicating whether the container has the definition of the specified name.
*
* @param string $id Class name, interface name or alias name.
*
* @return bool Whether the container is able to provide instance of class specified.
*
* @see addDefinition()
*/
public function has(string $id): bool
{
try {
if ($this->definitions->has($id)) {
return true;
}
} catch (CircularReferenceException) {
return true;
}
if (TagReference::isTagAlias($id)) {
$tag = TagReference::extractTagFromAlias($id);
return isset($this->tags[$tag]);
}
return false;
}
/**
* Returns an instance by either interface name or alias.
*
* The same instance of the class will be returned each time this method is called.
*
* @param string $id The interface or an alias name that was previously registered.
*
* @throws CircularReferenceException
* @throws InvalidConfigException
* @throws NotFoundExceptionInterface
* @throws NotInstantiableException
* @throws BuildingException
*
* @return mixed An instance of the requested interface.
*
* @psalm-template T
* @psalm-param string|class-string<T> $id
* @psalm-return ($id is class-string ? T : mixed)
*
* @psalm-suppress MixedReturnStatement `mixed` is a correct return type for this method.
*/
public function get(string $id)
{
// Fast path: check if instance exists.
if (array_key_exists($id, $this->instances)) {
if ($id === StateResetter::class) {
return $this->prepareStateResetter();
}
return $this->instances[$id];
}
try {
$this->instances[$id] = $this->build($id);
} catch (NotFoundException $exception) {
// Fast path: if the exception ID matches the requested ID, no need to modify stack.
if ($exception->getId() === $id) {
// Try delegates before giving up.
try {
if ($this->delegates->has($id)) {
return $this->delegates->get($id);
}
} catch (Throwable $e) {
throw new BuildingException($id, $e, $this->definitions->getBuildStack(), $e);
}
throw $exception;
}
// Add current ID to build stack for better error reporting.
$buildStack = $exception->getBuildStack();
array_unshift($buildStack, $id);
throw new NotFoundException($exception->getId(), $buildStack);
} catch (NotFoundExceptionInterface $exception) {
// Try delegates before giving up
try {
if ($this->delegates->has($id)) {
return $this->delegates->get($id);
}
} catch (Throwable $e) {
throw new BuildingException($id, $e, $this->definitions->getBuildStack(), $e);
}
throw new NotFoundException($id, [$id], previous: $exception);
} catch (ContainerExceptionInterface $e) {
if (!$e instanceof InvalidConfigException) {
throw $e;
}
throw new BuildingException($id, $e, $this->definitions->getBuildStack(), $e);
} catch (Throwable $e) {
throw new BuildingException($id, $e, $this->definitions->getBuildStack(), $e);
}
// Handle StateResetter for newly built instances.
if ($id === StateResetter::class) {
return $this->prepareStateResetter();
}
return $this->instances[$id];
}
private function prepareStateResetter(): StateResetter
{
$delegatesResetter = null;
if ($this->delegates->has(StateResetter::class)) {
$delegatesResetter = $this->delegates->get(StateResetter::class);
}
/** @var StateResetter $mainResetter */
$mainResetter = $this->instances[StateResetter::class];
if ($this->useResettersFromMeta) {
/** @var StateResetter[] $resetters */
$resetters = [];
foreach ($this->resetters as $serviceId => $callback) {
if (isset($this->instances[$serviceId])) {
$resetters[$serviceId] = $callback;
}
}
if ($delegatesResetter !== null) {
$resetters[] = $delegatesResetter;
}
$mainResetter->setResetters($resetters);
} elseif ($delegatesResetter !== null) {
$resetter = new StateResetter($this->get(ContainerInterface::class));
$resetter->setResetters([$mainResetter, $delegatesResetter]);
return $resetter;
}
return $mainResetter;
}
/**
* Sets a definition to the container. Definition may be defined multiple ways.
*
* @param string $id ID to set definition for.
* @param mixed $definition Definition to set.
*
* @throws InvalidConfigException
*
* @see DefinitionNormalizer::normalize()
*/
private function addDefinition(string $id, mixed $definition): void
{
[$definition, $meta] = DefinitionParser::parse($definition);
if ($this->validate) {
$this->validateDefinition($definition, $id);
// Only validate meta if it's not empty.
if ($meta !== []) {
$this->validateMeta($meta);
}
}
/**
* @psalm-var array{reset?:Closure,tags?:string[]} $meta
*/
// Process meta only if it has tags or reset callback.
if (isset($meta[self::META_TAGS])) {
$this->setDefinitionTags($id, $meta[self::META_TAGS]);
}
if (isset($meta[self::META_RESET])) {
$this->setDefinitionResetter($id, $meta[self::META_RESET]);
}
unset($this->instances[$id]);
$this->addDefinitionToStorage($id, $definition);
}
/**
* Sets multiple definitions at once.
*
* @param array $config Definitions indexed by their IDs.
*
* @throws InvalidConfigException
*/
private function addDefinitions(array $config): void
{
foreach ($config as $id => $definition) {
if ($this->validate && !is_string($id)) {
throw new InvalidConfigException(
sprintf(
'Key must be a string. %s given.',
get_debug_type($id),
),
);
}
/** @var string $id */
$this->addDefinition($id, $definition);
}
}
/**
* Set container delegates.
*
* Each delegate must be a callable in format `function (ContainerInterface $container): ContainerInterface`.
* The container instance returned is used in case a service can't be found in primary container.
*
* @throws InvalidConfigException
*/
private function setDelegates(array $delegates): void
{
$this->delegates = new CompositeContainer();
$container = $this->get(ContainerInterface::class);
foreach ($delegates as $delegate) {
if (!$delegate instanceof Closure) {
throw new InvalidConfigException(
'Delegate must be callable in format "function (ContainerInterface $container): ContainerInterface".',
);
}
$delegate = $delegate($container);
if (!$delegate instanceof ContainerInterface) {
throw new InvalidConfigException(
'Delegate callable must return an object that implements ContainerInterface.',
);
}
$this->delegates->attach($delegate);
}
$this->definitions->setDelegateContainer($this->delegates);
}
/**
* @param mixed $definition Definition to validate.
* @param string|null $id ID of the definition to validate.
*
* @throws InvalidConfigException
*/
private function validateDefinition(mixed $definition, ?string $id = null): void
{
// Skip validation for common simple cases.
if ($definition instanceof ContainerInterface || $definition instanceof Closure) {
return;
}
if (is_array($definition)) {
if (isset($definition[DefinitionParser::IS_PREPARED_ARRAY_DEFINITION_DATA])) {
$class = $definition['class'];
$constructorArguments = $definition['__construct()'];
/**
* @var array $methodsAndProperties Is always array for prepared array definition data.
* @see DefinitionParser::parse()
* @psalm-var array<string,mixed> $methodsAndProperties
*/
$methodsAndProperties = $definition['methodsAndProperties'];
$definition = array_merge(
$class === null ? [] : [ArrayDefinition::CLASS_NAME => $class],
[ArrayDefinition::CONSTRUCTOR => $constructorArguments],
// extract only value from parsed definition method
array_map(static fn(array $data): mixed => $data[2], $methodsAndProperties),
);
}
} elseif ($definition instanceof ExtensibleService) {
throw new InvalidConfigException(
'Invalid definition. ExtensibleService is only allowed in provider extensions.',
);
}
DefinitionValidator::validate($definition, $id);
}
/**
* @throws InvalidConfigException
*/
private function validateMeta(array $meta): void
{
foreach ($meta as $key => $value) {
if (!in_array($key, self::ALLOWED_META, true)) {
throw new InvalidConfigException(
sprintf(
'Invalid definition: metadata "%s" is not allowed. Did you mean "%s()" or "$%s"?',
$key,
$key,
$key,
),
);
}
if ($key === self::META_TAGS) {
$this->validateDefinitionTags($value);
}
if ($key === self::META_RESET) {
$this->validateDefinitionReset($value);
}
}
}
/**
* @throws InvalidConfigException
*/
private function validateDefinitionTags(mixed $tags): void
{
if (!is_array($tags)) {
throw new InvalidConfigException(
sprintf(
'Invalid definition: tags should be array of strings, %s given.',
get_debug_type($tags),
),
);
}
foreach ($tags as $tag) {
if (!is_string($tag)) {
throw new InvalidConfigException('Invalid tag. Expected a string, got ' . var_export($tag, true) . '.');
}
}
}
/**
* @throws InvalidConfigException
*/
private function validateDefinitionReset(mixed $reset): void
{
if (!$reset instanceof Closure) {
throw new InvalidConfigException(
sprintf(
'Invalid definition: "reset" should be closure, %s given.',
get_debug_type($reset),
),
);
}
}
/**
* @throws InvalidConfigException
*/
private function setTags(array $tags): void
{
if ($this->validate) {
foreach ($tags as $tag => $services) {
if (!is_string($tag)) {
throw new InvalidConfigException(
sprintf(
'Invalid tags configuration: tag should be string, %s given.',
$tag,
),
);
}
if (!is_array($services)) {
throw new InvalidConfigException(
sprintf(
'Invalid tags configuration: tag should contain array of service IDs, %s given.',
get_debug_type($services),
),
);
}
foreach ($services as $service) {
if (!is_string($service)) {
throw new InvalidConfigException(
sprintf(
'Invalid tags configuration: service should be defined as class string, %s given.',
get_debug_type($service),
),
);
}
}
}
}
/** @psalm-var array<string, list<string>> $tags */
$this->tags = $tags;
}
/**
* @psalm-param string[] $tags
*/
private function setDefinitionTags(string $id, array $tags): void
{
foreach ($tags as $tag) {
if (!isset($this->tags[$tag]) || !in_array($id, $this->tags[$tag], true)) {
$this->tags[$tag][] = $id;
}
}
}
private function setDefinitionResetter(string $id, Closure $resetter): void
{
$this->resetters[$id] = $resetter;
}
/**
* Add definition to storage.
*
* @param string $id ID to set definition for.
* @param mixed|object $definition Definition to set.
*
* @see $definitions
*/
private function addDefinitionToStorage(string $id, mixed $definition): void
{
$this->definitions->set($id, $definition);
if ($id === StateResetter::class) {
$this->useResettersFromMeta = false;
}
}
/**
* Creates new instance by either interface name or alias.
*
* @param string $id The interface or the alias name that was previously registered.
*
* @throws InvalidConfigException
* @throws NotFoundExceptionInterface
* @throws CircularReferenceException
*
* @return mixed|object New-built instance of the specified class.
*
* @internal
*/
private function build(string $id): mixed
{
// Fast path: check for circular reference first as it's the most critical.
if (isset($this->building[$id])) {
if ($id === ContainerInterface::class) {
return $this;
}
throw new CircularReferenceException(
sprintf(
'Circular reference to "%s" detected while building: %s.',
$id,
implode(', ', array_keys($this->building)),
),
);
}
// Less common case: tag alias.
if (TagReference::isTagAlias($id)) {
return $this->getTaggedServices($id);
}
// Check if the definition exists.
if (!$this->definitions->has($id)) {
throw new NotFoundException($id, $this->definitions->getBuildStack());
}
$this->building[$id] = 1;
try {
$normalizedDefinition = DefinitionNormalizer::normalize($this->definitions->get($id), $id);
$object = $normalizedDefinition->resolve($this->get(ContainerInterface::class));
} finally {
unset($this->building[$id]);
}
return $object;
}
private function getTaggedServices(string $tagAlias): array
{
$tag = TagReference::extractTagFromAlias($tagAlias);
$services = [];
if (isset($this->tags[$tag])) {
foreach ($this->tags[$tag] as $service) {
$services[] = $this->get($service);
}
}
return $services;
}
/**
* @throws CircularReferenceException
* @throws InvalidConfigException
*/
private function addProviders(array $providers): void
{
$extensions = [];
foreach ($providers as $provider) {
$providerInstance = $this->buildProvider($provider);
$extensions[] = $providerInstance->getExtensions();
$this->addDefinitions($providerInstance->getDefinitions());
}
foreach ($extensions as $providerExtensions) {
foreach ($providerExtensions as $id => $extension) {
if (!is_string($id)) {
throw new InvalidConfigException(
sprintf('Extension key must be a service ID as string, %s given.', $id),
);
}
if ($id === ContainerInterface::class) {
throw new InvalidConfigException('ContainerInterface extensions are not allowed.');
}
if (!$this->definitions->has($id)) {
throw new InvalidConfigException("Extended service \"$id\" doesn't exist.");
}
if (!is_callable($extension)) {
throw new InvalidConfigException(
sprintf(
'Extension of service should be callable, %s given.',
get_debug_type($extension),
),
);
}
$definition = $this->definitions->get($id);
if (!$definition instanceof ExtensibleService) {
$definition = new ExtensibleService($definition, $id);
$this->addDefinitionToStorage($id, $definition);
}
$definition->addExtension($extension);
}
}
}
/**
* Builds service provider by definition.
*
* @param mixed $provider Class name or instance of provider.
*
* @throws InvalidConfigException If provider argument is not valid.
*
* @return ServiceProviderInterface Instance of service provider.
*/
private function buildProvider(mixed $provider): ServiceProviderInterface
{
if ($this->validate && !(is_string($provider) || $provider instanceof ServiceProviderInterface)) {
throw new InvalidConfigException(
sprintf(
'Service provider should be a class name or an instance of %s. %s given.',
ServiceProviderInterface::class,
get_debug_type($provider),
),
);
}
/**
* @psalm-suppress MixedMethodCall Service provider defined as class string
* should container public constructor, otherwise throws error.
*/
$providerInstance = is_object($provider) ? $provider : new $provider();
if (!$providerInstance instanceof ServiceProviderInterface) {
throw new InvalidConfigException(
sprintf(
'Service provider should be an instance of %s. %s given.',
ServiceProviderInterface::class,
get_debug_type($providerInstance),
),
);
}
return $providerInstance;
}
}
================================================
FILE: src/ContainerConfig.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di;
/**
* Container configuration.
*/
final class ContainerConfig implements ContainerConfigInterface
{
private array $definitions = [];
private array $providers = [];
private array $tags = [];
private bool $validate = true;
private array $delegates = [];
private bool $useStrictMode = false;
private function __construct() {}
public static function create(): self
{
return new self();
}
/**
* @param array $definitions Definitions to put into container.
*/
public function withDefinitions(array $definitions): self
{
$new = clone $this;
$new->definitions = $definitions;
return $new;
}
public function getDefinitions(): array
{
return $this->definitions;
}
/**
* @param array $providers Service providers to get definitions from.
*/
public function withProviders(array $providers): self
{
$new = clone $this;
$new->providers = $providers;
return $new;
}
public function getProviders(): array
{
return $this->providers;
}
/**
* @param array $tags Tagged service IDs. The structure is `['tagID' => ['service1', 'service2']]`.
*/
public function withTags(array $tags): self
{
$new = clone $this;
$new->tags = $tags;
return $new;
}
public function getTags(): array
{
return $this->tags;
}
/**
* @param bool $validate Whether definitions should be validated immediately.
*/
public function withValidate(bool $validate = true): self
{
$new = clone $this;
$new->validate = $validate;
return $new;
}
public function shouldValidate(): bool
{
return $this->validate;
}
/**
* @param array $delegates Container delegates. Each delegate is a callable in format
* `function (ContainerInterface $container): ContainerInterface`. The container instance returned is used
* in case a service can't be found in primary container.
*/
public function withDelegates(array $delegates): self
{
$new = clone $this;
$new->delegates = $delegates;
return $new;
}
public function getDelegates(): array
{
return $this->delegates;
}
/**
* @param bool $useStrictMode If the automatic addition of definition when class exists and can be resolved
* is disabled.
*/
public function withStrictMode(bool $useStrictMode = true): self
{
$new = clone $this;
$new->useStrictMode = $useStrictMode;
return $new;
}
public function useStrictMode(): bool
{
return $this->useStrictMode;
}
}
================================================
FILE: src/ContainerConfigInterface.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di;
/**
* Container configuration.
*/
interface ContainerConfigInterface
{
/**
* @return array Definitions to put into container.
*/
public function getDefinitions(): array;
/**
* @return array Service providers to get definitions from.
*/
public function getProviders(): array;
/**
* @return array Tagged service IDs. The structure is `['tagID' => ['service1', 'service2']]`.
*/
public function getTags(): array;
/**
* @return bool Whether definitions should be validated immediately.
*/
public function shouldValidate(): bool;
/**
* @return array Container delegates. Each delegate is a callable in format
* `function (ContainerInterface $container): ContainerInterface`. The container instance returned is used
* in case a service can't be found in primary container.
*/
public function getDelegates(): array;
/**
* @return bool If the automatic addition of definition when class exists and can be resolved is disabled.
*/
public function useStrictMode(): bool;
}
================================================
FILE: src/ExtensibleService.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di;
use Psr\Container\ContainerInterface;
use Yiisoft\Definitions\Contract\DefinitionInterface;
use Yiisoft\Di\Helpers\DefinitionNormalizer;
/**
* @internal A wrapper for a service definition that allows registering extensions.
* An extension is callable that returns a modified service object:
*
* ```php
* static function (ContainerInterface $container, $service) {
* return $service->withAnotherOption(42);
* }
* ```
*/
final class ExtensibleService implements DefinitionInterface
{
/**
* @var callable[]
*/
private array $extensions = [];
/**
* @param mixed $definition Definition to allow registering extensions for.
*/
public function __construct(
private readonly mixed $definition,
private readonly string $id,
) {}
/**
* Add an extension.
*
* An extension is callable that returns a modified service object:
*
* ```php
* static function (ContainerInterface $container, $service) {
* return $service->withAnotherOption(42);
* }
* ```
*
* @param callable $closure An extension to register.
*/
public function addExtension(callable $closure): void
{
$this->extensions[] = $closure;
}
public function resolve(ContainerInterface $container): mixed
{
$service = DefinitionNormalizer::normalize($this->definition, $this->id)
->resolve($container);
foreach ($this->extensions as $extension) {
$result = $extension($container->get(ContainerInterface::class), $service);
if ($result === null) {
continue;
}
$service = $result;
}
return $service;
}
}
================================================
FILE: src/Helpers/DefinitionNormalizer.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Helpers;
use Yiisoft\Definitions\ArrayDefinition;
use Yiisoft\Definitions\Contract\DefinitionInterface;
use Yiisoft\Definitions\Exception\InvalidConfigException;
use Yiisoft\Definitions\Helpers\Normalizer;
use Yiisoft\Di\ExtensibleService;
use function is_array;
/**
* @internal Normalizes a definition.
*/
final class DefinitionNormalizer
{
/**
* @param mixed $definition Definition to normalize.
* @param string $id Service ID.
*
* @throws InvalidConfigException If configuration is not valid.
*/
public static function normalize(mixed $definition, string $id): DefinitionInterface
{
if (is_array($definition) && isset($definition[DefinitionParser::IS_PREPARED_ARRAY_DEFINITION_DATA])) {
/** @psalm-suppress MixedArgument Definition should be valid {@see Container::$validate} */
return ArrayDefinition::fromPreparedData(
$definition['class'] ?? $id,
$definition['__construct()'],
$definition['methodsAndProperties'],
);
}
if ($definition instanceof ExtensibleService) {
return $definition;
}
return Normalizer::normalize($definition, $id);
}
}
================================================
FILE: src/Helpers/DefinitionParser.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Helpers;
use Yiisoft\Definitions\ArrayDefinition;
use function count;
use function is_array;
use function is_callable;
use function is_string;
/**
* @internal Splits metadata and definition.
*
* Supports the following configuration:
*
* 1) With a dedicated definition:
*
* ```php
* Engine::class => [
* 'definition' => [
* '__class' => BigEngine::class,
* 'setNumber()' => [42],
* ],
* 'tags' => ['a', 'b'],
* 'reset' => function () {
* $this->number = 42;
* },
* ]
* ```
*
* 2) Mixed in array definition:
*
* ```php
* Engine::class => [
* '__class' => BigEngine::class,
* 'setNumber()' => [42],
* 'tags' => ['a', 'b'],
* 'reset' => function () {
* $this->number = 42;
* },
* ]
* ```
*/
final class DefinitionParser
{
public const IS_PREPARED_ARRAY_DEFINITION_DATA = 'isPreparedArrayDefinitionData';
private const DEFINITION_META = 'definition';
/**
* @param mixed $definition Definition to parse.
*
* @return array Definition parsed into an array of a special structure.
* @psalm-return array{mixed,array}
*/
public static function parse(mixed $definition): array
{
if (!is_array($definition)) {
return [$definition, []];
}
// Dedicated definition
if (isset($definition[self::DEFINITION_META])) {
$newDefinition = $definition[self::DEFINITION_META];
unset($definition[self::DEFINITION_META]);
return [$newDefinition, $definition];
}
// Callable definition
if (is_callable($definition, true)) {
return [$definition, []];
}
// Array definition
$meta = [];
$class = null;
$constructorArguments = [];
$methodsAndProperties = [];
foreach ($definition as $key => $value) {
if (is_string($key)) {
// Class
if ($key === ArrayDefinition::CLASS_NAME) {
$class = $value;
continue;
}
// Constructor arguments
if ($key === ArrayDefinition::CONSTRUCTOR) {
$constructorArguments = $value;
continue;
}
// Methods and properties
if (count($methodArray = explode('()', $key, 2)) === 2) {
$methodsAndProperties[$key] = [ArrayDefinition::TYPE_METHOD, $methodArray[0], $value];
continue;
}
if (count($propertyArray = explode('$', $key, 2)) === 2) {
$methodsAndProperties[$key] = [ArrayDefinition::TYPE_PROPERTY, $propertyArray[1], $value];
continue;
}
}
$meta[$key] = $value;
}
return [
[
'class' => $class,
'__construct()' => $constructorArguments,
'methodsAndProperties' => $methodsAndProperties,
self::IS_PREPARED_ARRAY_DEFINITION_DATA => true,
],
$meta,
];
}
}
================================================
FILE: src/NotFoundException.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di;
use Exception;
use Psr\Container\NotFoundExceptionInterface;
use Throwable;
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
use function sprintf;
/**
* `NotFoundException` is thrown when no definition or class was found in the container for a given ID.
*/
final class NotFoundException extends Exception implements NotFoundExceptionInterface, FriendlyExceptionInterface
{
/**
* @param string $id ID of the definition or name of the class that was not found.
* @param string[] $buildStack Stack of IDs of services requested definition or class that was not found.
*/
public function __construct(
private readonly string $id,
private array $buildStack = [],
?Throwable $previous = null,
) {
if (empty($this->buildStack)) {
$message = sprintf('No definition or class found or resolvable for "%s".', $id);
} elseif ($this->buildStack === [$id]) {
$message = sprintf('No definition or class found or resolvable for "%s" while building it.', $id);
} else {
$message = sprintf(
'No definition or class found or resolvable for "%s" while building "%s".',
end($this->buildStack),
implode('" -> "', $buildStack),
);
}
parent::__construct($message, previous: $previous);
}
public function getId(): string
{
return $this->id;
}
/**
* @return string[]
*/
public function getBuildStack(): array
{
return $this->buildStack;
}
public function getName(): string
{
return sprintf('No definition or class found for "%s" ID.', $this->id);
}
public function getSolution(): ?string
{
$solution = <<<SOLUTION
Ensure that either a service with ID "%1\$s" is defined or such class exists and is autoloadable.
SOLUTION;
return sprintf($solution, $this->id);
}
}
================================================
FILE: src/Reference/TagReference.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Reference;
use InvalidArgumentException;
use Yiisoft\Definitions\Reference;
use function sprintf;
/**
* Helper class used to specify a reference to a tag.
* For example, `TagReference::to('my-tag')` specifies a reference to all services that are tagged with `tag@my-tag`.
*/
final class TagReference
{
private const PREFIX = 'tag@';
private function __construct() {}
public static function to(string $tag): Reference
{
return Reference::to(self::id($tag));
}
public static function id(string $tag): string
{
return self::PREFIX . $tag;
}
public static function extractTagFromAlias(string $alias): string
{
if (!str_starts_with($alias, self::PREFIX)) {
throw new InvalidArgumentException(sprintf('Alias "%s" is not a tag alias.', $alias));
}
return substr($alias, 4);
}
public static function isTagAlias(string $id): bool
{
return str_starts_with($id, self::PREFIX);
}
}
================================================
FILE: src/ServiceProviderInterface.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di;
/**
* Represents a component responsible for class registration in the Container.
*
* The goal of service providers is to centralize and organize in one place
* registration of classes bound by any logic or classes with complex dependencies.
*
* You can organize registration of a service and its dependencies in a single
* provider class except for creating a bootstrap file or configuration array for the Container.
*
* Example:
*
* ```php
* class CarProvider implements ServiceProviderInterface
* {
* public function getDefinitions(): array
* {
* return [
* 'car' => ['class' => Car::class],
* 'car-factory' => CarFactory::class,
* EngineInterface::class => EngineMarkOne::class,
* ];
* }
* }
* ```
*/
interface ServiceProviderInterface
{
/**
* Returns definitions for the container.
*
* This method:
*
* - Should only return definitions for the Container preventing any side effects.
* - Should be idempotent.
*
* @return array Definitions for the container. Each array key is the name of the service (usually it is
* an interface name), and a corresponding value is a service definition.
*/
public function getDefinitions(): array;
/**
* Returns an array of service extensions.
*
* An extension is callable that returns a modified service object:
*
* ```php
* static function (ContainerInterface $container, $service) {
* return $service->withAnotherOption(42);
* }
* ```
*
* @return array Extensions for the container services. Each array key is the name of the service to be modified
* and a corresponding value is callable doing the job.
*/
public function getExtensions(): array;
}
================================================
FILE: src/StateResetter.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di;
use Closure;
use InvalidArgumentException;
use Psr\Container\ContainerInterface;
use function is_int;
use function is_object;
use function sprintf;
/**
* State resetter allows resetting state of the services that are currently stored in the container and have "reset"
* callback defined. The reset should be triggered after each request-response cycle in case you build long-running
* applications with tools like [Swoole](https://www.swoole.co.uk/) or [RoadRunner](https://roadrunner.dev/).
*/
final class StateResetter
{
/**
* @var Closure[]|self[]
*/
private array $resetters = [];
/**
* @param ContainerInterface $container Container to reset.
*/
public function __construct(
private readonly ContainerInterface $container,
) {}
/**
* Reset the container.
*/
public function reset(): void
{
foreach ($this->resetters as $resetter) {
if ($resetter instanceof self) {
$resetter->reset();
continue;
}
$resetter($this->container);
}
}
/**
* @param Closure[]|self[] $resetters Array of reset callbacks. Each callback has access to the private and
* protected properties of the service instance, so you can set the initial state of the service efficiently
* without creating a new instance.
*/
public function setResetters(array $resetters): void
{
$this->resetters = [];
foreach ($resetters as $serviceId => $callback) {
if (is_int($serviceId)) {
if (!$callback instanceof self) {
throw new InvalidArgumentException(sprintf(
'State resetter object should be instance of "%s", "%s" given.',
self::class,
get_debug_type($callback),
));
}
$this->resetters[] = $callback;
continue;
}
if (!$callback instanceof Closure) {
throw new InvalidArgumentException(
'Callback for state resetter should be closure in format '
. '`function (ContainerInterface $container): void`. '
. 'Got "' . get_debug_type($callback) . '".',
);
}
$instance = $this->container->get($serviceId);
if (!is_object($instance)) {
throw new InvalidArgumentException(
'State resetter supports resetting objects only. Container returned '
. get_debug_type($instance)
. '.',
);
}
/** @var Closure */
$this->resetters[] = $callback->bindTo($instance, $instance::class);
}
}
}
================================================
FILE: tests/Benchmark/ContainerBench.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Benchmark;
use PhpBench\Benchmark\Metadata\Annotations\BeforeMethods;
use PhpBench\Benchmark\Metadata\Annotations\Groups;
use PhpBench\Benchmark\Metadata\Annotations\Iterations;
use PhpBench\Benchmark\Metadata\Annotations\ParamProviders;
use PhpBench\Benchmark\Metadata\Annotations\Revs;
use Yiisoft\Di\CompositeContainer;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\Di\Tests\Support\Car;
use Yiisoft\Di\Tests\Support\EngineInterface;
use Yiisoft\Di\Tests\Support\EngineMarkOne;
use Yiisoft\Di\Tests\Support\EngineMarkTwo;
use Yiisoft\Di\Tests\Support\NullableConcreteDependency;
use Yiisoft\Di\Tests\Support\PropertyTestClass;
use Yiisoft\Definitions\Reference;
use Yiisoft\Definitions\Exception\InvalidConfigException;
use Yiisoft\Definitions\Exception\NotInstantiableException;
/**
* @Iterations(5)
* @Revs(1000)
* @BeforeMethods({"before"})
*/
class ContainerBench
{
public const SERVICE_COUNT = 200;
private CompositeContainer $composite;
/** @var int[] */
private array $indexes = [];
/** @var int[] */
private array $randomIndexes = [];
public function provideDefinitions(): array
{
return [
['serviceClass' => PropertyTestClass::class],
[
'serviceClass' => NullableConcreteDependency::class,
'otherDefinitions' => [
EngineInterface::class => EngineMarkOne::class,
Car::class => Car::class,
EngineMarkOne::class => EngineMarkOne::class,
],
],
[
'serviceClass' => NullableConcreteDependency::class,
'otherDefinitions' => [
EngineInterface::class => EngineMarkTwo::class,
],
],
];
}
/**
* Load the bulk of the definitions.
* These all refer to a service that is not yet defined but must be defined in the benchmark.
*/
public function before(): void
{
$definitions3 = [];
$definitions2 = [];
$definitions3['service'] = PropertyTestClass::class;
for ($i = 0; $i < self::SERVICE_COUNT; $i++) {
$this->indexes[] = $i;
$definitions2["second$i"] = Reference::to('service');
$definitions3["third$i"] = Reference::to('service');
}
$this->randomIndexes = $this->indexes;
shuffle($this->randomIndexes);
$this->composite = new CompositeContainer();
// Attach the dummy containers multiple times, to see what would happen if there are lots of them.
$this->composite->attach(
new Container(
ContainerConfig::create()
->withDefinitions($definitions2),
),
);
$this->composite->attach(
new Container(
ContainerConfig::create()
->withDefinitions($definitions3),
),
);
$this->composite->attach(
new Container(
ContainerConfig::create()
->withDefinitions($definitions2),
),
);
$this->composite->attach(
new Container(
ContainerConfig::create()
->withDefinitions($definitions3),
),
);
$this->composite->attach(
new Container(
ContainerConfig::create()
->withDefinitions($definitions2),
),
);
$this->composite->attach(
new Container(
ContainerConfig::create()
->withDefinitions($definitions3),
),
);
$this->composite->attach(
new Container(
ContainerConfig::create()
->withDefinitions($definitions2),
),
);
$this->composite->attach(
new Container(
ContainerConfig::create()
->withDefinitions($definitions3),
),
);
}
/**
* @Groups({"construct"})
*
* @throws InvalidConfigException
* @throws NotInstantiableException
*/
public function benchConstruct(): void
{
$definitions = [];
for ($i = 0; $i < self::SERVICE_COUNT; $i++) {
$definitions["service$i"] = PropertyTestClass::class;
}
$container = new Container(
ContainerConfig::create()
->withDefinitions($definitions),
);
}
/**
* @Groups({"lookup"})
* @ParamProviders({"provideDefinitions"})
*/
public function benchSequentialLookups($params): void
{
$definitions = [];
for ($i = 0; $i < self::SERVICE_COUNT; $i++) {
$definitions["service$i"] = $params['serviceClass'];
}
if (isset($params['otherDefinitions'])) {
$definitions = array_merge($definitions, $params['otherDefinitions']);
}
$container = new Container(
ContainerConfig::create()
->withDefinitions($definitions),
);
for ($i = 0; $i < self::SERVICE_COUNT / 2; $i++) {
// Do array lookup.
$index = $this->indexes[$i];
$container->get("service$index");
}
}
/**
* @Groups({"lookup"})
* @ParamProviders({"provideDefinitions"})
*/
public function benchRandomLookups($params): void
{
$definitions = [];
for ($i = 0; $i < self::SERVICE_COUNT; $i++) {
$definitions["service$i"] = $params['serviceClass'];
}
if (isset($params['otherDefinitions'])) {
$definitions = array_merge($definitions, $params['otherDefinitions']);
}
$container = new Container(
ContainerConfig::create()
->withDefinitions($definitions),
);
for ($i = 0; $i < self::SERVICE_COUNT / 2; $i++) {
// Do array lookup.
$index = $this->randomIndexes[$i];
$container->get("service$index");
}
}
/**
* @Groups({"lookup"})
* @ParamProviders({"provideDefinitions"})
*/
public function benchRandomLookupsComposite($params): void
{
$definitions = [];
for ($i = 0; $i < self::SERVICE_COUNT; $i++) {
$definitions["service$i"] = $params['serviceClass'];
}
if (isset($params['otherDefinitions'])) {
$definitions = array_merge($definitions, $params['otherDefinitions']);
}
$container = new Container(
ContainerConfig::create()
->withDefinitions($definitions),
);
$this->composite->attach($container);
for ($i = 0; $i < self::SERVICE_COUNT / 2; $i++) {
// Do array lookup.
$index = $this->randomIndexes[$i];
$this->composite->get("service$index");
}
}
}
================================================
FILE: tests/Benchmark/ContainerMethodHasBench.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Benchmark;
use PhpBench\Benchmark\Metadata\Annotations\BeforeMethods;
use PhpBench\Benchmark\Metadata\Annotations\Groups;
use PhpBench\Benchmark\Metadata\Annotations\Iterations;
use PhpBench\Benchmark\Metadata\Annotations\Revs;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\Di\Tests\Support\GearBox;
use Yiisoft\Di\Tests\Support\PropertyTestClass;
use Yiisoft\Definitions\Reference;
/**
* @Iterations(5)
* @Revs(1000)
* @Groups({"has"})
* @BeforeMethods({"before"})
*/
class ContainerMethodHasBench
{
private const SERVICE_COUNT = 200;
private Container $container;
/**
* Load the bulk of the definitions.
*/
public function before(): void
{
$definitions = [];
for ($i = 0; $i < self::SERVICE_COUNT; $i++) {
$definitions["service$i"] = Reference::to('service');
}
$definitions['service'] = PropertyTestClass::class;
$this->container = new Container(
ContainerConfig::create()
->withDefinitions($definitions),
);
}
public function benchPredefinedExisting(): void
{
for ($i = 0; $i < self::SERVICE_COUNT; $i++) {
$this->container->has("service$i");
}
}
public function benchUndefinedExisting(): void
{
for ($i = 0; $i < self::SERVICE_COUNT; $i++) {
$this->container->has(GearBox::class);
}
}
public function benchUndefinedNonexistent(): void
{
for ($i = 0; $i < self::SERVICE_COUNT; $i++) {
$this->container->has('NonexistentNamespace\NonexistentClass');
}
}
}
================================================
FILE: tests/Support/A.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
class A
{
public function __construct(public ?B $b = null) {}
}
================================================
FILE: tests/Support/B.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
class B
{
public function __construct(public ?A $a = null) {}
}
================================================
FILE: tests/Support/Car.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* A car
*/
class Car
{
public ColorInterface $color;
public function __construct(
private readonly EngineInterface $engine,
private readonly array $moreEngines = [],
) {}
public function setColor(ColorInterface $color): self
{
$this->color = $color;
return $this;
}
public function getColor(): ColorInterface
{
return $this->color;
}
public function getEngine(): EngineInterface
{
return $this->engine;
}
public function getEngineName(): string
{
return $this->engine->getName();
}
public function getMoreEngines(): array
{
return $this->moreEngines;
}
}
================================================
FILE: tests/Support/CarExtensionProvider.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
use Psr\Container\ContainerInterface;
use Yiisoft\Di\ServiceProviderInterface;
final class CarExtensionProvider implements ServiceProviderInterface
{
public function getDefinitions(): array
{
return [];
}
public function getExtensions(): array
{
return [
Car::class => static function (ContainerInterface $container, Car $car) {
$car->setColor(new ColorRed());
return $car;
},
EngineInterface::class => static fn(ContainerInterface $container, EngineInterface $engine) => $container->get(EngineMarkTwo::class),
];
}
}
================================================
FILE: tests/Support/CarFactory.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* Produces cars
*/
class CarFactory
{
public static function create(EngineInterface $engine): Car
{
return new Car($engine);
}
public function createByEngineName(EngineFactory $factory, $name): Car
{
return new Car($factory->createByName($name));
}
public function createWithColor(ColorInterface $color): Car
{
$car = new Car(EngineFactory::createDefault());
return $car->setColor($color);
}
}
================================================
FILE: tests/Support/CarProvider.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
use Psr\Container\ContainerInterface;
use Yiisoft\Di\ServiceProviderInterface;
final class CarProvider implements ServiceProviderInterface
{
public function getDefinitions(): array
{
return [
'car' => Car::class,
EngineInterface::class => EngineMarkOne::class,
];
}
public function getExtensions(): array
{
return [
Car::class => static function (ContainerInterface $container, Car $car) {
$car->setColor(new ColorPink());
return $car;
},
'sport_car' => static function (ContainerInterface $container, SportCar $car) {
$car->setColor(new ColorPink());
return $car;
},
];
}
}
================================================
FILE: tests/Support/ColorInterface.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* Interface ColorInterface defines car color
*/
interface ColorInterface
{
public function getColor(): string;
}
================================================
FILE: tests/Support/ColorPink.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* Class ColorPink
*/
final class ColorPink implements ColorInterface
{
private const COLOR_PINK = 'pink';
public function getColor(): string
{
return self::COLOR_PINK;
}
}
================================================
FILE: tests/Support/ColorRed.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* Class ColorRed
*/
final class ColorRed implements ColorInterface
{
private const COLOR_PINK = 'red';
public function getColor(): string
{
return self::COLOR_PINK;
}
}
================================================
FILE: tests/Support/ConstructorTestClass.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
use function func_get_args;
/**
* ConstructorTestClass
*/
class ConstructorTestClass
{
private readonly array $allParameters;
/**
* ConstructorTestClass constructor.
*
* @param $parameter
*/
public function __construct(private $parameter)
{
$this->allParameters = func_get_args();
}
/**
* @return mixed
*/
public function getParameter()
{
return $this->parameter;
}
public function getAllParameters(): array
{
return $this->allParameters;
}
}
================================================
FILE: tests/Support/ContainerInterfaceExtensionProvider.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
use Psr\Container\ContainerInterface;
use Yiisoft\Di\ServiceProviderInterface;
final class ContainerInterfaceExtensionProvider implements ServiceProviderInterface
{
public function getDefinitions(): array
{
return [];
}
public function getExtensions(): array
{
return [
ContainerInterface::class => static fn(ContainerInterface $container, ContainerInterface $extended) => $container,
];
}
}
================================================
FILE: tests/Support/Cycle/Chicken.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support\Cycle;
class Chicken
{
public function __construct(Egg $egg) {}
}
================================================
FILE: tests/Support/Cycle/Egg.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support\Cycle;
class Egg
{
public function __construct(Chicken $chicken) {}
}
================================================
FILE: tests/Support/EngineFactory.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
use Exception;
use Psr\Container\ContainerInterface;
/**
* EngineFactory
*/
class EngineFactory
{
public function __construct(private readonly ContainerInterface $container) {}
public function createByName(?string $name = null): EngineInterface
{
if ($name === EngineMarkOne::NAME) {
return $this->container->get(EngineMarkOne::class);
}
if ($name === EngineMarkTwo::NAME) {
return $this->container->get(EngineMarkTwo::class);
}
throw new Exception('unknown engine name: ' . $name);
}
public static function createDefault(): EngineInterface
{
return new EngineMarkOne();
}
}
================================================
FILE: tests/Support/EngineInterface.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* EngineInterface defines car engine interface
*/
interface EngineInterface
{
public function getName(): string;
public function setNumber(int $value): void;
public function getNumber(): int;
}
================================================
FILE: tests/Support/EngineMarkOne.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* EngineMarkOne
*/
class EngineMarkOne implements EngineInterface
{
public const NAME = 'Mark One';
public const NUMBER = 1;
public function __construct(private int $number = self::NUMBER) {}
public function getName(): string
{
return static::NAME;
}
public function setNumber(int $value): void
{
$this->number = $value;
}
public function getNumber(): int
{
return $this->number;
}
}
================================================
FILE: tests/Support/EngineMarkTwo.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* EngineMarkTwo
*/
class EngineMarkTwo implements EngineInterface
{
public const NAME = 'Mark Two';
public const NUMBER = 2;
public function __construct(private int $number = self::NUMBER) {}
public function getName(): string
{
return static::NAME;
}
public function setNumber(int $value): void
{
$this->number = $value;
}
public function getNumber(): int
{
return $this->number;
}
}
================================================
FILE: tests/Support/EngineStorage.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
final class EngineStorage
{
private readonly array $engines;
public function __construct(EngineInterface ...$engines)
{
$this->engines = $engines;
}
public function getEngines(): array
{
return $this->engines;
}
}
================================================
FILE: tests/Support/Garage.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* A garage
*/
final class Garage
{
public function __construct(private readonly SportCar $car) {}
public function getCar(): SportCar
{
return $this->car;
}
}
================================================
FILE: tests/Support/GearBox.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* A gear box.
*/
class GearBox
{
public function __construct(private readonly int $maxGear = 5) {}
}
================================================
FILE: tests/Support/InvokableCarFactory.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
use Psr\Container\ContainerInterface;
class InvokableCarFactory
{
public function __invoke(ContainerInterface $container): Car
{
/** @var EngineInterface $engine */
$engine = $container->get('engine');
return new Car($engine);
}
}
================================================
FILE: tests/Support/MethodTestClass.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* MethodTestClass
*/
class MethodTestClass
{
private $value;
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
public function setValue(mixed $value): void
{
$this->value = $value;
}
}
================================================
FILE: tests/Support/NonPsrContainer.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
use Psr\Container\ContainerInterface;
use stdClass;
final class NonPsrContainer implements ContainerInterface
{
public function get(string $id)
{
return new stdClass();
}
public function has(string $id): bool
{
return false;
}
}
================================================
FILE: tests/Support/NullCarExtensionProvider.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
use Psr\Container\ContainerInterface;
use Yiisoft\Di\ServiceProviderInterface;
final class NullCarExtensionProvider implements ServiceProviderInterface
{
public function getDefinitions(): array
{
return [
];
}
public function getExtensions(): array
{
return [
Car::class => static fn(ContainerInterface $container, Car $car) => null,
];
}
}
================================================
FILE: tests/Support/NullableConcreteDependency.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
class NullableConcreteDependency
{
public function __construct(?Car $car) {}
}
================================================
FILE: tests/Support/OptionalConcreteDependency.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
class OptionalConcreteDependency
{
public function __construct(private readonly ?Car $car = null) {}
public function getCar(): ?Car
{
return $this->car;
}
}
================================================
FILE: tests/Support/PropertyTestClass.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* PropertyTestClass
*/
class PropertyTestClass
{
public $property;
}
================================================
FILE: tests/Support/SportCar.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* A sport car
*/
class SportCar
{
public ColorInterface $color;
public function __construct(
private readonly EngineInterface $engine,
private readonly int $maxSpeed,
) {}
public function setColor(ColorInterface $color): self
{
$this->color = $color;
return $this;
}
public function getColor(): ColorInterface
{
return $this->color;
}
public function getEngine(): EngineInterface
{
return $this->engine;
}
public function getEngineName(): string
{
return $this->engine->getName();
}
public function getMaxSpeed(): int
{
return $this->maxSpeed;
}
}
================================================
FILE: tests/Support/StaticFactory.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
use stdClass;
final class StaticFactory
{
public static function create(): stdClass
{
return new stdClass();
}
}
================================================
FILE: tests/Support/TreeItem.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
/**
* TreeItem
*/
class TreeItem
{
public function __construct(private readonly self $treeItem) {}
}
================================================
FILE: tests/Support/UnionTypeInConstructorFirstTypeInParamResolvable.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
final class UnionTypeInConstructorFirstTypeInParamResolvable
{
public function __construct(private readonly EngineMarkOne|EngineInterface $engine) {}
}
================================================
FILE: tests/Support/UnionTypeInConstructorParamNotResolvable.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
final class UnionTypeInConstructorParamNotResolvable
{
public function __construct(private readonly EngineInterface|ColorInterface $param) {}
}
================================================
FILE: tests/Support/UnionTypeInConstructorSecondParamNotResolvable.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
final class UnionTypeInConstructorSecondParamNotResolvable
{
public function __construct(EngineMarkOne|EngineInterface $engine, string $name) {}
}
================================================
FILE: tests/Support/UnionTypeInConstructorSecondTypeInParamResolvable.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
final class UnionTypeInConstructorSecondTypeInParamResolvable
{
public function __construct(private readonly EngineInterface|EngineMarkOne $engine) {}
}
================================================
FILE: tests/Support/VariadicConstructor.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Support;
final class VariadicConstructor
{
private readonly array $parameters;
public function __construct(
private $first,
private readonly EngineInterface $engine,
...$parameters,
) {
$this->parameters = $parameters;
}
public function getFirst()
{
return $this->first;
}
public function getEngine(): EngineInterface
{
return $this->engine;
}
public function getParameters(): array
{
return $this->parameters;
}
}
================================================
FILE: tests/Unit/BuildingExceptionTest.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Unit;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use Yiisoft\Di\BuildingException;
final class BuildingExceptionTest extends TestCase
{
public function testMessage(): void
{
$exception = new BuildingException('test', new RuntimeException('i am angry'));
$this->assertSame('Caught unhandled error "i am angry" while building "test".', $exception->getMessage());
$this->assertSame('Unable to build "test" object.', $exception->getName());
$this->assertSame(
<<<SOLUTION
Ensure that either a service with ID "test" is defined or such class exists and is autoloadable.
Ensure that configuration for service with ID "test" is correct.
SOLUTION,
$exception->getSolution(),
);
}
public function testEmptyMessage(): void
{
$exception = new BuildingException('test', new RuntimeException());
$this->assertSame('Caught unhandled error "RuntimeException" while building "test".', $exception->getMessage());
}
public function testBuildStack(): void
{
$exception = new BuildingException('test', new RuntimeException('i am angry'), ['a', 'b', 'test']);
$this->assertSame('Caught unhandled error "i am angry" while building "a" -> "b" -> "test".', $exception->getMessage());
}
public function testCode(): void
{
$exception = new BuildingException('test', new RuntimeException());
$this->assertSame(0, $exception->getCode());
}
}
================================================
FILE: tests/Unit/CompositeContainerTest.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Unit;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\TestCase;
use Yiisoft\Di\CompositeContainer;
use Yiisoft\Di\CompositeNotFoundException;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\Di\Tests\Support\EngineMarkOne;
use Yiisoft\Di\Tests\Support\EngineMarkTwo;
use Yiisoft\Di\Tests\Support\NonPsrContainer;
use Yiisoft\Test\Support\Container\SimpleContainer;
use function PHPUnit\Framework\assertFalse;
use function PHPUnit\Framework\assertSame;
final class CompositeContainerTest extends TestCase
{
public function testGetNonString(): void
{
$container = new CompositeContainer();
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessageMatches(
'/^ID must be a string, (integer|int) given\.$/',
);
$container->get(42);
}
public function testTagsWithYiiAndNotYiiContainers(): void
{
$compositeContainer = new CompositeContainer();
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkOne::class => [
'class' => EngineMarkOne::class,
'tags' => ['engine'],
],
EngineMarkTwo::class => [
'class' => EngineMarkTwo::class,
'tags' => ['engine'],
],
]);
$firstContainer = new Container($config);
$secondContainer = new \League\Container\Container();
$compositeContainer->attach($firstContainer);
$compositeContainer->attach($secondContainer);
$engines = $compositeContainer->get('tag@engine');
$this->assertIsArray($engines);
$this->assertCount(2, $engines);
$this->assertInstanceOf(EngineMarkOne::class, $engines[0]);
$this->assertInstanceOf(EngineMarkTwo::class, $engines[1]);
}
public function testNonPsrContainer(): void
{
$compositeContainer = new CompositeContainer();
$compositeContainer->attach(new NonPsrContainer());
$this->expectException(CompositeNotFoundException::class);
$this->expectExceptionMessageMatches(
'/No definition or class found or resolvable in composite container/',
);
$this->expectExceptionMessageMatches(
'/Container "has\(\)" returned false, but no exception was thrown from "get\(\)"\./',
);
$compositeContainer->get('test');
}
public function testHasNoString(): void
{
$container = new CompositeContainer();
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('ID must be a string, bool given.');
$container->has(true);
}
#[TestWith([true, 'engine'])]
#[TestWith([false, 'other'])]
public function testHasTag(bool $expected, string $tag): void
{
$container = new CompositeContainer();
$container->attach(
new Container(
ContainerConfig::create()->withTags(['engine' => []]),
),
);
assertSame($expected, $container->has('tag@' . $tag));
}
public function testHasTagWithoutYiiContainer(): void
{
$container = new CompositeContainer();
$container->attach(new SimpleContainer());
assertFalse($container->has('tag@engine'));
}
}
================================================
FILE: tests/Unit/CompositePsrContainerOverLeagueTest.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Unit;
use League\Container\Container;
use Psr\Container\ContainerInterface;
use Yiisoft\Di\CompositeContainer;
use Yiisoft\Di\CompositeNotFoundException;
/**
* Test the CompositeContainer over League Container.
*/
final class CompositePsrContainerOverLeagueTest extends CompositePsrContainerTestAbstract
{
public function createContainer(iterable $definitions = []): ContainerInterface
{
$container = $this->setupContainer(new Container(), $definitions);
return $this->createCompositeContainer($container);
}
public function setupContainer(ContainerInterface $container, iterable $definitions = []): ContainerInterface
{
foreach ($definitions as $id => $definition) {
$container->add($id, $definition);
}
return $container;
}
public function testNotFoundException(): void
{
$compositeContainer = new CompositeContainer();
$container1 = new Container();
$container1Id = spl_object_id($container1);
$container2 = new Container();
$container2Id = spl_object_id($container2);
$compositeContainer->attach($container1);
$compositeContainer->attach($container2);
$this->expectException(CompositeNotFoundException::class);
$this->expectExceptionMessage("No definition or class found or resolvable in composite container:\n 1. Container League\Container\Container #$container1Id: Alias (test) is not being managed by the container or delegates\n 2. Container League\Container\Container #$container2Id: Alias (test) is not being managed by the container or delegates");
$compositeContainer->get('test');
}
}
================================================
FILE: tests/Unit/CompositePsrContainerOverYiisoftTest.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Unit;
use Psr\Container\ContainerInterface;
use Yiisoft\Di\CompositeContainer;
use Yiisoft\Di\CompositeNotFoundException;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\Di\StateResetter;
use Yiisoft\Di\Tests\Support\EngineMarkOne;
use Yiisoft\Di\Tests\Support\EngineMarkTwo;
/**
* Test the CompositeContainer over Yiisoft Container.
*/
final class CompositePsrContainerOverYiisoftTest extends CompositePsrContainerTestAbstract
{
public function createContainer(iterable $definitions = []): ContainerInterface
{
$config = ContainerConfig::create()
->withDefinitions($definitions);
$container = new Container($config);
return $this->createCompositeContainer($container);
}
public function testResetterInCompositeContainerWithExternalResetter(): void
{
$composite = $this->createContainer([
StateResetter::class => function (ContainerInterface $container) {
$resetter = new StateResetter($container);
$resetter->setResetters([
'engineMarkOne' => function () {
$this->number = 42;
},
]);
return $resetter;
},
'engineMarkOne' => function () {
$engine = new EngineMarkOne();
$engine->setNumber(42);
return $engine;
},
]);
$config = ContainerConfig::create()
->withDefinitions([
'engineMarkTwo' => ['class' => EngineMarkTwo::class,
'setNumber()' => [43],
'reset' => function () {
$this->number = 43;
},
],
]);
$secondContainer = new Container($config);
$composite->attach($secondContainer);
$engineMarkOne = $composite->get('engineMarkOne');
$engineMarkTwo = $composite->get('engineMarkTwo');
$this->assertSame(
42,
$composite
->get('engineMarkOne')
->getNumber(),
);
$this->assertSame(
43,
$composite
->get('engineMarkTwo')
->getNumber(),
);
$engineMarkOne->setNumber(45);
$engineMarkTwo->setNumber(46);
$this->assertSame(
45,
$composite
->get('engineMarkOne')
->getNumber(),
);
$this->assertSame(
46,
$composite
->get('engineMarkTwo')
->getNumber(),
);
$composite
->get(StateResetter::class)
->reset();
$this->assertSame($engineMarkOne, $composite->get('engineMarkOne'));
$this->assertSame($engineMarkTwo, $composite->get('engineMarkTwo'));
$this->assertSame(
42,
$composite
->get('engineMarkOne')
->getNumber(),
);
$this->assertSame(
43,
$composite
->get('engineMarkTwo')
->getNumber(),
);
}
public function testNotFoundException(): void
{
$compositeContainer = new CompositeContainer();
$container1 = new Container();
$container1Id = spl_object_id($container1);
$container2 = new Container();
$container2Id = spl_object_id($container2);
$compositeContainer->attach($container1);
$compositeContainer->attach($container2);
$this->expectException(CompositeNotFoundException::class);
$this->expectExceptionMessage("No definition or class found or resolvable in composite container:\n 1. Container Yiisoft\Di\Container #$container1Id: No definition or class found or resolvable for \"test\" while building it.\n 2. Container Yiisoft\Di\Container #$container2Id: No definition or class found or resolvable for \"test\" while building it.");
$compositeContainer->get('test');
}
}
================================================
FILE: tests/Unit/CompositePsrContainerTestAbstract.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Unit;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use Yiisoft\Di\CompositeContainer;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\Di\Tests\Support\Car;
use Yiisoft\Di\Tests\Support\UnionTypeInConstructorParamNotResolvable;
use Yiisoft\Di\Tests\Support\EngineInterface;
use Yiisoft\Di\Tests\Support\EngineMarkOne;
use Yiisoft\Di\Tests\Support\EngineMarkTwo;
/**
* General tests for PSR-11 composite container.
* To be extended for specific containers.
*/
abstract class CompositePsrContainerTestAbstract extends PsrContainerTestAbstract
{
public function createCompositeContainer(ContainerInterface $attachedContainer): ContainerInterface
{
$compositeContainer = new CompositeContainer();
$compositeContainer->attach($attachedContainer);
return $compositeContainer;
}
public function testAttach(): void
{
$compositeContainer = new CompositeContainer();
$config = ContainerConfig::create()
->withDefinitions([
'test' => EngineMarkOne::class,
]);
$container = new Container($config);
$compositeContainer->attach($container);
$this->assertTrue($compositeContainer->has('test'));
$this->assertInstanceOf(EngineMarkOne::class, $compositeContainer->get('test'));
}
public function testDetach(): void
{
$compositeContainer = new CompositeContainer();
$config = ContainerConfig::create()
->withDefinitions([
'test' => EngineMarkOne::class,
]);
$container = new Container($config);
$compositeContainer->attach($container);
$this->assertInstanceOf(EngineMarkOne::class, $compositeContainer->get('test'));
$compositeContainer->detach($container);
$this->expectException(NotFoundExceptionInterface::class);
$this->assertInstanceOf(EngineMarkOne::class, $compositeContainer->get('test'));
}
public function testHasDefinition(): void
{
$compositeContainer = $this->createContainer([EngineInterface::class => EngineMarkOne::class]);
$this->assertTrue($compositeContainer->has(EngineInterface::class));
$config = ContainerConfig::create()
->withDefinitions([
'test' => EngineMarkTwo::class,
]);
$container = new Container($config);
$compositeContainer->attach($container);
$this->assertTrue($compositeContainer->has('test'));
}
public function testGetPriority(): void
{
$compositeContainer = $this->createContainer([EngineInterface::class => EngineMarkOne::class]);
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkTwo::class,
]);
$container = new Container($config);
$compositeContainer->attach($container);
$this->assertInstanceOf(EngineMarkOne::class, $compositeContainer->get(EngineInterface::class));
$config = ContainerConfig::create()
->withDefinitions([EngineInterface::class => EngineMarkOne::class]);
$containerOne = new Container($config);
$config = ContainerConfig::create()
->withDefinitions([EngineInterface::class => EngineMarkTwo::class]);
$containerTwo = new Container($config);
$compositeContainer = new CompositeContainer();
$compositeContainer->attach($containerOne);
$compositeContainer->attach($containerTwo);
$this->assertInstanceOf(EngineMarkOne::class, $compositeContainer->get(EngineInterface::class));
}
public function testTags(): void
{
$compositeContainer = new CompositeContainer();
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkOne::class => [
'class' => EngineMarkOne::class,
'tags' => ['engine'],
],
]);
$firstContainer = new Container($config);
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkTwo::class => [
'class' => EngineMarkTwo::class,
'tags' => ['engine'],
],
]);
$secondContainer = new Container($config);
$compositeContainer->attach($firstContainer);
$compositeContainer->attach($secondContainer);
$engines = $compositeContainer->get('tag@engine');
$this->assertIsArray($engines);
$this->assertCount(2, $engines);
$this->assertSame(EngineMarkOne::class, $engines[1]::class);
$this->assertSame(EngineMarkTwo::class, $engines[0]::class);
}
public function testDelegateLookup(): void
{
$compositeContainer = new CompositeContainer();
$firstContainer = new Container();
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
]);
$secondContainer = new Container($config);
$compositeContainer->attach($firstContainer);
$compositeContainer->attach($secondContainer);
$car = $compositeContainer->get(Car::class);
$this->assertInstanceOf(Car::class, $car);
}
public function testDelegateLookupUnionTypes(): void
{
$compositeContainer = new CompositeContainer();
$firstContainer = new Container();
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
]);
$secondContainer = new Container($config);
$compositeContainer->attach($firstContainer);
$compositeContainer->attach($secondContainer);
$car = $compositeContainer->get(UnionTypeInConstructorParamNotResolvable::class);
$this->assertInstanceOf(UnionTypeInConstructorParamNotResolvable::class, $car);
}
}
================================================
FILE: tests/Unit/Container/DependencyFromDelegate/Car.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Unit\Container\DependencyFromDelegate;
final class Car
{
public function __construct(
public readonly EngineInterface $engine,
) {}
}
================================================
FILE: tests/Unit/Container/DependencyFromDelegate/DependencyFromDelegateTest.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Unit\Container\DependencyFromDelegate;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Throwable;
use Yiisoft\Di\BuildingException;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\Di\NotFoundException;
use Yiisoft\Test\Support\Container\SimpleContainer;
use function PHPUnit\Framework\assertInstanceOf;
use function PHPUnit\Framework\assertSame;
use function sprintf;
final class DependencyFromDelegateTest extends TestCase
{
public function testAnotherContainer(): void
{
$container = new Container(
ContainerConfig::create()
->withDefinitions([
ContainerInterface::class => new SimpleContainer(),
Car::class => Car::class,
])
->withDelegates([
static fn() => new SimpleContainer([
Car::class => new Car(new Engine()),
]),
]),
);
$car = $container->get(Car::class);
assertInstanceOf(Car::class, $car);
assertInstanceOf(Engine::class, $car->engine);
}
public function testNotFoundInDelegate(): void
{
$container = new Container(
ContainerConfig::create()
->withDefinitions([
ContainerInterface::class => new SimpleContainer(),
'car' => Car::class,
])
->withDelegates([
static fn() => new Container(
ContainerConfig::create()
->withDefinitions([
'car' => Car::class,
]),
),
]),
);
$exception = null;
try {
$container->get('car');
} catch (Throwable $exception) {
}
assertInstanceOf(BuildingException::class, $exception);
assertSame(
sprintf(
'Caught unhandled error "No definition or class found or resolvable for "%2$s" while building "%1$s" -> "%3$s" -> "%2$s"." while building "%1$s".',
'car',
EngineInterface::class,
Car::class,
),
$exception->getMessage(),
);
$previous = $exception->getPrevious();
assertInstanceOf(NotFoundException::class, $previous);
$this->assertSame(
sprintf(
'No definition or class found or resolvable for "%2$s" while building "%1$s" -> "%3$s" -> "%2$s".',
'car',
EngineInterface::class,
Car::class,
),
$previous->getMessage(),
);
}
}
================================================
FILE: tests/Unit/Container/DependencyFromDelegate/Engine.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Unit\Container\DependencyFromDelegate;
final class Engine implements EngineInterface {}
================================================
FILE: tests/Unit/Container/DependencyFromDelegate/EngineInterface.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Unit\Container\DependencyFromDelegate;
interface EngineInterface {}
================================================
FILE: tests/Unit/ContainerTest.php
================================================
<?php
declare(strict_types=1);
namespace Yiisoft\Di\Tests\Unit;
use ArrayIterator;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use RuntimeException;
use stdClass;
use Throwable;
use Yiisoft\Di\BuildingException;
use Yiisoft\Di\CompositeContainer;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\Di\ExtensibleService;
use Yiisoft\Di\NotFoundException;
use Yiisoft\Di\StateResetter;
use Yiisoft\Di\ServiceProviderInterface;
use Yiisoft\Di\Tests\Support\A;
use Yiisoft\Di\Tests\Support\B;
use Yiisoft\Di\Tests\Support\Car;
use Yiisoft\Di\Tests\Support\CarFactory;
use Yiisoft\Di\Tests\Support\ColorInterface;
use Yiisoft\Di\Tests\Support\ColorPink;
use Yiisoft\Di\Tests\Support\ColorRed;
use Yiisoft\Di\Tests\Support\ConstructorTestClass;
use Yiisoft\Di\Tests\Support\Cycle\Chicken;
use Yiisoft\Di\Tests\Support\Cycle\Egg;
use Yiisoft\Di\Tests\Support\EngineFactory;
use Yiisoft\Di\Tests\Support\EngineInterface;
use Yiisoft\Di\Tests\Support\EngineMarkOne;
use Yiisoft\Di\Tests\Support\EngineMarkTwo;
use Yiisoft\Di\Tests\Support\EngineStorage;
use Yiisoft\Di\Tests\Support\Garage;
use Yiisoft\Di\Tests\Support\InvokableCarFactory;
use Yiisoft\Di\Tests\Support\MethodTestClass;
use Yiisoft\Di\Tests\Support\NullableConcreteDependency;
use Yiisoft\Di\Tests\Support\OptionalConcreteDependency;
use Yiisoft\Di\Tests\Support\PropertyTestClass;
use Yiisoft\Di\Tests\Support\SportCar;
use Yiisoft\Di\Tests\Support\TreeItem;
use Yiisoft\Di\Tests\Support\UnionTypeInConstructorSecondTypeInParamResolvable;
use Yiisoft\Di\Tests\Support\UnionTypeInConstructorSecondParamNotResolvable;
use Yiisoft\Di\Tests\Support\UnionTypeInConstructorParamNotResolvable;
use Yiisoft\Di\Tests\Support\UnionTypeInConstructorFirstTypeInParamResolvable;
use Yiisoft\Di\Tests\Support\VariadicConstructor;
use Yiisoft\Definitions\DynamicReference;
use Yiisoft\Definitions\Exception\CircularReferenceException;
use Yiisoft\Definitions\Exception\InvalidConfigException;
use Yiisoft\Definitions\Reference;
use Yiisoft\Injector\Injector;
use Yiisoft\Test\Support\Container\SimpleContainer;
use function PHPUnit\Framework\assertInstanceOf;
use function PHPUnit\Framework\assertSame;
/**
* ContainerTest contains tests for \Yiisoft\Di\Container
*/
final class ContainerTest extends TestCase
{
public function testCanCreateWihtoutConfig(): void
{
$this->expectNotToPerformAssertions();
new Container();
}
public function testSettingScalars(): void
{
$this->expectException(InvalidConfigException::class);
$config = ContainerConfig::create()
->withDefinitions([
'scalar' => 123,
]);
$container = new Container($config);
$container->get('scalar');
}
public function testIntegerKeys(): void
{
$this->expectException(InvalidConfigException::class);
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkOne::class,
EngineMarkTwo::class,
]);
$container = new Container($config);
$container->get(Car::class);
}
public function testNullableClassDependency(): void
{
$container = new Container();
$this->expectException(NotFoundException::class);
$container->get(NullableConcreteDependency::class);
}
public function testOptionalResolvableClassDependency(): void
{
$container = new Container(
ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
]),
);
$this->assertTrue($container->has(OptionalConcreteDependency::class));
$service = $container->get(OptionalConcreteDependency::class);
$this->assertInstanceOf(Car::class, $service->getCar());
}
public function testOptionalNotResolvableClassDependency(): void
{
$container = new Container();
$this->assertTrue($container->has(OptionalConcreteDependency::class));
$service = $container->get(OptionalConcreteDependency::class);
$this->assertNull($service->getCar());
}
public function testOptionalCircularClassDependency(): void
{
$config = ContainerConfig::create()
->withDefinitions([
A::class => A::class,
B::class => B::class,
]);
$container = new Container($config);
$a = $container->get(A::class);
$this->assertInstanceOf(B::class, $a->b);
$this->assertNull($a->b->a);
}
public static function dataHas(): array
{
return [
[false, 'non_existing'],
[false, ColorInterface::class],
[true, Car::class],
[true, EngineMarkOne::class],
[true, EngineInterface::class],
[true, EngineStorage::class],
[true, Chicken::class],
[true, TreeItem::class],
];
}
#[DataProvider('dataHas')]
public function testHas(bool $expected, $id): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
]);
$container = new Container($config);
$this->assertSame($expected, $container->has($id));
}
public static function dataUnionTypes(): array
{
return [
[UnionTypeInConstructorSecondTypeInParamResolvable::class],
[UnionTypeInConstructorFirstTypeInParamResolvable::class],
];
}
#[DataProvider('dataUnionTypes')]
public function testUnionTypes(string $class): void
{
$container = new Container();
$this->assertTrue($container->has($class));
}
public function testClassExistsButIsNotResolvable(): void
{
$container = new Container();
$this->assertFalse($container->has('non_existing'));
$this->assertFalse($container->has(Car::class));
$this->assertFalse($container->has(SportCar::class));
$this->assertFalse($container->has(NullableConcreteDependency::class));
$this->assertFalse($container->has(ColorInterface::class));
}
public static function dataClassExistButIsNotResolvableWithUnionTypes(): array
{
return [
[UnionTypeInConstructorParamNotResolvable::class],
[UnionTypeInConstructorSecondParamNotResolvable::class],
];
}
#[DataProvider('dataClassExistButIsNotResolvableWithUnionTypes')]
public function testClassExistButIsNotResolvableWithUnionTypes(string $class): void
{
$container = new Container();
$this->assertFalse($container->has($class));
}
public function testWithoutDefinition(): void
{
$container = new Container();
$hasEngine = $container->has(EngineMarkOne::class);
$this->assertTrue($hasEngine);
$engine = $container->get(EngineMarkOne::class);
$this->assertInstanceOf(EngineMarkOne::class, $engine);
}
public function testCircularClassDependencyWithoutDefinition(): void
{
$container = new Container();
$this->expectException(CircularReferenceException::class);
$container->get(Chicken::class);
}
public function testTrivialDefinition(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkOne::class => EngineMarkOne::class,
]);
$container = new Container($config);
$one = $container->get(EngineMarkOne::class);
$two = $container->get(EngineMarkOne::class);
$this->assertInstanceOf(EngineMarkOne::class, $one);
$this->assertSame($one, $two);
}
public function testCircularClassDependency(): void
{
$config = ContainerConfig::create()
->withDefinitions([
Chicken::class => Chicken::class,
Egg::class => Egg::class,
]);
$container = new Container($config);
$this->expectException(CircularReferenceException::class);
$container->get(Chicken::class);
}
public function testClassSimple(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'engine' => EngineMarkOne::class,
]);
$container = new Container($config);
$this->assertInstanceOf(EngineMarkOne::class, $container->get('engine'));
}
public function testSetAll(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'engine1' => EngineMarkOne::class,
'engine2' => EngineMarkTwo::class,
]);
$container = new Container($config);
$this->assertInstanceOf(EngineMarkOne::class, $container->get('engine1'));
$this->assertInstanceOf(EngineMarkTwo::class, $container->get('engine2'));
}
public function testClassConstructor(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'constructor_test' => [
'class' => ConstructorTestClass::class,
'__construct()' => [42],
],
]);
$container = new Container($config);
/** @var ConstructorTestClass $object */
$object = $container->get('constructor_test');
$this->assertSame(42, $object->getParameter());
}
// See https://github.com/yiisoft/di/issues/157#issuecomment-701458616
public function testIntegerIndexedConstructorArguments(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'items' => [
'class' => ArrayIterator::class,
'__construct()' => [
[],
ArrayIterator::STD_PROP_LIST,
],
],
]);
$container = new Container($config);
$items = $container->get('items');
$this->assertInstanceOf(ArrayIterator::class, $items);
$this->assertSame(ArrayIterator::STD_PROP_LIST, $items->getFlags());
}
public function testExcessiveConstructorParametersIgnored(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'constructor_test' => [
'class' => ConstructorTestClass::class,
'__construct()' => [
'parameter' => 42,
'surplus1' => 43,
],
],
]);
$container = new Container($config);
/** @var ConstructorTestClass $object */
$object = $container->get('constructor_test');
$this->assertSame([42], $object->getAllParameters());
}
public function testVariadicConstructorParameters(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
'stringIndexed' => [
'class' => VariadicConstructor::class,
'__construct()' => [
'first' => 1,
'parameters' => [42, 43, 44],
],
],
'integerIndexed' => [
'class' => VariadicConstructor::class,
'__construct()' => [1, new EngineMarkOne(), 42, 43, 44],
],
]);
$container = new Container($config);
$object = $container->get('stringIndexed');
$this->assertSame(1, $object->getFirst());
$this->assertSame([42, 43, 44], $object->getParameters());
$this->assertInstanceOf(EngineMarkOne::class, $object->getEngine());
$object = $container->get('integerIndexed');
$this->assertSame(1, $object->getFirst());
$this->assertInstanceOf(EngineMarkOne::class, $object->getEngine());
$this->assertSame([42, 43, 44], $object->getParameters());
}
public function testMixedIndexedConstructorParametersAreNotAllowed(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'test' => [
'class' => VariadicConstructor::class,
'__construct()' => [
'parameters' => 42,
43,
],
],
]);
$container = new Container($config);
$this->expectException(BuildingException::class);
$this->expectExceptionMessage(
'Caught unhandled error "Arguments indexed both by name and by position are not allowed in the same array." while building "test".',
);
$container->get('test');
}
public function testClassProperties(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'property_test' => [
'class' => PropertyTestClass::class,
'$property' => 42,
],
]);
$container = new Container($config);
/** @var PropertyTestClass $object */
$object = $container->get('property_test');
$this->assertSame(42, $object->property);
}
public function testClassMethods(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'method_test' => [
'class' => MethodTestClass::class,
'setValue()' => [42],
],
]);
$container = new Container($config);
/** @var MethodTestClass $object */
$object = $container->get('method_test');
$this->assertSame(42, $object->getValue());
}
public function testClosureInConstructor(): void
{
$color = static fn() => new ColorPink();
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
ConstructorTestClass::class => [
'class' => ConstructorTestClass::class,
'__construct()' => [$color],
],
]);
$container = new Container($config);
$testClass = $container->get(ConstructorTestClass::class);
$this->assertSame($color, $testClass->getParameter());
}
public function testDynamicClosureInConstruct(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'car' => [
'class' => Car::class,
'__construct()' => [
DynamicReference::to(static fn(EngineInterface $engine) => $engine),
],
],
EngineInterface::class => EngineMarkTwo::class,
]);
$container = new Container($config);
$car = $container->get('car');
$engine = $container->get(EngineInterface::class);
$this->assertSame($engine, $car->getEngine());
}
public function testKeepClosureDefinition(): void
{
$engine = new EngineMarkOne();
$closure = static fn(EngineInterface $engine) => $engine;
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => $engine,
'closure' => DynamicReference::to($closure),
'engine' => $closure,
]);
$container = new Container($config);
$closure = $container->get('closure');
$this->assertSame($closure, $container->get('closure'));
$this->assertSame($engine, $container->get('engine'));
}
public function testClosureInProperty(): void
{
$color = static fn() => new ColorPink();
$config = ContainerConfig::create()
->withDefinitions([
PropertyTestClass::class => [
'class' => PropertyTestClass::class,
'$property' => $color,
],
]);
$container = new Container($config);
$testClass = $container->get(PropertyTestClass::class);
$this->assertSame($color, $testClass->property);
}
public function testDynamicClosureInProperty(): void
{
$color = new ColorPink();
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
ColorInterface::class => $color,
'car' => [
'class' => Car::class,
'$color' => DynamicReference::to(fn() => $color),
],
]);
$container = new Container($config);
$car = $container->get('car');
$this->assertSame($color, $car->getColor());
}
public function testClosureInMethodCall(): void
{
$color = static fn() => new ColorPink();
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
MethodTestClass::class => [
'class' => MethodTestClass::class,
'setValue()' => [$color],
],
]);
$container = new Container($config);
$testClass = $container->get(MethodTestClass::class);
$this->assertSame($color, $testClass->getValue());
}
public function testDynamicClosureInMethodCall(): void
{
$color = new ColorPink();
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
ColorInterface::class => $color,
'car' => [
'class' => Car::class,
'setColor()' => [DynamicReference::to(fn() => $color)],
],
]);
$container = new Container($config);
$car = $container->get('car');
$this->assertSame($color, $car->getColor());
}
public function testAlias(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => Reference::to('engine'),
'engine' => Reference::to('engine-mark-one'),
'engine-mark-one' => EngineMarkOne::class,
]);
$container = new Container($config);
$engine1 = $container->get('engine-mark-one');
$engine2 = $container->get('engine');
$engine3 = $container->get(EngineInterface::class);
$this->assertInstanceOf(EngineMarkOne::class, $engine1);
$this->assertSame($engine1, $engine2);
$this->assertSame($engine2, $engine3);
}
public function testCircularAlias(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'engine-1' => Reference::to('engine-2'),
'engine-2' => Reference::to('engine-3'),
'engine-3' => Reference::to('engine-1'),
]);
$container = new Container($config);
$this->expectException(CircularReferenceException::class);
$container->get('engine-1');
}
public function testUndefinedDependencies(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'car' => Car::class,
]);
$container = new Container($config);
$this->expectException(NotFoundException::class);
$container->get('car');
}
public function testDependencies(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'car' => Car::class,
EngineInterface::class => EngineMarkTwo::class,
]);
$container = new Container($config);
/** @var Car $car */
$car = $container->get('car');
$this->assertEquals(EngineMarkTwo::NAME, $car->getEngineName());
}
public function testCircularReference(): void
{
$config = ContainerConfig::create()
->withDefinitions([
TreeItem::class => TreeItem::class,
]);
$container = new Container($config);
$this->expectException(CircularReferenceException::class);
$container->get(TreeItem::class);
}
/**
* @link https://github.com/yiisoft/di/pull/189
*/
public function testFalsePositiveCircularReferenceWithClassID(): void
{
$this->expectNotToPerformAssertions();
$container = new Container();
// Build an object
$container->get(ColorPink::class);
// set definition to container
(fn(string $id, $definition) => $this->addDefinition($id, $definition))->call(
$container,
ColorPink::class,
ColorPink::class,
);
try {
// Build an object
$container->get(ColorPink::class);
} catch (CircularReferenceException) {
$this->fail('Circular reference detected false positively.');
}
}
/**
* @link https://github.com/yiisoft/di/pull/189
*/
public function testFalsePositiveCircularReferenceWithStringID(): void
{
$this->expectNotToPerformAssertions();
$container = new Container();
try {
// Build an object
$container->get('test');
} catch (NotFoundException) {
// It is expected
}
// set definition to container
(fn(string $id, $definition) => $this->addDefinition($id, $definition))->call(
$container,
'test',
ColorPink::class,
);
try {
// Build an object
$container->get('test');
} catch (CircularReferenceException) {
$this->fail('Circular reference detected false positively.');
}
}
public function testCallable(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
'test' => fn(ContainerInterface $container) => $container->get(EngineInterface::class),
]);
$container = new Container($config);
$object = $container->get('test');
$this->assertInstanceOf(EngineMarkOne::class, $object);
}
public function testCallableWithInjector(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
'car' => fn(CarFactory $factory, Injector $injector) => $injector->invoke($factory->create(...)),
]);
$container = new Container($config);
$engine = $container->get(EngineInterface::class);
$car = $container->get('car');
$this->assertInstanceOf(Car::class, $car);
$this->assertSame($engine, $car->getEngine());
}
public function testCallableWithArgs(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'engine1' => fn(EngineFactory $factory) => $factory->createByName(EngineMarkOne::NAME),
'engine2' => fn(EngineFactory $factory) => $factory->createByName(EngineMarkTwo::NAME),
]);
$container = new Container($config);
$engine1 = $container->get('engine1');
$this->assertInstanceOf(EngineMarkOne::class, $engine1);
$this->assertSame(EngineMarkOne::NUMBER, $engine1->getNumber());
$engine2 = $container->get('engine2');
$this->assertInstanceOf(EngineMarkTwo::class, $engine2);
$this->assertSame(EngineMarkTwo::NUMBER, $engine2->getNumber());
}
public function testCallableWithDependencies(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'car1' => fn(CarFactory $carFactory, EngineFactory $engineFactory) => $carFactory->createByEngineName(
$engineFactory,
EngineMarkOne::NAME,
),
'car2' => fn(CarFactory $carFactory, EngineFactory $engineFactory) => $carFactory->createByEngineName(
$engineFactory,
EngineMarkTwo::NAME,
),
]);
$container = new Container($config);
$car1 = $container->get('car1');
$this->assertInstanceOf(Car::class, $car1);
$this->assertInstanceOf(EngineMarkOne::class, $car1->getEngine());
$car2 = $container->get('car2');
$this->assertInstanceOf(Car::class, $car2);
$this->assertInstanceOf(EngineMarkTwo::class, $car2->getEngine());
}
public function testObject(): void
{
$engine = new EngineMarkOne();
$config = ContainerConfig::create()
->withDefinitions([
'engine' => $engine,
]);
$container = new Container($config);
$object = $container->get('engine');
$this->assertSame($engine, $object);
}
public function testArrayStaticCall(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
'car' => CarFactory::create(...),
]);
$container = new Container($config);
$car = $container->get('car');
$this->assertInstanceOf(Car::class, $car);
$this->assertInstanceOf(EngineMarkOne::class, $car->getEngine());
}
public function testArrayDynamicCall(): void
{
$config = ContainerConfig::create()
->withDefinitions([
ColorInterface::class => ColorPink::class,
'car' => [CarFactory::class, 'createWithColor'],
]);
$container = new Container($config);
$car = $container->get('car');
$this->assertInstanceOf(Car::class, $car);
$this->assertInstanceOf(ColorPink::class, $car->getColor());
}
public function testArrayDynamicCallWithObject(): void
{
$config = ContainerConfig::create()
->withDefinitions([
ColorInterface::class => ColorPink::class,
'car' => [new CarFactory(), 'createWithColor'],
]);
$container = new Container($config);
$car = $container->get('car');
$this->assertInstanceOf(Car::class, $car);
$this->assertInstanceOf(ColorPink::class, $car->getColor());
}
public function testInvokeable(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'engine' => EngineMarkOne::class,
'invokeable' => new InvokableCarFactory(),
]);
$container = new Container($config);
$object = $container->get('invokeable');
$this->assertInstanceOf(Car::class, $object);
}
public function testReference(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'engine' => EngineMarkOne::class,
'color' => ColorPink::class,
'car' => [
'class' => Car::class,
'__construct()' => [
Reference::to('engine'),
],
'$color' => Reference::to('color'),
],
]);
$container = new Container($config);
$object = $container->get('car');
$this->assertInstanceOf(Car::class, $object);
$this->assertInstanceOf(ColorPink::class, $object->color);
}
public function testReferencesInArrayInDependencies(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'engine1' => EngineMarkOne::class,
'engine2' => EngineMarkTwo::class,
'engine3' => EngineMarkTwo::class,
'engine4' => EngineMarkTwo::class,
'car' => [
'class' => Car::class,
'__construct()' => [
Reference::to('engine1'),
[
'engine2' => Reference::to('engine2'),
'more' => [
'engine3' => Reference::to('engine3'),
'more' => [
'engine4' => Reference::to('engine4'),
],
],
],
],
],
]);
$container = new Container($config);
$car = $container->get('car');
$this->assertInstanceOf(Car::class, $car);
$moreEngines = $car->getMoreEngines();
$this->assertSame($container->get('engine2'), $moreEngines['engine2']);
$this->assertSame($container->get('engine3'), $moreEngines['more']['engine3']);
$this->assertSame($container->get('engine4'), $moreEngines['more']['more']['engine4']);
}
public function testReferencesInProperties(): void
{
$color = new ColorPink();
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
ColorInterface::class => $color,
'car' => [
'class' => Car::class,
'$color' => Reference::to(ColorInterface::class),
],
]);
$container = new Container($config);
$car = $container->get('car');
$this->assertInstanceOf(Car::class, $car);
$this->assertSame($color, $car->getColor());
}
public function testReferencesInMethodCall(): void
{
$color = new ColorPink();
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
ColorInterface::class => $color,
'car' => [
'class' => Car::class,
'setColor()' => [Reference::to(ColorInterface::class)],
],
]);
$container = new Container($config);
$car = $container->get('car');
$this->assertInstanceOf(Car::class, $car);
$this->assertSame($color, $car->getColor());
}
public function testCallableArrayValueInConstructor(): void
{
$array = [
[EngineMarkTwo::class, 'getNumber'],
];
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
Car::class => [
'class' => Car::class,
'__construct()' => [
Reference::to(EngineInterface::class),
$array,
],
],
]);
$container = new Container($config);
/** @var Car $object */
$object = $container->get(Car::class);
$this->assertSame($array, $object->getMoreEngines());
}
public function testSameInstance(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'engine' => EngineMarkOne::class,
]);
$container = new Container($config);
$one = $container->get('engine');
$two = $container->get('engine');
$this->assertSame($one, $two);
}
public function testGetByClassIndirectly(): void
{
$number = 42;
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
EngineMarkOne::class => [
'setNumber()' => [$number],
],
]);
$container = new Container($config);
$engine = $container->get(EngineInterface::class);
$this->assertInstanceOf(EngineMarkOne::class, $engine);
$this->assertSame($number, $engine->getNumber());
}
public function testThrowingNotFoundException(): void
{
$this->expectException(NotFoundException::class);
$container = new Container();
$container->get('non_existing');
}
public function testContainerInContainer(): void
{
$config = ContainerConfig::create()
->withDefinitions([
'container' => static fn(ContainerInterface $container) => $container,
]);
$container = new Container($config);
$this->assertSame($container, $container->get('container'));
$this->assertSame($container, $container->get(ContainerInterface::class));
}
public function testTagsInArrayDefinition(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkOne::class => [
'class' => EngineMarkOne::class,
'tags' => ['engine'],
],
EngineMarkTwo::class => [
'class' => EngineMarkTwo::class,
'tags' => ['engine'],
],
]);
$container = new Container($config);
$engines = $container->get('tag@engine');
$this->assertIsArray($engines);
$this->assertSame(EngineMarkOne::class, $engines[0]::class);
$this->assertSame(EngineMarkTwo::class, $engines[1]::class);
}
public function testTagsInClosureDefinition(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkOne::class => [
'definition' => fn() => new EngineMarkOne(),
'tags' => ['engine'],
],
EngineMarkTwo::class => [
'definition' => fn() => new EngineMarkTwo(),
'tags' => ['engine'],
],
]);
$container = new Container($config);
$engines = $container->get('tag@engine');
$this->assertIsArray($engines);
$this->assertSame(EngineMarkOne::class, $engines[0]::class);
$this->assertSame(EngineMarkTwo::class, $engines[1]::class);
}
public function testTagsMultiple(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkOne::class => [
'class' => EngineMarkOne::class,
'tags' => ['engine', 'mark_one'],
],
EngineMarkTwo::class => [
'class' => EngineMarkTwo::class,
'tags' => ['engine'],
],
]);
$container = new Container($config);
$engines = $container->get('tag@engine');
$markOneEngines = $container->get('tag@mark_one');
$this->assertIsArray($engines);
$this->assertSame(EngineMarkOne::class, $engines[0]::class);
$this->assertSame(EngineMarkTwo::class, $engines[1]::class);
$this->assertIsArray($markOneEngines);
$this->assertSame(EngineMarkOne::class, $markOneEngines[0]::class);
$this->assertCount(1, $markOneEngines);
}
public function testTagsEmpty(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkOne::class => [
'class' => EngineMarkOne::class,
],
EngineMarkTwo::class => [
'class' => EngineMarkTwo::class,
],
]);
$container = new Container($config);
$engines = $container->get('tag@engine');
$this->assertIsArray($engines);
$this->assertCount(0, $engines);
}
public function testTagsWithExternalDefinition(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkOne::class => [
'class' => EngineMarkOne::class,
'tags' => ['engine'],
],
EngineMarkTwo::class => [
'class' => EngineMarkTwo::class,
],
])
->withTags(['engine' => [EngineMarkTwo::class]]);
$container = new Container($config);
$engines = $container->get('tag@engine');
$this->assertIsArray($engines);
$this->assertCount(2, $engines);
$this->assertSame(EngineMarkOne::class, $engines[1]::class);
$this->assertSame(EngineMarkTwo::class, $engines[0]::class);
}
public function testTagsWithExternalDefinitionMerge(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineMarkOne::class => [
'class' => EngineMarkOne::class,
'tags' => ['engine'],
],
EngineMarkTwo::class => [
'class' => EngineMarkTwo::class,
'tags' => ['engine'],
],
])
->withTags(['mark_two' => [EngineMarkTwo::class]]);
$container = new Container($config);
$engines = $container->get('tag@engine');
$markTwoEngines = $container->get('tag@mark_two');
$this->assertIsArray($engines);
$this->assertCount(2, $engines);
$this->assertSame(EngineMarkOne::class, $engines[0]::class);
$this->assertSame(EngineMarkTwo::class, $engines[1]::class);
$this->assertIsArray($markTwoEngines);
$this->assertCount(1, $markTwoEngines);
$this->assertSame(EngineMarkTwo::class, $markTwoEngines[0]::class);
}
public function testTagsAsArrayInConstructor(): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
EngineMarkOne::class => [
'class' => EngineMarkOne::class,
'tags' => ['engine'],
],
EngineMarkTwo::class => [
'class' => EngineMarkTwo::class,
'tags' => ['engine'],
],
Car::class => [
'__construct()' => ['moreEngines' => Reference::to('tag@engine')],
],
]);
$container = new Container($config);
$engines = $container
->get(Car::class)
->getMoreEngines();
$this->assertIsArray($engines);
$this->assertCount(2, $engines);
$this->assertSame(EngineMarkOne::class, $engines[0]::class);
$this->assertSame(EngineMarkTwo::class, $engines[1]::class);
}
public static function dataResetter(): array
{
return [
'strict-mode' => [true],
'non-strict-mode' => [false],
];
}
#[DataProvider('dataResetter')]
public function testResetter(bool $strictMode): void
{
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => EngineMarkOne::class,
EngineMarkOne::class => [
'class' => EngineMarkOne::class,
'setNumber()' => [42],
'reset' => function () {
$this->number = 42;
},
],
])
->withStrictMode($strictMode);
$container = new Container($config);
$engine = $container->get(EngineInterface::class);
$this->assertSame(
42,
$container
->get(EngineInterface::class)
->getNumber(),
);
$engine->setNumber(45);
$this->assertSame(
45,
$container
->get(EngineInterface::class)
->getNumber(),
);
$container
->get(StateResetter::class)
->reset();
$this->assertSame($engine, $container->get(EngineInterface::class));
$this->assertSame(42, $engine->getNumber());
}
public function testResetterInDelegates(): void
{
$config = ContainerConfig::create()
->withDelegates([
static function (ContainerInterface $container) {
$config = ContainerConfig::create()
->withDefinitions([
EngineInterface::class => [
'class' => EngineMarkOne::class,
'setNumber()' => [42],
'reset' => function () {
$this->number = 42;
},
],
]);
return new Container($config);
},
]);
$container = new Container($config);
$engine = $container->get(EngineInterface::class);
$this->assertSame(
42,
$container
->get(EngineInterface::class)
->getNumber(),
);
$engine->setNumber(45);
$this->assertSame(
45,
$container
->get(EngineInterface::class)
->getNumber(),
);
$container
->get(StateResetter::class)
->reset();
$this->assertSame($engine, $container->get(EngineInterface::class));
$this->assertSame(42, $engine->getNumber());
}
public function testNewContainerDefinitionInDelegates(): void
{
$firstContainer = null;
$secondContainer = null;
$config = ContainerConfig::create()
->withDefinitions([
ContainerInterface::class => new Container(),
])
->withDelegates([
function (ContainerInterface $container) use (&$firstContainer): ContainerInterface {
$firstContainer = $container;
return new Container();
},
function (ContainerInterface $container) use (&$secondContainer): ContainerInterface {
$secondContainer = $container;
return new Container();
},
]);
$originalContainer = new Container($config);
$container = $origin
gitextract_8vkzrttm/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── SECURITY.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── bc.yml
│ ├── bechmark.yml
│ ├── build.yml
│ ├── composer-require-checker.yml
│ ├── mutation.yml
│ ├── rector-cs.yml
│ └── static.yml
├── .gitignore
├── .php-cs-fixer.dist.php
├── .phpstorm.meta.php
├── .phpunit-watcher.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── benchmarks.md
├── composer.json
├── docs/
│ └── internals.md
├── infection.json.dist
├── phpbench.json
├── phpcs.xml.dist
├── phpunit.xml.dist
├── psalm.xml
├── rector.php
├── src/
│ ├── BuildingException.php
│ ├── CompositeContainer.php
│ ├── CompositeNotFoundException.php
│ ├── Container.php
│ ├── ContainerConfig.php
│ ├── ContainerConfigInterface.php
│ ├── ExtensibleService.php
│ ├── Helpers/
│ │ ├── DefinitionNormalizer.php
│ │ └── DefinitionParser.php
│ ├── NotFoundException.php
│ ├── Reference/
│ │ └── TagReference.php
│ ├── ServiceProviderInterface.php
│ └── StateResetter.php
├── tests/
│ ├── Benchmark/
│ │ ├── ContainerBench.php
│ │ └── ContainerMethodHasBench.php
│ ├── Support/
│ │ ├── A.php
│ │ ├── B.php
│ │ ├── Car.php
│ │ ├── CarExtensionProvider.php
│ │ ├── CarFactory.php
│ │ ├── CarProvider.php
│ │ ├── ColorInterface.php
│ │ ├── ColorPink.php
│ │ ├── ColorRed.php
│ │ ├── ConstructorTestClass.php
│ │ ├── ContainerInterfaceExtensionProvider.php
│ │ ├── Cycle/
│ │ │ ├── Chicken.php
│ │ │ └── Egg.php
│ │ ├── EngineFactory.php
│ │ ├── EngineInterface.php
│ │ ├── EngineMarkOne.php
│ │ ├── EngineMarkTwo.php
│ │ ├── EngineStorage.php
│ │ ├── Garage.php
│ │ ├── GearBox.php
│ │ ├── InvokableCarFactory.php
│ │ ├── MethodTestClass.php
│ │ ├── NonPsrContainer.php
│ │ ├── NullCarExtensionProvider.php
│ │ ├── NullableConcreteDependency.php
│ │ ├── OptionalConcreteDependency.php
│ │ ├── PropertyTestClass.php
│ │ ├── SportCar.php
│ │ ├── StaticFactory.php
│ │ ├── TreeItem.php
│ │ ├── UnionTypeInConstructorFirstTypeInParamResolvable.php
│ │ ├── UnionTypeInConstructorParamNotResolvable.php
│ │ ├── UnionTypeInConstructorSecondParamNotResolvable.php
│ │ ├── UnionTypeInConstructorSecondTypeInParamResolvable.php
│ │ └── VariadicConstructor.php
│ └── Unit/
│ ├── BuildingExceptionTest.php
│ ├── CompositeContainerTest.php
│ ├── CompositePsrContainerOverLeagueTest.php
│ ├── CompositePsrContainerOverYiisoftTest.php
│ ├── CompositePsrContainerTestAbstract.php
│ ├── Container/
│ │ └── DependencyFromDelegate/
│ │ ├── Car.php
│ │ ├── DependencyFromDelegateTest.php
│ │ ├── Engine.php
│ │ └── EngineInterface.php
│ ├── ContainerTest.php
│ ├── Helpers/
│ │ └── DefinitionParserTest.php
│ ├── LeaguePsrContainerTest.php
│ ├── NotFoundExceptionTest.php
│ ├── PsrContainerTestAbstract.php
│ ├── Reference/
│ │ └── TagReference/
│ │ ├── Resolve/
│ │ │ ├── A.php
│ │ │ ├── B.php
│ │ │ ├── Main.php
│ │ │ └── TagReferenceResolveTest.php
│ │ └── TagReferenceTest.php
│ ├── ServiceProviderTest.php
│ ├── StateResetterTest.php
│ └── YiisoftPsrContainerTest.php
└── tools/
├── .gitignore
├── infection/
│ └── composer.json
└── psalm/
└── composer.json
SYMBOL INDEX (387 symbols across 72 files)
FILE: src/BuildingException.php
class BuildingException (line 18) | final class BuildingException extends Exception implements ContainerExce...
method __construct (line 24) | public function __construct(
method getName (line 39) | public function getName(): string
method getSolution (line 44) | public function getSolution(): ?string
FILE: src/CompositeContainer.php
class CompositeContainer (line 19) | final class CompositeContainer implements ContainerInterface
method get (line 33) | public function get($id)
method has (line 105) | public function has($id): bool
method attach (line 145) | public function attach(ContainerInterface $container): void
method detach (line 153) | public function detach(ContainerInterface $container): void
FILE: src/CompositeNotFoundException.php
class CompositeNotFoundException (line 17) | final class CompositeNotFoundException extends Exception implements NotF...
method __construct (line 24) | public function __construct(array $exceptions)
FILE: src/Container.php
class Container (line 35) | final class Container implements ContainerInterface
method __construct (line 83) | public function __construct(?ContainerConfigInterface $config = null)
method has (line 110) | public function has(string $id): bool
method get (line 149) | public function get(string $id)
method prepareStateResetter (line 207) | private function prepareStateResetter(): StateResetter
method addDefinition (line 249) | private function addDefinition(string $id, mixed $definition): void
method addDefinitions (line 283) | private function addDefinitions(array $config): void
method setDelegates (line 308) | private function setDelegates(array $delegates): void
method validateDefinition (line 340) | private function validateDefinition(mixed $definition, ?string $id = n...
method validateMeta (line 378) | private function validateMeta(array $meta): void
method validateDefinitionTags (line 405) | private function validateDefinitionTags(mixed $tags): void
method validateDefinitionReset (line 426) | private function validateDefinitionReset(mixed $reset): void
method setTags (line 441) | private function setTags(array $tags): void
method setDefinitionTags (line 481) | private function setDefinitionTags(string $id, array $tags): void
method setDefinitionResetter (line 490) | private function setDefinitionResetter(string $id, Closure $resetter):...
method addDefinitionToStorage (line 503) | private function addDefinitionToStorage(string $id, mixed $definition)...
method build (line 525) | private function build(string $id): mixed
method getTaggedServices (line 562) | private function getTaggedServices(string $tagAlias): array
method addProviders (line 579) | private function addProviders(array $providers): void
method buildProvider (line 633) | private function buildProvider(mixed $provider): ServiceProviderInterface
FILE: src/ContainerConfig.php
class ContainerConfig (line 10) | final class ContainerConfig implements ContainerConfigInterface
method __construct (line 19) | private function __construct() {}
method create (line 21) | public static function create(): self
method withDefinitions (line 29) | public function withDefinitions(array $definitions): self
method getDefinitions (line 36) | public function getDefinitions(): array
method withProviders (line 44) | public function withProviders(array $providers): self
method getProviders (line 51) | public function getProviders(): array
method withTags (line 59) | public function withTags(array $tags): self
method getTags (line 66) | public function getTags(): array
method withValidate (line 74) | public function withValidate(bool $validate = true): self
method shouldValidate (line 81) | public function shouldValidate(): bool
method withDelegates (line 91) | public function withDelegates(array $delegates): self
method getDelegates (line 98) | public function getDelegates(): array
method withStrictMode (line 107) | public function withStrictMode(bool $useStrictMode = true): self
method useStrictMode (line 114) | public function useStrictMode(): bool
FILE: src/ContainerConfigInterface.php
type ContainerConfigInterface (line 10) | interface ContainerConfigInterface
method getDefinitions (line 15) | public function getDefinitions(): array;
method getProviders (line 20) | public function getProviders(): array;
method getTags (line 25) | public function getTags(): array;
method shouldValidate (line 30) | public function shouldValidate(): bool;
method getDelegates (line 37) | public function getDelegates(): array;
method useStrictMode (line 42) | public function useStrictMode(): bool;
FILE: src/ExtensibleService.php
class ExtensibleService (line 21) | final class ExtensibleService implements DefinitionInterface
method __construct (line 31) | public function __construct(
method addExtension (line 49) | public function addExtension(callable $closure): void
method resolve (line 54) | public function resolve(ContainerInterface $container): mixed
FILE: src/Helpers/DefinitionNormalizer.php
class DefinitionNormalizer (line 18) | final class DefinitionNormalizer
method normalize (line 26) | public static function normalize(mixed $definition, string $id): Defin...
FILE: src/Helpers/DefinitionParser.php
class DefinitionParser (line 47) | final class DefinitionParser
method parse (line 58) | public static function parse(mixed $definition): array
FILE: src/NotFoundException.php
class NotFoundException (line 17) | final class NotFoundException extends Exception implements NotFoundExcep...
method __construct (line 23) | public function __construct(
method getId (line 43) | public function getId(): string
method getBuildStack (line 51) | public function getBuildStack(): array
method getName (line 56) | public function getName(): string
method getSolution (line 61) | public function getSolution(): ?string
FILE: src/Reference/TagReference.php
class TagReference (line 16) | final class TagReference
method __construct (line 20) | private function __construct() {}
method to (line 22) | public static function to(string $tag): Reference
method id (line 27) | public static function id(string $tag): string
method extractTagFromAlias (line 32) | public static function extractTagFromAlias(string $alias): string
method isTagAlias (line 40) | public static function isTagAlias(string $id): bool
FILE: src/ServiceProviderInterface.php
type ServiceProviderInterface (line 32) | interface ServiceProviderInterface
method getDefinitions (line 45) | public function getDefinitions(): array;
method getExtensions (line 61) | public function getExtensions(): array;
FILE: src/StateResetter.php
class StateResetter (line 20) | final class StateResetter
method __construct (line 30) | public function __construct(
method reset (line 37) | public function reset(): void
method setResetters (line 53) | public function setResetters(array $resetters): void
FILE: tests/Benchmark/ContainerBench.php
class ContainerBench (line 30) | class ContainerBench
method provideDefinitions (line 42) | public function provideDefinitions(): array
method before (line 67) | public function before(): void
method benchConstruct (line 138) | public function benchConstruct(): void
method benchSequentialLookups (line 154) | public function benchSequentialLookups($params): void
method benchRandomLookups (line 178) | public function benchRandomLookups($params): void
method benchRandomLookupsComposite (line 202) | public function benchRandomLookupsComposite($params): void
FILE: tests/Benchmark/ContainerMethodHasBench.php
class ContainerMethodHasBench (line 23) | class ContainerMethodHasBench
method before (line 32) | public function before(): void
method benchPredefinedExisting (line 46) | public function benchPredefinedExisting(): void
method benchUndefinedExisting (line 53) | public function benchUndefinedExisting(): void
method benchUndefinedNonexistent (line 60) | public function benchUndefinedNonexistent(): void
FILE: tests/Support/A.php
class A (line 7) | class A
method __construct (line 9) | public function __construct(public ?B $b = null) {}
FILE: tests/Support/B.php
class B (line 7) | class B
method __construct (line 9) | public function __construct(public ?A $a = null) {}
FILE: tests/Support/Car.php
class Car (line 10) | class Car
method __construct (line 14) | public function __construct(
method setColor (line 19) | public function setColor(ColorInterface $color): self
method getColor (line 26) | public function getColor(): ColorInterface
method getEngine (line 31) | public function getEngine(): EngineInterface
method getEngineName (line 36) | public function getEngineName(): string
method getMoreEngines (line 41) | public function getMoreEngines(): array
FILE: tests/Support/CarExtensionProvider.php
class CarExtensionProvider (line 10) | final class CarExtensionProvider implements ServiceProviderInterface
method getDefinitions (line 12) | public function getDefinitions(): array
method getExtensions (line 17) | public function getExtensions(): array
FILE: tests/Support/CarFactory.php
class CarFactory (line 10) | class CarFactory
method create (line 12) | public static function create(EngineInterface $engine): Car
method createByEngineName (line 17) | public function createByEngineName(EngineFactory $factory, $name): Car
method createWithColor (line 22) | public function createWithColor(ColorInterface $color): Car
FILE: tests/Support/CarProvider.php
class CarProvider (line 10) | final class CarProvider implements ServiceProviderInterface
method getDefinitions (line 12) | public function getDefinitions(): array
method getExtensions (line 20) | public function getExtensions(): array
FILE: tests/Support/ColorInterface.php
type ColorInterface (line 10) | interface ColorInterface
method getColor (line 12) | public function getColor(): string;
FILE: tests/Support/ColorPink.php
class ColorPink (line 10) | final class ColorPink implements ColorInterface
method getColor (line 14) | public function getColor(): string
FILE: tests/Support/ColorRed.php
class ColorRed (line 11) | final class ColorRed implements ColorInterface
method getColor (line 15) | public function getColor(): string
FILE: tests/Support/ConstructorTestClass.php
class ConstructorTestClass (line 12) | class ConstructorTestClass
method __construct (line 21) | public function __construct(private $parameter)
method getParameter (line 29) | public function getParameter()
method getAllParameters (line 34) | public function getAllParameters(): array
FILE: tests/Support/ContainerInterfaceExtensionProvider.php
class ContainerInterfaceExtensionProvider (line 10) | final class ContainerInterfaceExtensionProvider implements ServiceProvid...
method getDefinitions (line 12) | public function getDefinitions(): array
method getExtensions (line 17) | public function getExtensions(): array
FILE: tests/Support/Cycle/Chicken.php
class Chicken (line 7) | class Chicken
method __construct (line 9) | public function __construct(Egg $egg) {}
FILE: tests/Support/Cycle/Egg.php
class Egg (line 7) | class Egg
method __construct (line 9) | public function __construct(Chicken $chicken) {}
FILE: tests/Support/EngineFactory.php
class EngineFactory (line 13) | class EngineFactory
method __construct (line 15) | public function __construct(private readonly ContainerInterface $conta...
method createByName (line 17) | public function createByName(?string $name = null): EngineInterface
method createDefault (line 29) | public static function createDefault(): EngineInterface
FILE: tests/Support/EngineInterface.php
type EngineInterface (line 10) | interface EngineInterface
method getName (line 12) | public function getName(): string;
method setNumber (line 14) | public function setNumber(int $value): void;
method getNumber (line 16) | public function getNumber(): int;
FILE: tests/Support/EngineMarkOne.php
class EngineMarkOne (line 10) | class EngineMarkOne implements EngineInterface
method __construct (line 15) | public function __construct(private int $number = self::NUMBER) {}
method getName (line 17) | public function getName(): string
method setNumber (line 22) | public function setNumber(int $value): void
method getNumber (line 27) | public function getNumber(): int
FILE: tests/Support/EngineMarkTwo.php
class EngineMarkTwo (line 10) | class EngineMarkTwo implements EngineInterface
method __construct (line 15) | public function __construct(private int $number = self::NUMBER) {}
method getName (line 17) | public function getName(): string
method setNumber (line 22) | public function setNumber(int $value): void
method getNumber (line 27) | public function getNumber(): int
FILE: tests/Support/EngineStorage.php
class EngineStorage (line 7) | final class EngineStorage
method __construct (line 11) | public function __construct(EngineInterface ...$engines)
method getEngines (line 16) | public function getEngines(): array
FILE: tests/Support/Garage.php
class Garage (line 10) | final class Garage
method __construct (line 12) | public function __construct(private readonly SportCar $car) {}
method getCar (line 14) | public function getCar(): SportCar
FILE: tests/Support/GearBox.php
class GearBox (line 10) | class GearBox
method __construct (line 12) | public function __construct(private readonly int $maxGear = 5) {}
FILE: tests/Support/InvokableCarFactory.php
class InvokableCarFactory (line 9) | class InvokableCarFactory
method __invoke (line 11) | public function __invoke(ContainerInterface $container): Car
FILE: tests/Support/MethodTestClass.php
class MethodTestClass (line 10) | class MethodTestClass
method getValue (line 17) | public function getValue()
method setValue (line 22) | public function setValue(mixed $value): void
FILE: tests/Support/NonPsrContainer.php
class NonPsrContainer (line 10) | final class NonPsrContainer implements ContainerInterface
method get (line 12) | public function get(string $id)
method has (line 17) | public function has(string $id): bool
FILE: tests/Support/NullCarExtensionProvider.php
class NullCarExtensionProvider (line 10) | final class NullCarExtensionProvider implements ServiceProviderInterface
method getDefinitions (line 12) | public function getDefinitions(): array
method getExtensions (line 18) | public function getExtensions(): array
FILE: tests/Support/NullableConcreteDependency.php
class NullableConcreteDependency (line 7) | class NullableConcreteDependency
method __construct (line 9) | public function __construct(?Car $car) {}
FILE: tests/Support/OptionalConcreteDependency.php
class OptionalConcreteDependency (line 7) | class OptionalConcreteDependency
method __construct (line 9) | public function __construct(private readonly ?Car $car = null) {}
method getCar (line 11) | public function getCar(): ?Car
FILE: tests/Support/PropertyTestClass.php
class PropertyTestClass (line 10) | class PropertyTestClass
FILE: tests/Support/SportCar.php
class SportCar (line 10) | class SportCar
method __construct (line 14) | public function __construct(
method setColor (line 19) | public function setColor(ColorInterface $color): self
method getColor (line 26) | public function getColor(): ColorInterface
method getEngine (line 31) | public function getEngine(): EngineInterface
method getEngineName (line 36) | public function getEngineName(): string
method getMaxSpeed (line 41) | public function getMaxSpeed(): int
FILE: tests/Support/StaticFactory.php
class StaticFactory (line 9) | final class StaticFactory
method create (line 11) | public static function create(): stdClass
FILE: tests/Support/TreeItem.php
class TreeItem (line 10) | class TreeItem
method __construct (line 12) | public function __construct(private readonly self $treeItem) {}
FILE: tests/Support/UnionTypeInConstructorFirstTypeInParamResolvable.php
class UnionTypeInConstructorFirstTypeInParamResolvable (line 7) | final class UnionTypeInConstructorFirstTypeInParamResolvable
method __construct (line 9) | public function __construct(private readonly EngineMarkOne|EngineInter...
FILE: tests/Support/UnionTypeInConstructorParamNotResolvable.php
class UnionTypeInConstructorParamNotResolvable (line 7) | final class UnionTypeInConstructorParamNotResolvable
method __construct (line 9) | public function __construct(private readonly EngineInterface|ColorInte...
FILE: tests/Support/UnionTypeInConstructorSecondParamNotResolvable.php
class UnionTypeInConstructorSecondParamNotResolvable (line 7) | final class UnionTypeInConstructorSecondParamNotResolvable
method __construct (line 9) | public function __construct(EngineMarkOne|EngineInterface $engine, str...
FILE: tests/Support/UnionTypeInConstructorSecondTypeInParamResolvable.php
class UnionTypeInConstructorSecondTypeInParamResolvable (line 7) | final class UnionTypeInConstructorSecondTypeInParamResolvable
method __construct (line 9) | public function __construct(private readonly EngineInterface|EngineMar...
FILE: tests/Support/VariadicConstructor.php
class VariadicConstructor (line 7) | final class VariadicConstructor
method __construct (line 11) | public function __construct(
method getFirst (line 19) | public function getFirst()
method getEngine (line 24) | public function getEngine(): EngineInterface
method getParameters (line 29) | public function getParameters(): array
FILE: tests/Unit/BuildingExceptionTest.php
class BuildingExceptionTest (line 11) | final class BuildingExceptionTest extends TestCase
method testMessage (line 13) | public function testMessage(): void
method testEmptyMessage (line 29) | public function testEmptyMessage(): void
method testBuildStack (line 36) | public function testBuildStack(): void
method testCode (line 43) | public function testCode(): void
FILE: tests/Unit/CompositeContainerTest.php
class CompositeContainerTest (line 22) | final class CompositeContainerTest extends TestCase
method testGetNonString (line 24) | public function testGetNonString(): void
method testTagsWithYiiAndNotYiiContainers (line 35) | public function testTagsWithYiiAndNotYiiContainers(): void
method testNonPsrContainer (line 65) | public function testNonPsrContainer(): void
method testHasNoString (line 81) | public function testHasNoString(): void
method testHasTag (line 90) | #[TestWith([true, 'engine'])]
method testHasTagWithoutYiiContainer (line 105) | public function testHasTagWithoutYiiContainer(): void
FILE: tests/Unit/CompositePsrContainerOverLeagueTest.php
class CompositePsrContainerOverLeagueTest (line 15) | final class CompositePsrContainerOverLeagueTest extends CompositePsrCont...
method createContainer (line 17) | public function createContainer(iterable $definitions = []): Container...
method setupContainer (line 23) | public function setupContainer(ContainerInterface $container, iterable...
method testNotFoundException (line 32) | public function testNotFoundException(): void
FILE: tests/Unit/CompositePsrContainerOverYiisoftTest.php
class CompositePsrContainerOverYiisoftTest (line 19) | final class CompositePsrContainerOverYiisoftTest extends CompositePsrCon...
method createContainer (line 21) | public function createContainer(iterable $definitions = []): Container...
method testResetterInCompositeContainerWithExternalResetter (line 29) | public function testResetterInCompositeContainerWithExternalResetter()...
method testNotFoundException (line 109) | public function testNotFoundException(): void
FILE: tests/Unit/CompositePsrContainerTestAbstract.php
class CompositePsrContainerTestAbstract (line 22) | abstract class CompositePsrContainerTestAbstract extends PsrContainerTes...
method createCompositeContainer (line 24) | public function createCompositeContainer(ContainerInterface $attachedC...
method testAttach (line 32) | public function testAttach(): void
method testDetach (line 46) | public function testDetach(): void
method testHasDefinition (line 63) | public function testHasDefinition(): void
method testGetPriority (line 77) | public function testGetPriority(): void
method testTags (line 103) | public function testTags(): void
method testDelegateLookup (line 136) | public function testDelegateLookup(): void
method testDelegateLookupUnionTypes (line 155) | public function testDelegateLookupUnionTypes(): void
FILE: tests/Unit/Container/DependencyFromDelegate/Car.php
class Car (line 7) | final class Car
method __construct (line 9) | public function __construct(
FILE: tests/Unit/Container/DependencyFromDelegate/DependencyFromDelegateTest.php
class DependencyFromDelegateTest (line 20) | final class DependencyFromDelegateTest extends TestCase
method testAnotherContainer (line 22) | public function testAnotherContainer(): void
method testNotFoundInDelegate (line 43) | public function testNotFoundInDelegate(): void
FILE: tests/Unit/Container/DependencyFromDelegate/Engine.php
class Engine (line 7) | final class Engine implements EngineInterface {}
FILE: tests/Unit/Container/DependencyFromDelegate/EngineInterface.php
type EngineInterface (line 7) | interface EngineInterface {}
FILE: tests/Unit/ContainerTest.php
class ContainerTest (line 64) | final class ContainerTest extends TestCase
method testCanCreateWihtoutConfig (line 66) | public function testCanCreateWihtoutConfig(): void
method testSettingScalars (line 73) | public function testSettingScalars(): void
method testIntegerKeys (line 86) | public function testIntegerKeys(): void
method testNullableClassDependency (line 100) | public function testNullableClassDependency(): void
method testOptionalResolvableClassDependency (line 108) | public function testOptionalResolvableClassDependency(): void
method testOptionalNotResolvableClassDependency (line 122) | public function testOptionalNotResolvableClassDependency(): void
method testOptionalCircularClassDependency (line 131) | public function testOptionalCircularClassDependency(): void
method dataHas (line 144) | public static function dataHas(): array
method testHas (line 158) | #[DataProvider('dataHas')]
method dataUnionTypes (line 170) | public static function dataUnionTypes(): array
method testUnionTypes (line 178) | #[DataProvider('dataUnionTypes')]
method testClassExistsButIsNotResolvable (line 186) | public function testClassExistsButIsNotResolvable(): void
method dataClassExistButIsNotResolvableWithUnionTypes (line 197) | public static function dataClassExistButIsNotResolvableWithUnionTypes(...
method testClassExistButIsNotResolvableWithUnionTypes (line 205) | #[DataProvider('dataClassExistButIsNotResolvableWithUnionTypes')]
method testWithoutDefinition (line 213) | public function testWithoutDefinition(): void
method testCircularClassDependencyWithoutDefinition (line 224) | public function testCircularClassDependencyWithoutDefinition(): void
method testTrivialDefinition (line 231) | public function testTrivialDefinition(): void
method testCircularClassDependency (line 245) | public function testCircularClassDependency(): void
method testClassSimple (line 258) | public function testClassSimple(): void
method testSetAll (line 268) | public function testSetAll(): void
method testClassConstructor (line 281) | public function testClassConstructor(): void
method testIntegerIndexedConstructorArguments (line 298) | public function testIntegerIndexedConstructorArguments(): void
method testExcessiveConstructorParametersIgnored (line 318) | public function testExcessiveConstructorParametersIgnored(): void
method testVariadicConstructorParameters (line 337) | public function testVariadicConstructorParameters(): void
method testMixedIndexedConstructorParametersAreNotAllowed (line 367) | public function testMixedIndexedConstructorParametersAreNotAllowed(): ...
method testClassProperties (line 388) | public function testClassProperties(): void
method testClassMethods (line 404) | public function testClassMethods(): void
method testClosureInConstructor (line 420) | public function testClosureInConstructor(): void
method testDynamicClosureInConstruct (line 438) | public function testDynamicClosureInConstruct(): void
method testKeepClosureDefinition (line 457) | public function testKeepClosureDefinition(): void
method testClosureInProperty (line 475) | public function testClosureInProperty(): void
method testDynamicClosureInProperty (line 492) | public function testDynamicClosureInProperty(): void
method testClosureInMethodCall (line 511) | public function testClosureInMethodCall(): void
method testDynamicClosureInMethodCall (line 529) | public function testDynamicClosureInMethodCall(): void
method testAlias (line 548) | public function testAlias(): void
method testCircularAlias (line 566) | public function testCircularAlias(): void
method testUndefinedDependencies (line 580) | public function testUndefinedDependencies(): void
method testDependencies (line 592) | public function testDependencies(): void
method testCircularReference (line 606) | public function testCircularReference(): void
method testFalsePositiveCircularReferenceWithClassID (line 621) | public function testFalsePositiveCircularReferenceWithClassID(): void
method testFalsePositiveCircularReferenceWithStringID (line 648) | public function testFalsePositiveCircularReferenceWithStringID(): void
method testCallable (line 675) | public function testCallable(): void
method testCallableWithInjector (line 688) | public function testCallableWithInjector(): void
method testCallableWithArgs (line 703) | public function testCallableWithArgs(): void
method testCallableWithDependencies (line 719) | public function testCallableWithDependencies(): void
method testObject (line 741) | public function testObject(): void
method testArrayStaticCall (line 755) | public function testArrayStaticCall(): void
method testArrayDynamicCall (line 769) | public function testArrayDynamicCall(): void
method testArrayDynamicCallWithObject (line 783) | public function testArrayDynamicCallWithObject(): void
method testInvokeable (line 797) | public function testInvokeable(): void
method testReference (line 810) | public function testReference(): void
method testReferencesInArrayInDependencies (line 830) | public function testReferencesInArrayInDependencies(): void
method testReferencesInProperties (line 863) | public function testReferencesInProperties(): void
method testReferencesInMethodCall (line 883) | public function testReferencesInMethodCall(): void
method testCallableArrayValueInConstructor (line 902) | public function testCallableArrayValueInConstructor(): void
method testSameInstance (line 926) | public function testSameInstance(): void
method testGetByClassIndirectly (line 939) | public function testGetByClassIndirectly(): void
method testThrowingNotFoundException (line 957) | public function testThrowingNotFoundException(): void
method testContainerInContainer (line 965) | public function testContainerInContainer(): void
method testTagsInArrayDefinition (line 977) | public function testTagsInArrayDefinition(): void
method testTagsInClosureDefinition (line 999) | public function testTagsInClosureDefinition(): void
method testTagsMultiple (line 1021) | public function testTagsMultiple(): void
method testTagsEmpty (line 1047) | public function testTagsEmpty(): void
method testTagsWithExternalDefinition (line 1066) | public function testTagsWithExternalDefinition(): void
method testTagsWithExternalDefinitionMerge (line 1089) | public function testTagsWithExternalDefinitionMerge(): void
method testTagsAsArrayInConstructor (line 1117) | public function testTagsAsArrayInConstructor(): void
method dataResetter (line 1146) | public static function dataResetter(): array
method testResetter (line 1154) | #[DataProvider('dataResetter')]
method testResetterInDelegates (line 1195) | public function testResetterInDelegates(): void
method testNewContainerDefinitionInDelegates (line 1239) | public function testNewContainerDefinitionInDelegates(): void
method testResetterInDelegatesWithCustomResetter (line 1267) | public function testResetterInDelegatesWithCustomResetter(): void
method dataResetterInProviderDefinitions (line 1351) | public static function dataResetterInProviderDefinitions(): array
method testResetterInProviderDefinitions (line 1359) | #[DataProvider('dataResetterInProviderDefinitions')]
method testResetterInProviderExtensions (line 1405) | public function testResetterInProviderExtensions(): void
method testNestedResetter (line 1451) | public function testNestedResetter(): void
method testResetterInCompositeContainer (line 1513) | public function testResetterInCompositeContainer(): void
method testCircularReferenceExceptionWhileResolvingProviders (line 1593) | public function testCircularReferenceExceptionWhileResolvingProviders(...
method testDifferentContainerWithProviders (line 1626) | public function testDifferentContainerWithProviders(): void
method testErrorOnMethodTypo (line 1651) | public function testErrorOnMethodTypo(): void
method testErrorOnPropertyTypo (line 1668) | public function testErrorOnPropertyTypo(): void
method testErrorOnDisallowMeta (line 1685) | public function testErrorOnDisallowMeta(): void
method testDelegateLookup (line 1703) | public function testDelegateLookup(): void
method testNonClosureDelegate (line 1734) | public function testNonClosureDelegate(): void
method testNonContainerDelegate (line 1746) | public function testNonContainerDelegate(): void
method testExtensibleServiceDefinition (line 1760) | public function testExtensibleServiceDefinition(): void
method testWrongTag (line 1774) | public function testWrongTag(): void
method testNumberProvider (line 1790) | public function testNumberProvider(): void
method testNonServiceProviderInterfaceProvider (line 1804) | public function testNonServiceProviderInterfaceProvider(): void
method testStrictModeDisabled (line 1817) | public function testStrictModeDisabled(): void
method testStrictModeEnabled (line 1828) | public function testStrictModeEnabled(): void
method testIntegerKeyInExtensions (line 1839) | public function testIntegerKeyInExtensions(): void
method testNonCallableExtension (line 1863) | public function testNonCallableExtension(): void
method testNonArrayReset (line 1887) | public function testNonArrayReset(): void
method testNonArrayTags (line 1905) | public function testNonArrayTags(): void
method testNonArrayArguments (line 1923) | public function testNonArrayArguments(): void
method dataInvalidTags (line 1940) | public static function dataInvalidTags(): array
method testInvalidTags (line 1958) | #[DataProvider('dataInvalidTags')]
method dataNotFoundExceptionMessageWithDefinitions (line 1969) | public static function dataNotFoundExceptionMessageWithDefinitions(): ...
method testNotFoundExceptionMessageWithDefinitions (line 1977) | #[DataProvider('dataNotFoundExceptionMessageWithDefinitions')]
method testNotFoundExceptionWithNotYiiContainer (line 1996) | public function testNotFoundExceptionWithNotYiiContainer(): void
method testExceptionOnGetInDelegate (line 2023) | public function testExceptionOnGetInDelegate(): void
method testExceptionOnHasInDelegate (line 2050) | public function testExceptionOnHasInDelegate(): void
method testGetStateResetterTwice (line 2077) | public function testGetStateResetterTwice(): void
FILE: tests/Unit/Helpers/DefinitionParserTest.php
class DefinitionParserTest (line 12) | final class DefinitionParserTest extends TestCase
method testParseCallableDefinition (line 14) | public function testParseCallableDefinition(): void
method testParseArrayCallableDefinition (line 26) | public function testParseArrayCallableDefinition(): void
method testParseArrayDefinition (line 37) | public function testParseArrayDefinition(): void
FILE: tests/Unit/LeaguePsrContainerTest.php
class LeaguePsrContainerTest (line 13) | final class LeaguePsrContainerTest extends PsrContainerTestAbstract
method createContainer (line 15) | public function createContainer(array $definitions = []): ContainerInt...
method setupContainer (line 20) | public function setupContainer(ContainerInterface $container, iterable...
FILE: tests/Unit/NotFoundExceptionTest.php
class NotFoundExceptionTest (line 10) | final class NotFoundExceptionTest extends TestCase
method testGetId (line 12) | public function testGetId(): void
method testMessage (line 26) | public function testMessage(): void
method testBuildStack (line 33) | public function testBuildStack(): void
method testCode (line 43) | public function testCode(): void
FILE: tests/Unit/PsrContainerTestAbstract.php
class PsrContainerTestAbstract (line 20) | abstract class PsrContainerTestAbstract extends TestCase
method createContainer (line 22) | abstract public function createContainer(array $definitions = []): Con...
method testCircularClassDependencyWithoutDefinition (line 24) | public function testCircularClassDependencyWithoutDefinition(): void
method testSimpleDefinition (line 31) | public function testSimpleDefinition(): void
method testClassSimple (line 40) | public function testClassSimple(): void
method testSetAll (line 46) | public function testSetAll(): void
method testObject (line 56) | public function testObject(): void
method testThrowingNotFoundException (line 65) | public function testThrowingNotFoundException(): void
FILE: tests/Unit/Reference/TagReference/Resolve/A.php
class A (line 7) | final class A {}
FILE: tests/Unit/Reference/TagReference/Resolve/B.php
class B (line 7) | final class B {}
FILE: tests/Unit/Reference/TagReference/Resolve/Main.php
class Main (line 7) | final class Main
FILE: tests/Unit/Reference/TagReference/Resolve/TagReferenceResolveTest.php
class TagReferenceResolveTest (line 12) | final class TagReferenceResolveTest extends TestCase
method testBase (line 14) | public function testBase(): void
FILE: tests/Unit/Reference/TagReference/TagReferenceTest.php
class TagReferenceTest (line 14) | final class TagReferenceTest extends TestCase
method testConstructorIsPrivate (line 16) | public function testConstructorIsPrivate(): void
method testConstructor (line 22) | public function testConstructor(): void
method testAliases (line 30) | public function testAliases(): void
method testExtractTag (line 37) | public function testExtractTag(): void
method testExtractWrongTagDelimiter (line 42) | public function testExtractWrongTagDelimiter(): void
method testExtractWrongTagFormat (line 48) | public function testExtractWrongTagFormat(): void
method testReference (line 54) | public function testReference(): void
method testId (line 74) | public function testId(): void
FILE: tests/Unit/ServiceProviderTest.php
class ServiceProviderTest (line 25) | final class ServiceProviderTest extends TestCase
method testAddProviderByClassName (line 27) | public function testAddProviderByClassName(): void
method testAddProviderByInstance (line 33) | public function testAddProviderByInstance(): void
method testNotExistedExtension (line 39) | public function testNotExistedExtension(): void
method testContainerInterfaceExtension (line 49) | public function testContainerInterfaceExtension(): void
method testExtensionOverride (line 59) | public function testExtensionOverride(): void
method testExtensionReturnedNull (line 80) | public function testExtensionReturnedNull(): void
method testClassMethodsWithExtensible (line 102) | public function testClassMethodsWithExtensible(): void
method ensureProviderRegisterExtensions (line 134) | private function ensureProviderRegisterExtensions($provider): void
method ensureProviderRegisterDefinitions (line 162) | private function ensureProviderRegisterDefinitions($provider): void
FILE: tests/Unit/StateResetterTest.php
class StateResetterTest (line 14) | final class StateResetterTest extends TestCase
method testNonStateResetterObject (line 16) | public function testNonStateResetterObject(): void
method testStateResetterObjectForService (line 29) | public function testStateResetterObjectForService(): void
method testResetNonObject (line 44) | public function testResetNonObject(): void
FILE: tests/Unit/YiisoftPsrContainerTest.php
class YiisoftPsrContainerTest (line 14) | final class YiisoftPsrContainerTest extends PsrContainerTestAbstract
method createContainer (line 16) | public function createContainer(iterable $definitions = []): Container...
Condensed preview — 105 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (249K chars).
[
{
"path": ".editorconfig",
"chars": 336,
"preview": "# editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\ni"
},
{
"path": ".gitattributes",
"chars": 836,
"preview": "# Autodetect text files\n* text=auto eol=lf\n\n# ...Unless the name matches the following overriding patterns\n\n# Definitive"
},
{
"path": ".github/CODE_OF_CONDUCT.md",
"chars": 5104,
"preview": "# Yii Contributor Code of Conduct\n\n## Our Pledge\n\nAs contributors and maintainers of this project, and in order to keep "
},
{
"path": ".github/CONTRIBUTING.md",
"chars": 1087,
"preview": "# Prerequisites\n\n- [Yii goal and values](https://github.com/yiisoft/docs/blob/master/001-yii-values.md)\n- [Namespaces](h"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 187,
"preview": "| Q | A\n| ------------- | ---\n| Is bugfix? | ✔️/❌\n| New feature? | ✔️/❌\n| Breaks BC? | ✔️/❌\n| Fixed i"
},
{
"path": ".github/SECURITY.md",
"chars": 354,
"preview": "# Security Policy\n\nPlease use the [security issue form](https://www.yiiframework.com/security) to report to us any secur"
},
{
"path": ".github/dependabot.yml",
"chars": 513,
"preview": "version: 2\nupdates:\n # Maintain dependencies for GitHub Actions.\n - package-ecosystem: \"github-actions\"\n dire"
},
{
"path": ".github/workflows/bc.yml",
"chars": 650,
"preview": "on:\n pull_request:\n paths-ignore:\n - 'docs/**'\n - 'README.md'\n - 'CHANGELOG.md'\n - '.gitignore'\n"
},
{
"path": ".github/workflows/bechmark.yml",
"chars": 576,
"preview": "on:\n pull_request:\n paths-ignore:\n - 'docs/**'\n - 'README.md'\n - 'CHANGELOG.md'\n - '.gitignore'\n"
},
{
"path": ".github/workflows/build.yml",
"chars": 685,
"preview": "on:\n pull_request:\n paths-ignore:\n - 'docs/**'\n - 'README.md'\n - 'CHANGELOG.md'\n - '.gitignore'\n"
},
{
"path": ".github/workflows/composer-require-checker.yml",
"chars": 712,
"preview": "on:\n pull_request:\n paths-ignore:\n - 'docs/**'\n - 'README.md'\n - 'CHANGELOG.md'\n - '.gitignore'\n"
},
{
"path": ".github/workflows/mutation.yml",
"chars": 670,
"preview": "on:\n pull_request:\n paths-ignore:\n - 'docs/**'\n - 'README.md'\n - 'CHANGELOG.md'\n - '.gitignore'\n"
},
{
"path": ".github/workflows/rector-cs.yml",
"chars": 587,
"preview": "name: Rector + PHP CS Fixer\n\non:\n pull_request_target:\n paths:\n - 'src/**'\n - 'tests/**'\n - '.github/"
},
{
"path": ".github/workflows/static.yml",
"chars": 618,
"preview": "on:\n pull_request:\n paths-ignore:\n - 'docs/**'\n - 'README.md'\n - 'CHANGELOG.md'\n - '.gitignore'\n"
},
{
"path": ".gitignore",
"chars": 475,
"preview": "# phpstorm project files\n.idea\n\n# netbeans project files\nnbproject\n\n# zend studio for eclipse project files\n.buildpath\n."
},
{
"path": ".php-cs-fixer.dist.php",
"chars": 952,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nuse PhpCsFixer\\Config;\nuse PhpCsFixer\\Finder;\nuse PhpCsFixer\\Runner\\Parallel\\ParallelCo"
},
{
"path": ".phpstorm.meta.php",
"chars": 178,
"preview": "<?php\n// .phpstorm.meta.php\n\nnamespace PHPSTORM_META {\n\n override(\n \\Psr\\Container\\ContainerInterface::get(0),"
},
{
"path": ".phpunit-watcher.yml",
"chars": 200,
"preview": "watch:\n directories:\n - src\n - tests\n fileMask: '*.php'\nnotifications:\n passingTests: false\n f"
},
{
"path": "CHANGELOG.md",
"chars": 1920,
"preview": "# Yii Dependency Injection Change Log\n\n## 1.4.2 under development\n\n- Enh #397: Explicitly import functions in \"use\" sect"
},
{
"path": "LICENSE.md",
"chars": 1524,
"preview": "Copyright © 2008 by Yii Software (<https://www.yiiframework.com/>)\nAll rights reserved.\n\nRedistribution and use in sourc"
},
{
"path": "README.md",
"chars": 21315,
"preview": "<p align=\"center\">\n <a href=\"https://github.com/yiisoft\" target=\"_blank\">\n <img src=\"https://yiisoft.github.io"
},
{
"path": "benchmarks.md",
"chars": 1597,
"preview": "DI benchmark report\n===================\n\n### suite: 1343bd6191c6668ccf8fb4cf1a51a7b61159c825, date: 2020-04-06, stime: 2"
},
{
"path": "composer.json",
"chars": 2508,
"preview": "{\n \"name\": \"yiisoft/di\",\n \"type\": \"library\",\n \"description\": \"Yii DI container\",\n \"keywords\": [\n \"di\""
},
{
"path": "docs/internals.md",
"chars": 8711,
"preview": "# Internals\n\n## Further reading\n\n- [Martin Fowler's article](https://martinfowler.com/articles/injection.html).\n\n## Benc"
},
{
"path": "infection.json.dist",
"chars": 246,
"preview": "{\n \"source\": {\n \"directories\": [\n \"src\"\n ]\n },\n \"logs\": {\n \"text\": \"php:\\/\\/std"
},
{
"path": "phpbench.json",
"chars": 287,
"preview": "{\n \"runner.bootstrap\": \"vendor/autoload.php\",\n \"runner.path\": \"tests/Benchmark\",\n \"runner.retry_threshold\": 3,\n"
},
{
"path": "phpcs.xml.dist",
"chars": 136,
"preview": "<?xml version=\"1.0\"?>\n<ruleset>\n <file>./</file>\n <exclude-pattern>./vendor/*</exclude-pattern>\n <rule ref=\"PSR"
},
{
"path": "phpunit.xml.dist",
"chars": 949,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noNam"
},
{
"path": "psalm.xml",
"chars": 600,
"preview": "<?xml version=\"1.0\"?>\n<psalm\n errorLevel=\"1\"\n findUnusedBaselineEntry=\"true\"\n findUnusedCode=\"false\"\n ensure"
},
{
"path": "rector.php",
"chars": 699,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Rector\\CodeQuality\\Rector\\Class_\\InlineConstructorDefaultToPropertyRector;\nuse Rect"
},
{
"path": "src/BuildingException.php",
"chars": 1664,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di;\n\nuse Exception;\nuse Psr\\Container\\ContainerExceptionInterface;\nus"
},
{
"path": "src/CompositeContainer.php",
"chars": 4601,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di;\n\nuse InvalidArgumentException;\nuse Psr\\Container\\ContainerInterfa"
},
{
"path": "src/CompositeNotFoundException.php",
"chars": 1212,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di;\n\nuse Exception;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Con"
},
{
"path": "src/Container.php",
"chars": 22266,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di;\n\nuse Closure;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse "
},
{
"path": "src/ContainerConfig.php",
"chars": 2796,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di;\n\n/**\n * Container configuration.\n */\nfinal class ContainerConfig "
},
{
"path": "src/ContainerConfigInterface.php",
"chars": 1144,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di;\n\n/**\n * Container configuration.\n */\ninterface ContainerConfigInt"
},
{
"path": "src/ExtensibleService.php",
"chars": 1785,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di;\n\nuse Psr\\Container\\ContainerInterface;\nuse Yiisoft\\Definitions\\Co"
},
{
"path": "src/Helpers/DefinitionNormalizer.php",
"chars": 1285,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Helpers;\n\nuse Yiisoft\\Definitions\\ArrayDefinition;\nuse Yiisoft\\Def"
},
{
"path": "src/Helpers/DefinitionParser.php",
"chars": 3233,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Helpers;\n\nuse Yiisoft\\Definitions\\ArrayDefinition;\n\nuse function c"
},
{
"path": "src/NotFoundException.php",
"chars": 2029,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di;\n\nuse Exception;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse"
},
{
"path": "src/Reference/TagReference.php",
"chars": 1051,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Reference;\n\nuse InvalidArgumentException;\nuse Yiisoft\\Definitions\\"
},
{
"path": "src/ServiceProviderInterface.php",
"chars": 1863,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di;\n\n/**\n * Represents a component responsible for class registration"
},
{
"path": "src/StateResetter.php",
"chars": 2888,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di;\n\nuse Closure;\nuse InvalidArgumentException;\nuse Psr\\Container\\Con"
},
{
"path": "tests/Benchmark/ContainerBench.php",
"chars": 7029,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Benchmark;\n\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\Befo"
},
{
"path": "tests/Benchmark/ContainerMethodHasBench.php",
"chars": 1698,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Benchmark;\n\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\Befo"
},
{
"path": "tests/Support/A.php",
"chars": 138,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nclass A\n{\n public function __construct(public ?"
},
{
"path": "tests/Support/B.php",
"chars": 138,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nclass B\n{\n public function __construct(public ?"
},
{
"path": "tests/Support/Car.php",
"chars": 774,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * A car\n */\nclass Car\n{\n public ColorInter"
},
{
"path": "tests/Support/CarExtensionProvider.php",
"chars": 706,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nuse Psr\\Container\\ContainerInterface;\nuse Yiisoft\\"
},
{
"path": "tests/Support/CarFactory.php",
"chars": 541,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * Produces cars\n */\nclass CarFactory\n{\n pu"
},
{
"path": "tests/Support/CarProvider.php",
"chars": 839,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nuse Psr\\Container\\ContainerInterface;\nuse Yiisoft\\"
},
{
"path": "tests/Support/ColorInterface.php",
"chars": 193,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * Interface ColorInterface defines car color\n"
},
{
"path": "tests/Support/ColorPink.php",
"chars": 273,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * Class ColorPink\n */\nfinal class ColorPink i"
},
{
"path": "tests/Support/ColorRed.php",
"chars": 271,
"preview": "<?php\n\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * Class ColorRed\n */\nfinal class ColorRed im"
},
{
"path": "tests/Support/ConstructorTestClass.php",
"chars": 621,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nuse function func_get_args;\n\n/**\n * ConstructorTes"
},
{
"path": "tests/Support/ContainerInterfaceExtensionProvider.php",
"chars": 524,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nuse Psr\\Container\\ContainerInterface;\nuse Yiisoft\\"
},
{
"path": "tests/Support/Cycle/Chicken.php",
"chars": 139,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support\\Cycle;\n\nclass Chicken\n{\n public function __constr"
},
{
"path": "tests/Support/Cycle/Egg.php",
"chars": 143,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support\\Cycle;\n\nclass Egg\n{\n public function __construct("
},
{
"path": "tests/Support/EngineFactory.php",
"chars": 751,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nuse Exception;\nuse Psr\\Container\\ContainerInterfac"
},
{
"path": "tests/Support/EngineInterface.php",
"chars": 284,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * EngineInterface defines car engine interfac"
},
{
"path": "tests/Support/EngineMarkOne.php",
"chars": 536,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * EngineMarkOne\n */\nclass EngineMarkOne imple"
},
{
"path": "tests/Support/EngineMarkTwo.php",
"chars": 536,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * EngineMarkTwo\n */\nclass EngineMarkTwo imple"
},
{
"path": "tests/Support/EngineStorage.php",
"chars": 330,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nfinal class EngineStorage\n{\n private readonly a"
},
{
"path": "tests/Support/Garage.php",
"chars": 259,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * A garage\n */\nfinal class Garage\n{\n publi"
},
{
"path": "tests/Support/GearBox.php",
"chars": 181,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * A gear box.\n */\nclass GearBox\n{\n public "
},
{
"path": "tests/Support/InvokableCarFactory.php",
"chars": 338,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nuse Psr\\Container\\ContainerInterface;\n\nclass Invok"
},
{
"path": "tests/Support/MethodTestClass.php",
"chars": 346,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * MethodTestClass\n */\nclass MethodTestClass\n{"
},
{
"path": "tests/Support/NonPsrContainer.php",
"chars": 341,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nuse Psr\\Container\\ContainerInterface;\nuse stdClass"
},
{
"path": "tests/Support/NullCarExtensionProvider.php",
"chars": 481,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nuse Psr\\Container\\ContainerInterface;\nuse Yiisoft\\"
},
{
"path": "tests/Support/NullableConcreteDependency.php",
"chars": 153,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nclass NullableConcreteDependency\n{\n public func"
},
{
"path": "tests/Support/OptionalConcreteDependency.php",
"chars": 252,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nclass OptionalConcreteDependency\n{\n public func"
},
{
"path": "tests/Support/PropertyTestClass.php",
"chars": 149,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * PropertyTestClass\n */\nclass PropertyTestCla"
},
{
"path": "tests/Support/SportCar.php",
"chars": 767,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * A sport car\n */\nclass SportCar\n{\n public"
},
{
"path": "tests/Support/StaticFactory.php",
"chars": 204,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nuse stdClass;\n\nfinal class StaticFactory\n{\n pub"
},
{
"path": "tests/Support/TreeItem.php",
"chars": 177,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\n/**\n * TreeItem\n */\nclass TreeItem\n{\n public fu"
},
{
"path": "tests/Support/UnionTypeInConstructorFirstTypeInParamResolvable.php",
"chars": 226,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nfinal class UnionTypeInConstructorFirstTypeInParam"
},
{
"path": "tests/Support/UnionTypeInConstructorParamNotResolvable.php",
"chars": 218,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nfinal class UnionTypeInConstructorParamNotResolvab"
},
{
"path": "tests/Support/UnionTypeInConstructorSecondParamNotResolvable.php",
"chars": 221,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nfinal class UnionTypeInConstructorSecondParamNotRe"
},
{
"path": "tests/Support/UnionTypeInConstructorSecondTypeInParamResolvable.php",
"chars": 227,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nfinal class UnionTypeInConstructorSecondTypeInPara"
},
{
"path": "tests/Support/VariadicConstructor.php",
"chars": 588,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Support;\n\nfinal class VariadicConstructor\n{\n private read"
},
{
"path": "tests/Unit/BuildingExceptionTest.php",
"chars": 1591,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse PHPUnit\\Framework\\TestCase;\nuse RuntimeException;"
},
{
"path": "tests/Unit/CompositeContainerTest.php",
"chars": 3488,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\A"
},
{
"path": "tests/Unit/CompositePsrContainerOverLeagueTest.php",
"chars": 1742,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse League\\Container\\Container;\nuse Psr\\Container\\Con"
},
{
"path": "tests/Unit/CompositePsrContainerOverYiisoftTest.php",
"chars": 4140,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse Psr\\Container\\ContainerInterface;\nuse Yiisoft\\Di\\"
},
{
"path": "tests/Unit/CompositePsrContainerTestAbstract.php",
"chars": 6079,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Contain"
},
{
"path": "tests/Unit/Container/DependencyFromDelegate/Car.php",
"chars": 211,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit\\Container\\DependencyFromDelegate;\n\nfinal class Car\n{\n "
},
{
"path": "tests/Unit/Container/DependencyFromDelegate/DependencyFromDelegateTest.php",
"chars": 2809,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit\\Container\\DependencyFromDelegate;\n\nuse PHPUnit\\Framewor"
},
{
"path": "tests/Unit/Container/DependencyFromDelegate/Engine.php",
"chars": 149,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit\\Container\\DependencyFromDelegate;\n\nfinal class Engine i"
},
{
"path": "tests/Unit/Container/DependencyFromDelegate/EngineInterface.php",
"chars": 129,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit\\Container\\DependencyFromDelegate;\n\ninterface EngineInte"
},
{
"path": "tests/Unit/ContainerTest.php",
"chars": 69953,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse ArrayIterator;\nuse PHPUnit\\Framework\\Attributes\\D"
},
{
"path": "tests/Unit/Helpers/DefinitionParserTest.php",
"chars": 1707,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit\\Helpers;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Yiisoft\\D"
},
{
"path": "tests/Unit/LeaguePsrContainerTest.php",
"chars": 684,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse League\\Container\\Container;\nuse Psr\\Container\\Con"
},
{
"path": "tests/Unit/NotFoundExceptionTest.php",
"chars": 1361,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Yiisoft\\Di\\NotFou"
},
{
"path": "tests/Unit/PsrContainerTestAbstract.php",
"chars": 2271,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Psr\\Container\\Con"
},
{
"path": "tests/Unit/Reference/TagReference/Resolve/A.php",
"chars": 115,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit\\Reference\\TagReference\\Resolve;\n\nfinal class A {}\n"
},
{
"path": "tests/Unit/Reference/TagReference/Resolve/B.php",
"chars": 115,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit\\Reference\\TagReference\\Resolve;\n\nfinal class B {}\n"
},
{
"path": "tests/Unit/Reference/TagReference/Resolve/Main.php",
"chars": 148,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit\\Reference\\TagReference\\Resolve;\n\nfinal class Main\n{\n "
},
{
"path": "tests/Unit/Reference/TagReference/Resolve/TagReferenceResolveTest.php",
"chars": 912,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit\\Reference\\TagReference\\Resolve;\n\nuse PHPUnit\\Framework\\"
},
{
"path": "tests/Unit/Reference/TagReference/TagReferenceTest.php",
"chars": 2122,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit\\Reference\\TagReference;\n\nuse Error;\nuse InvalidArgument"
},
{
"path": "tests/Unit/ServiceProviderTest.php",
"chars": 6459,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Psr\\Container\\Con"
},
{
"path": "tests/Unit/StateResetterTest.php",
"chars": 1775,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\T"
},
{
"path": "tests/Unit/YiisoftPsrContainerTest.php",
"chars": 507,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Yiisoft\\Di\\Tests\\Unit;\n\nuse Psr\\Container\\ContainerInterface;\nuse Yiisoft\\Di\\"
},
{
"path": "tools/.gitignore",
"chars": 27,
"preview": "/*/vendor\n/*/composer.lock\n"
},
{
"path": "tools/infection/composer.json",
"chars": 191,
"preview": "{\n \"require-dev\": {\n \"infection/infection\": \"^0.26 || ^0.31.9\"\n },\n \"config\": {\n \"allow-plugins\":"
},
{
"path": "tools/psalm/composer.json",
"chars": 73,
"preview": "{\n \"require-dev\": {\n \"vimeo/psalm\": \"^5.26.1 || ^6.12\"\n }\n}\n"
}
]
About this extraction
This page contains the full source code of the yiisoft/di GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 105 files (227.9 KB), approximately 55.2k tokens, and a symbol index with 387 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.