init
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 120
|
||||
tab_width = 4
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
# Changelog
|
||||
|
||||
All Notable changes to `League\Container` will be documented in this file
|
||||
|
||||
## 5.1.0
|
||||
|
||||
### Added
|
||||
- Attribute based resolution for dependencies using `#[Inject]` and `#[Resolve]` attributes.
|
||||
- Docs: [https://container.thephpleague.com/5.x/attribute-resolution/](https://container.thephpleague.com/5.x/attribute-resolution/)
|
||||
- Support for PHPUnit 12 (@ADmad)
|
||||
- Explicit non-support for auto-wiring union types.
|
||||
|
||||
### Changed
|
||||
- Small internal changes for stricter static analysis and type safety. (@ADmad)
|
||||
|
||||
### 5.0.1
|
||||
|
||||
### Fixed
|
||||
- Fixed a small unreachable code bug
|
||||
|
||||
## 5.0.0
|
||||
|
||||
### Added
|
||||
- Ability to overwrite a definition within the container, disabled by default
|
||||
|
||||
### Changed
|
||||
- PHP requirement now `>=8.1`
|
||||
- General language modernisation
|
||||
- General prep for future updates and container compilation
|
||||
|
||||
## 4.2.4
|
||||
|
||||
### Fixed
|
||||
- Now properly handle string based fully qualified class names in with preceding slashes. (@M1ke)
|
||||
|
||||
## 4.2.3
|
||||
|
||||
### Fixed
|
||||
- Warnings for PHP 8.4 implicit nullable types (@andypost)
|
||||
|
||||
## 4.2.1
|
||||
|
||||
### Fixed
|
||||
- Remove an unnecessary conditional in ServiceProviderAggregate. (@SubhanSh)
|
||||
- Fixed an infinite loop in resolving definitions. (@arai-ta)
|
||||
|
||||
## 4.2.0
|
||||
|
||||
### Added
|
||||
- Support for psr/container 1.1.0.
|
||||
|
||||
## 4.1.2
|
||||
|
||||
### Fixed
|
||||
- Fix bug that was causing an error on inflection due to introduced type hint.
|
||||
|
||||
## 4.1.1
|
||||
|
||||
### Changed
|
||||
- Move files autoload directive to dev (@ADmad)
|
||||
|
||||
## 4.1.0
|
||||
|
||||
### Added
|
||||
- Way to handle non-public controllers safely (@beryllium)
|
||||
|
||||
## 4.0.0
|
||||
|
||||
### Added
|
||||
- New definition interface that extends psr/container
|
||||
- Literal and resolvable argument wrappers for better explicitness in definitions
|
||||
|
||||
### Changed
|
||||
- PHP requirement now `>=7.2`
|
||||
- Updated `psr/container` to ^2.0.0
|
||||
- `Container::shared` convenience method is now explicit `Container::addShared` method
|
||||
- Removed third argument `$shared` from `Container::add`, use `Container::addShared`
|
||||
- `ServiceProviderInterface` now defines return types
|
||||
- Service providers now require implementation of a `provides` method rather than relying on a class property.
|
||||
|
||||
## 3.4.1
|
||||
|
||||
### Added
|
||||
- Way to handle non-public controllers safely (@beryllium)
|
||||
- PHPUnit ^7.0 for PHP versions that support it (@beryllium)
|
||||
|
||||
## 3.4.0
|
||||
|
||||
### Removed
|
||||
- Support for `psr/container` ^2.0.0 as the interface cannot be reconciled between versions
|
||||
|
||||
## 3.3.5
|
||||
|
||||
### Added
|
||||
- Support for `psr/container` ^2.0.0
|
||||
|
||||
## 3.3.4
|
||||
|
||||
### Fixed
|
||||
- Fixed an issue that caused a recursive `register` call. @pcoutinho
|
||||
- Fixed a return type declaration. @orbex
|
||||
|
||||
## 3.3.3
|
||||
|
||||
### Fixed
|
||||
- Fixed bug relating to `ReflectionContainer::call` on arrow functions.
|
||||
|
||||
## 3.3.2
|
||||
|
||||
### Added
|
||||
- Experimental support for PHP 8.
|
||||
|
||||
### Fixed
|
||||
- Fix issue when preventing reflection from using default value for arguments.
|
||||
|
||||
## 3.3.1
|
||||
|
||||
### Fixed
|
||||
- Respect `$new` argument when getting tagged definitions.
|
||||
|
||||
## 3.3.0
|
||||
|
||||
### Added
|
||||
- Support for PHP 7.3
|
||||
- `{set,get}LeagueContainer` methods added to ContainerAwareTrait as a temporary measure until next major release when this can be properly addressed, less hinting of `Psr\Container\ContainerInterface`
|
||||
|
||||
### Changed
|
||||
- Various internal code improvements
|
||||
|
||||
### Fixed
|
||||
- Fix for `setConcrete` not re-resolving class on when overriding (@jleeothon)
|
||||
- Fix stack overflow error incase a service provider lies about providing a specific service (@azjezz)
|
||||
- Fix issue where providers may be aggregated multiple times (@bwg)
|
||||
- Various documentation fixes
|
||||
|
||||
## 3.2.2
|
||||
|
||||
### Fixed
|
||||
- Fixed issue that prevented service providers from registering if a previous one in the aggregate was already registered.
|
||||
|
||||
## 3.2.1
|
||||
|
||||
### Fixed
|
||||
- Fixed issue where all service providers were registered regardless of whether they need to be.
|
||||
|
||||
## 3.2.0
|
||||
|
||||
### Added
|
||||
- Added ability to add definition as not shared when container is set to default to shared.
|
||||
- Added `{set|get}Concrete` to definitions to allow for better use of `extend`.
|
||||
|
||||
## 3.1.0
|
||||
|
||||
### Added
|
||||
- Re-added the `share` proxy method that was mistakenly removed in previous major release.
|
||||
- Added ability to set Container to "share" by default using `defaultToShared` method.
|
||||
- Added ability for `ReflectionContainer` to cache resolutions and pull from cache for following calls.
|
||||
|
||||
## 3.0.1
|
||||
|
||||
### Added
|
||||
- Allow definition aggregates to be built outside of container.
|
||||
|
||||
## 3.0.0
|
||||
|
||||
### Added
|
||||
- Service providers can now be pulled from the container if they are registered.
|
||||
- Definition logic now handled by aggregate for better separation.
|
||||
- Now able to add tags to a definition to return an array of items containing that tag.
|
||||
|
||||
### Changed
|
||||
- Updated minimum PHP requirements to 7.0.
|
||||
- Now depend directly on PSR-11 interfaces, including providing PSR-11 exceptions.
|
||||
- Refactored inflector logic to accept type on construction and use generator to iterate.
|
||||
- Refactored service provider logic with better separation and performance.
|
||||
- Merged service provider signature logic in to one interface and abstract.
|
||||
- Heavily simplified definition logic providing more control to user.
|
||||
|
||||
## 2.4.1
|
||||
|
||||
### Fixed
|
||||
- Ensures `ReflectionContainer` converts class name in array callable to object.
|
||||
|
||||
## 2.4.0
|
||||
|
||||
### Changed
|
||||
- Can now wrap shared objects as `RawArgument`.
|
||||
- Ability to override shared items.
|
||||
|
||||
### Fixed
|
||||
- Booleans now recognised as accepted values.
|
||||
- Various docblock fixes.
|
||||
- Unused imports removed.
|
||||
- Unreachable arguments no longer passed.
|
||||
|
||||
## 2.3.0
|
||||
|
||||
### Added
|
||||
- Now implementation of the PSR-11.
|
||||
|
||||
## 2.2.0
|
||||
|
||||
### Changed
|
||||
- Service providers can now be added multiple times by giving them a signature.
|
||||
|
||||
## 2.1.0
|
||||
|
||||
### Added
|
||||
- Allow resolving of `RawArgument` objects as first class dependencies.
|
||||
|
||||
### Changed
|
||||
- Unnecessary recursion removed from `Container::get`.
|
||||
|
||||
## 2.0.3
|
||||
|
||||
### Fixed
|
||||
- Bug where delegating container was not passed to delegate when needed.
|
||||
- Bug where `Container::extend` would not return a shared definition to extend.
|
||||
|
||||
## 2.0.2
|
||||
|
||||
### Fixed
|
||||
- Bug introduced in 2.0.1 where shared definitions registered via a service provider would never be returned as shared.
|
||||
|
||||
## 2.0.1
|
||||
|
||||
### Fixed
|
||||
- Bug where shared definitions were not stored as shared.
|
||||
|
||||
## 2.0.0
|
||||
|
||||
### Added
|
||||
- Now implementation of the container-interop project.
|
||||
- `BootableServiceProviderInterface` for eagerly loaded service providers.
|
||||
- Delegate container functionality.
|
||||
- `RawArgument` to ensure scalars are not resolved from the container but seen as an argument.
|
||||
|
||||
### Altered
|
||||
- Refactor of definition functionality.
|
||||
- `Container::share` replaces `singleton` functionality to improve understanding.
|
||||
- Auto wiring is now disabled by default.
|
||||
- Auto wiring abstracted to be a delegate container `ReflectionContainer` handling all reflection based functionality.
|
||||
- Inflection functionality abstracted to an aggregate.
|
||||
- Service provider functionality abstracted to an aggregate.
|
||||
- Much bloat removed.
|
||||
- `Container::call` now proxies to `ReflectionContainer::call` and handles argument resolution in a much more efficient way.
|
||||
|
||||
### Removed
|
||||
- Ability to register invokables, this functionality added a layer of complexity too large for the problem it solved.
|
||||
- Container no longer accepts a configuration array, this functionality will now be provided by an external service provider package.
|
||||
|
||||
## 1.4.0
|
||||
|
||||
### Added
|
||||
- Added `isRegisteredCallable` method to public API.
|
||||
- Invoking `call` now accepts named arguments at runtime.
|
||||
|
||||
### Fixed
|
||||
- Container now stores instantiated Service Providers after first instantiation.
|
||||
- Extending a definition now looks in Service Providers as well as just Definitions.
|
||||
|
||||
## 1.3.1 - 2015-02-21
|
||||
|
||||
### Fixed
|
||||
- Fixed bug where arbitrary values were attempted to be resolved as classes.
|
||||
|
||||
## 1.3.0 - 2015-02-09
|
||||
|
||||
### Added
|
||||
- Added `ServiceProvider` functionality to allow cleaner resolving of complex dependencies.
|
||||
- Added `Inflector` functionality to allow for manipulation of resolved objects of a specific type.
|
||||
- Improvements to DRY throughout the package.
|
||||
|
||||
### Fixed
|
||||
- Setter in `ContainerAwareTrait` now returns self (`$this`).
|
||||
|
||||
## 1.2.1 - 2015-01-29
|
||||
|
||||
### Fixed
|
||||
- Allow arbitrary values to be registered via container config.
|
||||
|
||||
## 1.2.0 - 2015-01-13
|
||||
|
||||
### Added
|
||||
- Improvements to `Container::call` functionality.
|
||||
|
||||
### Fixed
|
||||
- General code tidy.
|
||||
- Improvements to test suite.
|
||||
|
||||
## 1.1.1 - 2015-01-13
|
||||
|
||||
### Fixed
|
||||
- Allow singleton to be passed as method argument.
|
||||
|
||||
## 1.1.0 - 2015-01-12
|
||||
|
||||
### Added
|
||||
- Addition of `ContainerAwareTrait` to provide functionality from `ContainerAwareInterface`.
|
||||
|
||||
## 1.0.0 - 2015-01-12
|
||||
|
||||
### Added
|
||||
- Migrated from [Orno\Di](https://github.com/orno/di).
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# Contributing
|
||||
|
||||
Contributions are **welcome** and will be fully **credited**.
|
||||
|
||||
We accept contributions via Pull Requests on [GitHub](https://github.com/thephpleague/container).
|
||||
|
||||
## Pull Requests
|
||||
|
||||
- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).
|
||||
|
||||
- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
|
||||
|
||||
- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
|
||||
|
||||
- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option.
|
||||
|
||||
- **Create feature branches** - Don't ask us to pull from your master branch.
|
||||
|
||||
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
|
||||
|
||||
- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting.
|
||||
|
||||
## Running Tests
|
||||
|
||||
``` bash
|
||||
$ composer test
|
||||
```
|
||||
|
||||
**Happy coding**!
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Phil Bennett <philipobenito@gmail.com>
|
||||
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in
|
||||
> all copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
> THE SOFTWARE.
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
# Container (Dependency Injection)
|
||||
|
||||
[](https://github.com/philipobenito)
|
||||
[](https://github.com/thephpleague/container/releases)
|
||||
[](LICENSE.md)
|
||||
[](https://github.com/thephpleague/container/actions/workflows/test.yml)
|
||||
[](https://scrutinizer-ci.com/g/thephpleague/container/code-structure)
|
||||
[](https://scrutinizer-ci.com/g/thephpleague/container)
|
||||
[](https://packagist.org/packages/league/container)
|
||||
|
||||
This package is compliant with [PSR-1], [PSR-2], [PSR-12], [PSR-4], [PSR-11] and [PSR-12]. If you notice compliance oversights, please send a patch via pull request.
|
||||
|
||||
[PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
|
||||
[PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
|
||||
[PSR-12]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-12-extended-coding-style-guide.md
|
||||
[PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md
|
||||
[PSR-11]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md
|
||||
[PSR-12]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-12-extended-coding-style-guide.md
|
||||
|
||||
## Install
|
||||
|
||||
Via Composer
|
||||
|
||||
``` bash
|
||||
composer require league/container
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
The following versions of PHP are supported by this version.
|
||||
|
||||
* PHP 8.1
|
||||
* PHP 8.2
|
||||
* PHP 8.3
|
||||
* PHP 8.4
|
||||
|
||||
## Documentation
|
||||
|
||||
Container has [full documentation](http://container.thephpleague.com), powered by [Jekyll](http://jekyllrb.com/).
|
||||
|
||||
Contribute to this documentation in the [docs/](https://github.com/thephpleague/container/tree/master/docs) sub-directory.
|
||||
|
||||
## Testing
|
||||
|
||||
Testing includes PHPUnit and PHPStan (Level 7).
|
||||
``` bash
|
||||
$ composer test
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see [CONTRIBUTING](https://github.com/thephpleague/container/blob/master/CONTRIBUTING.md) for details.
|
||||
|
||||
## Security
|
||||
|
||||
If you discover any security related issues, please email philipobenito@gmail.com instead of using the issue tracker.
|
||||
|
||||
## Credits
|
||||
|
||||
- [Phil Bennett](https://github.com/philipobenito)
|
||||
- [All Contributors](https://github.com/thephpleague/container/contributors)
|
||||
- `Orno\Di` contributors
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](https://github.com/thephpleague/container/blob/master/LICENSE.md) for more information.
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "league/container",
|
||||
"description": "A fast and intuitive dependency injection container.",
|
||||
"keywords": [
|
||||
"league",
|
||||
"container",
|
||||
"dependency",
|
||||
"injection",
|
||||
"di",
|
||||
"service",
|
||||
"provider"
|
||||
],
|
||||
"homepage": "https://github.com/thephpleague/container",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Phil Bennett",
|
||||
"email": "mail@philbennett.co.uk",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"psr/container": "^2.0.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/php-generator": "^4.1",
|
||||
"nikic/php-parser": "^5.0",
|
||||
"phpstan/phpstan": "^2.1.11",
|
||||
"phpunit/phpunit": "^10.5.45|^11.5.15|^12.0",
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"scrutinizer/ocular": "^1.9",
|
||||
"squizlabs/php_codesniffer": "^3.9"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "^1.0"
|
||||
},
|
||||
"replace": {
|
||||
"orno/di": "~2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Container\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"League\\Container\\Test\\": "tests"
|
||||
},
|
||||
"files": [
|
||||
"tests/Asset/function.php"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.x-dev",
|
||||
"dev-5.x": "5.x-dev",
|
||||
"dev-4.x": "4.x-dev",
|
||||
"dev-3.x": "3.x-dev",
|
||||
"dev-2.x": "2.x-dev",
|
||||
"dev-1.x": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"vendor/bin/phpunit",
|
||||
"vendor/bin/phpstan analyse"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
interface ArgumentInterface
|
||||
{
|
||||
public function getValue(): mixed;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
use League\Container\ContainerAwareInterface;
|
||||
use ReflectionFunctionAbstract;
|
||||
|
||||
interface ArgumentReflectorInterface extends ContainerAwareInterface
|
||||
{
|
||||
public function reflectArguments(ReflectionFunctionAbstract $method, array $args = []): array;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
use League\Container\Attribute\AttributeInterface;
|
||||
use League\Container\Exception\NotFoundException;
|
||||
use League\Container\ReflectionContainer;
|
||||
use League\Container\{ContainerAwareInterface, DefinitionContainerInterface};
|
||||
use Psr\Container\{ContainerExceptionInterface, NotFoundExceptionInterface};
|
||||
use ReflectionAttribute;
|
||||
use ReflectionException;
|
||||
use ReflectionFunctionAbstract;
|
||||
use ReflectionMethod;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionParameter;
|
||||
use ReflectionUnionType;
|
||||
|
||||
trait ArgumentReflectorTrait
|
||||
{
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function reflectArguments(ReflectionFunctionAbstract $method, array $args = []): array
|
||||
{
|
||||
$params = $method->getParameters();
|
||||
$arguments = [];
|
||||
|
||||
foreach ($params as $param) {
|
||||
$name = $param->getName();
|
||||
|
||||
// if we've been given a value for the argument, treat as literal
|
||||
if (array_key_exists($name, $args)) {
|
||||
$arguments[] = new LiteralArgument($args[$name]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// next we see if we have an attribute that can resolve the argument (if enabled)
|
||||
if ($this->getMode() & ReflectionContainer::ATTRIBUTE_RESOLUTION) {
|
||||
$attrs = $param->getAttributes();
|
||||
|
||||
foreach ($attrs as $attr) {
|
||||
if ($argument = $this->resolveArgumentFromAttribute($attr)) {
|
||||
$arguments[] = $argument;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$type = $param->getType();
|
||||
|
||||
// if we have a union type, loop until we can resolve
|
||||
if ($type instanceof ReflectionUnionType) {
|
||||
$this->throwParameterException(
|
||||
$name,
|
||||
'union',
|
||||
$param->getDeclaringClass()?->getName(),
|
||||
$method->getName(),
|
||||
$method instanceof ReflectionMethod ? $method->isClosure() : false,
|
||||
'Union types are not supported'
|
||||
);
|
||||
}
|
||||
|
||||
// then we check if we have a type hint (if auto wiring is enabled)
|
||||
if ($this->getMode() & ReflectionContainer::AUTO_WIRING && $type instanceof ReflectionNamedType) {
|
||||
$arguments[] = $this->resolveArgumentForNamedType($param, $type);
|
||||
continue;
|
||||
}
|
||||
|
||||
// finally we check if we have a default value
|
||||
if ($param->isDefaultValueAvailable()) {
|
||||
$arguments[] = new LiteralArgument($param->getDefaultValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->throwParameterException(
|
||||
$name,
|
||||
$type instanceof ReflectionNamedType ? $type->getName() : 'unknown',
|
||||
$param->getDeclaringClass()?->getName(),
|
||||
$method->getName(),
|
||||
$method instanceof ReflectionMethod ? $method->isClosure() : false,
|
||||
'No default value available and no type hint to resolve'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->resolveArguments($arguments);
|
||||
}
|
||||
|
||||
protected function resolveArgumentFromAttribute(ReflectionAttribute $attribute): LiteralArgumentInterface|false
|
||||
{
|
||||
$attrClass = $attribute->getName();
|
||||
|
||||
if (is_subclass_of($attrClass, AttributeInterface::class)) {
|
||||
$attrClass = $attribute->newInstance();
|
||||
|
||||
if ($attrClass instanceof ContainerAwareInterface) {
|
||||
$attrClass->setContainer($this->getContainer());
|
||||
}
|
||||
|
||||
// purposely don't define a type here so that any typing errors
|
||||
// from the consuming code bubble up
|
||||
/** @var AttributeInterface $attrClass */
|
||||
return new LiteralArgument($attrClass->resolve(), null);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function resolveArgumentForNamedType(
|
||||
ReflectionParameter $param,
|
||||
ReflectionNamedType $type,
|
||||
): ResolvableArgumentInterface {
|
||||
$typeHint = $type->getName();
|
||||
|
||||
if ($type->getName() === 'mixed') {
|
||||
$this->throwParameterException(
|
||||
$param->getName(),
|
||||
'mixed',
|
||||
$param->getDeclaringClass()?->getName(),
|
||||
$param->getDeclaringFunction()->getName(),
|
||||
$param->getDeclaringFunction()->isClosure(),
|
||||
'Mixed types are not supported'
|
||||
);
|
||||
}
|
||||
|
||||
if ($param->isDefaultValueAvailable()) {
|
||||
return new DefaultValueArgument($typeHint, $param->getDefaultValue());
|
||||
}
|
||||
|
||||
return new ResolvableArgument($typeHint);
|
||||
}
|
||||
|
||||
public function throwParameterException(
|
||||
string $name,
|
||||
string $type,
|
||||
?string $declaringClass = null,
|
||||
?string $declaringFunction = null,
|
||||
bool $isClosure = false,
|
||||
?string $additionalMessage = null
|
||||
): void {
|
||||
throw new NotFoundException(sprintf(
|
||||
'Unable to resolve parameter ($%s) with type (%s) in %s%s%s()%s',
|
||||
$name,
|
||||
$type,
|
||||
$declaringClass ? $declaringClass . '::' : '',
|
||||
$declaringFunction ?? '',
|
||||
$isClosure ? ' [closure]' : '',
|
||||
$additionalMessage ? ' - ' . $additionalMessage : ''
|
||||
));
|
||||
}
|
||||
|
||||
abstract public function getContainer(): DefinitionContainerInterface;
|
||||
abstract public function getMode(): int;
|
||||
abstract public function resolveArguments(array $arguments): array;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
use League\Container\ContainerAwareInterface;
|
||||
|
||||
interface ArgumentResolverInterface extends ContainerAwareInterface
|
||||
{
|
||||
public function resolveArguments(array $arguments): array;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
use League\Container\Exception\{ContainerException, NotFoundException};
|
||||
use League\Container\{DefinitionContainerInterface, ReflectionContainer};
|
||||
use Psr\Container\{ContainerExceptionInterface, ContainerInterface, NotFoundExceptionInterface};
|
||||
use ReflectionException;
|
||||
|
||||
trait ArgumentResolverTrait
|
||||
{
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws ReflectionException
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function resolveArguments(array $arguments): array
|
||||
{
|
||||
try {
|
||||
$container = $this->getContainer();
|
||||
} catch (ContainerException) {
|
||||
$container = ($this instanceof ReflectionContainer) ? $this : null;
|
||||
}
|
||||
|
||||
foreach ($arguments as &$arg) {
|
||||
// if we have a literal, we don't want to do anything more with it
|
||||
if ($arg instanceof LiteralArgumentInterface) {
|
||||
$arg = $arg->getValue();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($arg instanceof ArgumentInterface) {
|
||||
$argValue = $arg->getValue();
|
||||
} else {
|
||||
$argValue = $arg;
|
||||
}
|
||||
|
||||
if (!is_string($argValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// resolve the argument from the container, if it happens to be another
|
||||
// argument wrapper, use that value
|
||||
if ($container instanceof ContainerInterface && $container->has($argValue)) {
|
||||
try {
|
||||
$arg = $container->get($argValue);
|
||||
|
||||
if ($arg instanceof ArgumentInterface) {
|
||||
$arg = $arg->getValue();
|
||||
}
|
||||
|
||||
continue;
|
||||
} catch (NotFoundException) {
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a default value, we use that, no more resolution as
|
||||
// we expect a default/optional argument value to be literal
|
||||
if ($arg instanceof DefaultValueInterface) {
|
||||
$arg = $arg->getDefaultValue();
|
||||
}
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
abstract public function getContainer(): DefinitionContainerInterface;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
class DefaultValueArgument extends ResolvableArgument implements DefaultValueInterface
|
||||
{
|
||||
public function __construct(string $value, protected mixed $defaultValue = null)
|
||||
{
|
||||
parent::__construct($value);
|
||||
}
|
||||
|
||||
public function getDefaultValue(): mixed
|
||||
{
|
||||
return $this->defaultValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
interface DefaultValueInterface extends ArgumentInterface
|
||||
{
|
||||
public function getDefaultValue(): mixed;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument\Literal;
|
||||
|
||||
use League\Container\Argument\LiteralArgument;
|
||||
|
||||
class ArrayArgument extends LiteralArgument
|
||||
{
|
||||
public function __construct(array $value)
|
||||
{
|
||||
parent::__construct($value, LiteralArgument::TYPE_ARRAY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument\Literal;
|
||||
|
||||
use League\Container\Argument\LiteralArgument;
|
||||
|
||||
class BooleanArgument extends LiteralArgument
|
||||
{
|
||||
public function __construct(bool $value)
|
||||
{
|
||||
parent::__construct($value, LiteralArgument::TYPE_BOOL);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument\Literal;
|
||||
|
||||
use League\Container\Argument\LiteralArgument;
|
||||
|
||||
class CallableArgument extends LiteralArgument
|
||||
{
|
||||
public function __construct(callable $value)
|
||||
{
|
||||
parent::__construct($value, LiteralArgument::TYPE_CALLABLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument\Literal;
|
||||
|
||||
use League\Container\Argument\LiteralArgument;
|
||||
|
||||
class FloatArgument extends LiteralArgument
|
||||
{
|
||||
public function __construct(float $value)
|
||||
{
|
||||
parent::__construct($value, LiteralArgument::TYPE_FLOAT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument\Literal;
|
||||
|
||||
use League\Container\Argument\LiteralArgument;
|
||||
|
||||
class IntegerArgument extends LiteralArgument
|
||||
{
|
||||
public function __construct(int $value)
|
||||
{
|
||||
parent::__construct($value, LiteralArgument::TYPE_INT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument\Literal;
|
||||
|
||||
use League\Container\Argument\LiteralArgument;
|
||||
|
||||
class ObjectArgument extends LiteralArgument
|
||||
{
|
||||
public function __construct(object $value)
|
||||
{
|
||||
parent::__construct($value, LiteralArgument::TYPE_OBJECT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument\Literal;
|
||||
|
||||
use League\Container\Argument\LiteralArgument;
|
||||
|
||||
class StringArgument extends LiteralArgument
|
||||
{
|
||||
public function __construct(string $value)
|
||||
{
|
||||
parent::__construct($value, LiteralArgument::TYPE_STRING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class LiteralArgument implements LiteralArgumentInterface
|
||||
{
|
||||
public const TYPE_ARRAY = 'array';
|
||||
public const TYPE_BOOLEAN = 'boolean';
|
||||
public const TYPE_BOOL = self::TYPE_BOOLEAN;
|
||||
public const TYPE_CALLABLE = 'callable';
|
||||
public const TYPE_DOUBLE = 'double';
|
||||
public const TYPE_FLOAT = self::TYPE_DOUBLE;
|
||||
public const TYPE_INTEGER = 'integer';
|
||||
public const TYPE_INT = self::TYPE_INTEGER;
|
||||
public const TYPE_OBJECT = 'object';
|
||||
public const TYPE_STRING = 'string';
|
||||
|
||||
protected mixed $value;
|
||||
|
||||
public function __construct(mixed $value, ?string $type = null)
|
||||
{
|
||||
if (
|
||||
null === $type
|
||||
|| ($type === self::TYPE_CALLABLE && is_callable($value))
|
||||
|| ($type === self::TYPE_OBJECT && is_object($value))
|
||||
|| gettype($value) === $type
|
||||
) {
|
||||
$this->value = $value;
|
||||
} else {
|
||||
throw new InvalidArgumentException('Incorrect type for value.');
|
||||
}
|
||||
}
|
||||
|
||||
public function getValue(): mixed
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
interface LiteralArgumentInterface extends ArgumentInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
class ResolvableArgument implements ResolvableArgumentInterface
|
||||
{
|
||||
public function __construct(protected string $value)
|
||||
{
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Argument;
|
||||
|
||||
interface ResolvableArgumentInterface extends ArgumentInterface
|
||||
{
|
||||
public function getValue(): string;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Attribute;
|
||||
|
||||
interface AttributeInterface
|
||||
{
|
||||
public function resolve(): mixed;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Attribute;
|
||||
|
||||
use Attribute;
|
||||
use League\Container\{ContainerAwareInterface, ContainerAwareTrait};
|
||||
use Psr\Container\{ContainerExceptionInterface, NotFoundExceptionInterface};
|
||||
|
||||
#[Attribute(Attribute::TARGET_PARAMETER | Attribute::IS_REPEATABLE)]
|
||||
class Inject implements AttributeInterface, ContainerAwareInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
public function __construct(protected string $id)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function resolve(): mixed
|
||||
{
|
||||
return $this->getContainer()->get($this->id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Attribute;
|
||||
|
||||
use Attribute;
|
||||
use League\Container\{ContainerAwareInterface, ContainerAwareTrait, Exception\NotFoundException};
|
||||
use Psr\Container\{ContainerExceptionInterface, NotFoundExceptionInterface};
|
||||
|
||||
#[Attribute(Attribute::TARGET_PARAMETER | Attribute::IS_REPEATABLE)]
|
||||
class Resolve implements AttributeInterface, ContainerAwareInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
public function __construct(protected string $resolver, protected string $path)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function resolve(): mixed
|
||||
{
|
||||
$resolved = $this->getContainer()->get($this->resolver);
|
||||
|
||||
foreach (explode('.', $this->path) as $segment) {
|
||||
$resolved = $this->getResolvedValue($resolved, $segment);
|
||||
}
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
protected function getResolvedValue(mixed $previous, string $next): mixed
|
||||
{
|
||||
if (is_object($previous) && method_exists($previous, $next)) {
|
||||
return $previous->{$next}();
|
||||
}
|
||||
|
||||
if (is_object($previous) && property_exists($previous, $next)) {
|
||||
return $previous->{$next};
|
||||
}
|
||||
|
||||
if (is_array($previous) && array_key_exists($next, $previous)) {
|
||||
return $previous[$next];
|
||||
}
|
||||
|
||||
throw new NotFoundException(
|
||||
sprintf(
|
||||
'Unable to resolve value for path (%s) on resolver (%s)',
|
||||
$this->path,
|
||||
$this->resolver
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container;
|
||||
|
||||
use League\Container\Definition\{DefinitionAggregate, DefinitionInterface, DefinitionAggregateInterface};
|
||||
use League\Container\Exception\{NotFoundException, ContainerException};
|
||||
use League\Container\Inflector\{InflectorAggregate, InflectorInterface, InflectorAggregateInterface};
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use League\Container\ServiceProvider\{ServiceProviderAggregate,
|
||||
ServiceProviderAggregateInterface,
|
||||
ServiceProviderInterface};
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class Container implements DefinitionContainerInterface
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface[]
|
||||
*/
|
||||
protected array $delegates = [];
|
||||
|
||||
public function __construct(
|
||||
protected DefinitionAggregateInterface $definitions = new DefinitionAggregate(),
|
||||
protected ServiceProviderAggregateInterface $providers = new ServiceProviderAggregate(),
|
||||
protected InflectorAggregateInterface $inflectors = new InflectorAggregate(),
|
||||
protected bool $defaultToShared = false,
|
||||
protected bool $defaultToOverwrite = false,
|
||||
) {
|
||||
$this->definitions->setContainer($this);
|
||||
$this->providers->setContainer($this);
|
||||
$this->inflectors->setContainer($this);
|
||||
}
|
||||
|
||||
public function add(string $id, mixed $concrete = null, bool $overwrite = false): DefinitionInterface
|
||||
{
|
||||
$toOverwrite = $this->defaultToOverwrite || $overwrite;
|
||||
$concrete = $concrete ?? $id;
|
||||
|
||||
if (true === $this->defaultToShared) {
|
||||
return $this->addShared($id, $concrete, $toOverwrite);
|
||||
}
|
||||
|
||||
return $this->definitions->add($id, $concrete, $toOverwrite);
|
||||
}
|
||||
|
||||
public function addShared(string $id, mixed $concrete = null, bool $overwrite = false): DefinitionInterface
|
||||
{
|
||||
$toOverwrite = $this->defaultToOverwrite || $overwrite;
|
||||
$concrete = $concrete ?? $id;
|
||||
return $this->definitions->addShared($id, $concrete, $toOverwrite);
|
||||
}
|
||||
|
||||
public function defaultToShared(bool $shared = true): ContainerInterface
|
||||
{
|
||||
$this->defaultToShared = $shared;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function defaultToOverwrite(bool $overwrite = true): ContainerInterface
|
||||
{
|
||||
$this->defaultToOverwrite = $overwrite;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function extend(string $id): DefinitionInterface
|
||||
{
|
||||
if ($this->providers->provides($id)) {
|
||||
$this->providers->register($id);
|
||||
}
|
||||
|
||||
if ($this->definitions->has($id)) {
|
||||
return $this->definitions->getDefinition($id);
|
||||
}
|
||||
|
||||
throw new NotFoundException(sprintf(
|
||||
'Unable to extend alias (%s) as it is not being managed as a definition',
|
||||
$id
|
||||
));
|
||||
}
|
||||
|
||||
public function addServiceProvider(ServiceProviderInterface $provider): DefinitionContainerInterface
|
||||
{
|
||||
$this->providers->add($provider);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get(string $id)
|
||||
{
|
||||
return $this->resolve($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function getNew(string $id): mixed
|
||||
{
|
||||
return $this->resolve($id, true);
|
||||
}
|
||||
|
||||
public function has(string $id): bool
|
||||
{
|
||||
if ($this->definitions->has($id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->definitions->hasTag($id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->providers->provides($id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->delegates as $delegate) {
|
||||
if ($delegate->has($id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function inflector(string $type, ?callable $callback = null): InflectorInterface
|
||||
{
|
||||
return $this->inflectors->add($type, $callback);
|
||||
}
|
||||
|
||||
public function delegate(ContainerInterface $container): self
|
||||
{
|
||||
$this->delegates[] = $container;
|
||||
|
||||
if ($container instanceof ContainerAwareInterface) {
|
||||
$container->setContainer($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
protected function resolve(string $id, bool $new = false): mixed
|
||||
{
|
||||
if ($this->definitions->has($id)) {
|
||||
$resolved = (true === $new) ? $this->definitions->resolveNew($id) : $this->definitions->resolve($id);
|
||||
return $this->inflectors->inflect($resolved);
|
||||
}
|
||||
|
||||
if ($this->definitions->hasTag($id)) {
|
||||
$arrayOf = (true === $new)
|
||||
? $this->definitions->resolveTaggedNew($id)
|
||||
: $this->definitions->resolveTagged($id);
|
||||
|
||||
array_walk($arrayOf, function (&$resolved) {
|
||||
$resolved = $this->inflectors->inflect($resolved);
|
||||
});
|
||||
|
||||
return $arrayOf;
|
||||
}
|
||||
|
||||
if ($this->providers->provides($id)) {
|
||||
$this->providers->register($id);
|
||||
|
||||
if (false === $this->definitions->has($id) && false === $this->definitions->hasTag($id)) { // @phpstan-ignore-line
|
||||
throw new ContainerException(sprintf('Service provider lied about providing (%s) service', $id));
|
||||
}
|
||||
|
||||
return $this->resolve($id, $new); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
foreach ($this->delegates as $delegate) {
|
||||
if ($delegate->has($id)) {
|
||||
$resolved = $delegate->get($id);
|
||||
return $this->inflectors->inflect($resolved);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundException(sprintf('Alias (%s) is not being managed by the container or delegates', $id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container;
|
||||
|
||||
interface ContainerAwareInterface
|
||||
{
|
||||
public function getContainer(): DefinitionContainerInterface;
|
||||
public function setContainer(DefinitionContainerInterface $container): ContainerAwareInterface;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container;
|
||||
|
||||
use BadMethodCallException;
|
||||
use League\Container\Exception\ContainerException;
|
||||
|
||||
trait ContainerAwareTrait
|
||||
{
|
||||
/**
|
||||
* @var ?DefinitionContainerInterface
|
||||
*/
|
||||
protected ?DefinitionContainerInterface $container = null;
|
||||
|
||||
public function setContainer(DefinitionContainerInterface $container): ContainerAwareInterface
|
||||
{
|
||||
$this->container = $container;
|
||||
|
||||
if ($this instanceof ContainerAwareInterface) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
throw new BadMethodCallException(sprintf(
|
||||
'Attempt to use (%s) while not implementing (%s)',
|
||||
ContainerAwareTrait::class,
|
||||
ContainerAwareInterface::class
|
||||
));
|
||||
}
|
||||
|
||||
public function getContainer(): DefinitionContainerInterface
|
||||
{
|
||||
if ($this->container instanceof DefinitionContainerInterface) {
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
throw new ContainerException('No container implementation has been set.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Definition;
|
||||
|
||||
use League\Container\Argument\{
|
||||
ArgumentResolverInterface,
|
||||
ArgumentResolverTrait,
|
||||
ArgumentInterface,
|
||||
LiteralArgumentInterface
|
||||
};
|
||||
use League\Container\ContainerAwareTrait;
|
||||
use League\Container\Exception\ContainerException;
|
||||
use Psr\Container\{ContainerExceptionInterface, ContainerInterface, NotFoundExceptionInterface};
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
|
||||
class Definition implements ArgumentResolverInterface, DefinitionInterface
|
||||
{
|
||||
use ArgumentResolverTrait;
|
||||
use ContainerAwareTrait;
|
||||
|
||||
protected mixed $resolved = null;
|
||||
protected array $recursiveCheck = [];
|
||||
|
||||
public function __construct(
|
||||
protected string $id,
|
||||
protected mixed $concrete = null,
|
||||
protected bool $shared = false,
|
||||
protected array $arguments = [],
|
||||
protected array $methods = [],
|
||||
protected array $tags = [],
|
||||
) {
|
||||
$this->setId($this->id);
|
||||
$this->concrete ??= $this->id;
|
||||
}
|
||||
|
||||
public function addTag(string $tag): DefinitionInterface
|
||||
{
|
||||
$this->tags[$tag] = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasTag(string $tag): bool
|
||||
{
|
||||
return isset($this->tags[$tag]);
|
||||
}
|
||||
|
||||
public function setId(string $id): DefinitionInterface
|
||||
{
|
||||
$this->id = static::normaliseAlias($id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return static::normaliseAlias($this->id);
|
||||
}
|
||||
|
||||
public function setAlias(string $id): DefinitionInterface
|
||||
{
|
||||
return $this->setId($id);
|
||||
}
|
||||
|
||||
public function getAlias(): string
|
||||
{
|
||||
return $this->getId();
|
||||
}
|
||||
|
||||
public function setShared(bool $shared = true): DefinitionInterface
|
||||
{
|
||||
$this->shared = $shared;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isShared(): bool
|
||||
{
|
||||
return $this->shared;
|
||||
}
|
||||
|
||||
public function getConcrete(): mixed
|
||||
{
|
||||
return $this->concrete;
|
||||
}
|
||||
|
||||
public function setConcrete(mixed $concrete): DefinitionInterface
|
||||
{
|
||||
$this->concrete = $concrete;
|
||||
$this->resolved = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addArgument(mixed $arg): DefinitionInterface
|
||||
{
|
||||
$this->arguments[] = $arg;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addArguments(array $args): DefinitionInterface
|
||||
{
|
||||
foreach ($args as $arg) {
|
||||
$this->addArgument($arg);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addMethodCall(string $method, array $args = []): DefinitionInterface
|
||||
{
|
||||
$this->methods[] = [
|
||||
'method' => $method,
|
||||
'arguments' => $args
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addMethodCalls(array $methods = []): DefinitionInterface
|
||||
{
|
||||
foreach ($methods as $method => $args) {
|
||||
$this->addMethodCall($method, $args);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function resolve(): mixed
|
||||
{
|
||||
if (null !== $this->resolved && $this->isShared()) {
|
||||
return $this->resolved;
|
||||
}
|
||||
|
||||
return $this->resolveNew();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function resolveNew(): mixed
|
||||
{
|
||||
$concrete = $this->concrete;
|
||||
|
||||
if (is_callable($concrete)) {
|
||||
$concrete = $this->resolveCallable($concrete);
|
||||
}
|
||||
|
||||
if ($concrete instanceof LiteralArgumentInterface) {
|
||||
$this->resolved = $concrete->getValue();
|
||||
return $concrete->getValue();
|
||||
}
|
||||
|
||||
if ($concrete instanceof ArgumentInterface) {
|
||||
$concrete = $concrete->getValue();
|
||||
}
|
||||
|
||||
if (is_string($concrete) && class_exists($concrete)) {
|
||||
$concrete = $this->resolveClass($concrete);
|
||||
}
|
||||
|
||||
if (is_object($concrete)) {
|
||||
$concrete = $this->invokeMethods($concrete);
|
||||
}
|
||||
|
||||
try {
|
||||
$container = $this->getContainer();
|
||||
} catch (ContainerException) {
|
||||
$container = null;
|
||||
}
|
||||
|
||||
if (is_string($concrete)) {
|
||||
if (class_exists($concrete)) {
|
||||
$concrete = $this->resolveClass($concrete);
|
||||
} elseif ($this->getAlias() === $concrete) {
|
||||
return $concrete;
|
||||
}
|
||||
}
|
||||
|
||||
// if we still have a string, try to pull it from the container
|
||||
// this allows for `alias -> alias -> ... -> concrete
|
||||
if (is_string($concrete) && $container instanceof ContainerInterface && $container->has($concrete)) {
|
||||
$this->recursiveCheck[] = $concrete;
|
||||
$concrete = $container->get($concrete);
|
||||
}
|
||||
|
||||
$this->resolved = $concrete;
|
||||
return $concrete;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ReflectionException
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
protected function resolveCallable(callable $concrete): mixed
|
||||
{
|
||||
$resolved = $this->resolveArguments($this->arguments);
|
||||
return call_user_func_array($concrete, $resolved);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @throws ReflectionException
|
||||
* @throws ContainerExceptionInterface
|
||||
*/
|
||||
protected function resolveClass(string $concrete): object
|
||||
{
|
||||
$resolved = $this->resolveArguments($this->arguments);
|
||||
$reflection = new ReflectionClass($concrete);
|
||||
return $reflection->newInstanceArgs($resolved);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ReflectionException
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
protected function invokeMethods(object $instance): object
|
||||
{
|
||||
foreach ($this->methods as $method) {
|
||||
$args = $this->resolveArguments($method['arguments']);
|
||||
$callable = [$instance, $method['method']];
|
||||
call_user_func_array($callable, $args);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public static function normaliseAlias(string $alias): string
|
||||
{
|
||||
return ltrim($alias, "\\");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Definition;
|
||||
|
||||
use Generator;
|
||||
use League\Container\ContainerAwareTrait;
|
||||
use League\Container\Exception\NotFoundException;
|
||||
|
||||
class DefinitionAggregate implements DefinitionAggregateInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
public function __construct(protected array $definitions = [])
|
||||
{
|
||||
$this->definitions = array_filter($this->definitions, static function ($definition) {
|
||||
return ($definition instanceof DefinitionInterface);
|
||||
});
|
||||
}
|
||||
|
||||
public function add(string $id, mixed $definition, bool $overwrite = false): DefinitionInterface
|
||||
{
|
||||
if (true === $overwrite) {
|
||||
$this->remove($id);
|
||||
}
|
||||
|
||||
if (false === ($definition instanceof DefinitionInterface)) {
|
||||
$definition = new Definition($id, $definition);
|
||||
}
|
||||
|
||||
$this->definitions[] = $definition->setAlias($id);
|
||||
|
||||
return $definition;
|
||||
}
|
||||
|
||||
public function addShared(string $id, mixed $definition, bool $overwrite = false): DefinitionInterface
|
||||
{
|
||||
$definition = $this->add($id, $definition, $overwrite);
|
||||
return $definition->setShared(true);
|
||||
}
|
||||
|
||||
public function has(string $id): bool
|
||||
{
|
||||
$id = Definition::normaliseAlias($id);
|
||||
|
||||
foreach ($this as $definition) {
|
||||
if ($id === $definition->getAlias()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasTag(string $tag): bool
|
||||
{
|
||||
foreach ($this as $definition) {
|
||||
if ($definition->hasTag($tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDefinition(string $id): DefinitionInterface
|
||||
{
|
||||
$id = Definition::normaliseAlias($id);
|
||||
|
||||
foreach ($this as $definition) {
|
||||
if ($id === $definition->getAlias()) {
|
||||
return $definition->setContainer($this->getContainer());
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundException(sprintf('Alias (%s) is not being handled as a definition.', $id));
|
||||
}
|
||||
|
||||
public function resolve(string $id): mixed
|
||||
{
|
||||
return $this->getDefinition($id)->resolve();
|
||||
}
|
||||
|
||||
public function resolveNew(string $id): mixed
|
||||
{
|
||||
return $this->getDefinition($id)->resolveNew();
|
||||
}
|
||||
|
||||
public function resolveTagged(string $tag): array
|
||||
{
|
||||
$arrayOf = [];
|
||||
|
||||
foreach ($this as $definition) {
|
||||
if ($definition->hasTag($tag)) {
|
||||
$arrayOf[] = $definition->setContainer($this->getContainer())->resolve();
|
||||
}
|
||||
}
|
||||
|
||||
return $arrayOf;
|
||||
}
|
||||
|
||||
public function resolveTaggedNew(string $tag): array
|
||||
{
|
||||
$arrayOf = [];
|
||||
|
||||
foreach ($this as $definition) {
|
||||
if ($definition->hasTag($tag)) {
|
||||
$arrayOf[] = $definition->setContainer($this->getContainer())->resolveNew();
|
||||
}
|
||||
}
|
||||
|
||||
return $arrayOf;
|
||||
}
|
||||
|
||||
public function remove(string $id): void
|
||||
{
|
||||
$id = Definition::normaliseAlias($id);
|
||||
|
||||
foreach ($this as $key => $definition) {
|
||||
if ($id === $definition->getAlias()) {
|
||||
unset($this->definitions[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getIterator(): Generator
|
||||
{
|
||||
yield from $this->definitions;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Definition;
|
||||
|
||||
use IteratorAggregate;
|
||||
use League\Container\ContainerAwareInterface;
|
||||
|
||||
interface DefinitionAggregateInterface extends ContainerAwareInterface, IteratorAggregate
|
||||
{
|
||||
public function add(string $id, mixed $definition, bool $overwrite = false): DefinitionInterface;
|
||||
public function addShared(string $id, mixed $definition, bool $overwrite = false): DefinitionInterface;
|
||||
public function getDefinition(string $id): DefinitionInterface;
|
||||
public function has(string $id): bool;
|
||||
public function hasTag(string $tag): bool;
|
||||
public function resolve(string $id): mixed;
|
||||
public function resolveNew(string $id): mixed;
|
||||
public function resolveTagged(string $tag): array;
|
||||
public function resolveTaggedNew(string $tag): array;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Definition;
|
||||
|
||||
use League\Container\ContainerAwareInterface;
|
||||
|
||||
interface DefinitionInterface extends ContainerAwareInterface
|
||||
{
|
||||
public function addArgument(mixed $arg): DefinitionInterface;
|
||||
public function addArguments(array $args): DefinitionInterface;
|
||||
public function addMethodCall(string $method, array $args = []): DefinitionInterface;
|
||||
public function addMethodCalls(array $methods = []): DefinitionInterface;
|
||||
public function addTag(string $tag): DefinitionInterface;
|
||||
public function getAlias(): string;
|
||||
public function getConcrete(): mixed;
|
||||
public function hasTag(string $tag): bool;
|
||||
public function isShared(): bool;
|
||||
public function resolve(): mixed;
|
||||
public function resolveNew(): mixed;
|
||||
public function setAlias(string $id): DefinitionInterface;
|
||||
public function setConcrete(mixed $concrete): DefinitionInterface;
|
||||
public function setShared(bool $shared): DefinitionInterface;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container;
|
||||
|
||||
use League\Container\Definition\DefinitionInterface;
|
||||
use League\Container\Inflector\InflectorInterface;
|
||||
use League\Container\ServiceProvider\ServiceProviderInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
interface DefinitionContainerInterface extends ContainerInterface
|
||||
{
|
||||
public function add(string $id, mixed $concrete = null, bool $overwrite = false): DefinitionInterface;
|
||||
public function addServiceProvider(ServiceProviderInterface $provider): self;
|
||||
public function addShared(string $id, mixed $concrete = null, bool $overwrite = false): DefinitionInterface;
|
||||
public function extend(string $id): DefinitionInterface;
|
||||
public function getNew(string $id): mixed;
|
||||
public function inflector(string $type, ?callable $callback = null): InflectorInterface;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Exception;
|
||||
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class ContainerException extends RuntimeException implements ContainerExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Exception;
|
||||
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class NotFoundException extends InvalidArgumentException implements NotFoundExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Inflector;
|
||||
|
||||
use League\Container\Argument\{ArgumentResolverInterface, ArgumentResolverTrait};
|
||||
use League\Container\ContainerAwareTrait;
|
||||
use Psr\Container\{ContainerExceptionInterface, NotFoundExceptionInterface};
|
||||
use ReflectionException;
|
||||
|
||||
class Inflector implements ArgumentResolverInterface, InflectorInterface
|
||||
{
|
||||
use ArgumentResolverTrait;
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var callable|null
|
||||
*/
|
||||
protected $callback;
|
||||
|
||||
protected array $inflected = [];
|
||||
|
||||
public function __construct(
|
||||
protected string $type,
|
||||
?callable $callback = null,
|
||||
protected bool $oncePerMatch = false,
|
||||
protected array $methods = [],
|
||||
protected array $properties = [],
|
||||
) {
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
public function oncePerMatch(): InflectorInterface
|
||||
{
|
||||
$this->oncePerMatch = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function invokeMethod(string $name, array $args): InflectorInterface
|
||||
{
|
||||
$this->methods[$name] = $args;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function invokeMethods(array $methods): InflectorInterface
|
||||
{
|
||||
foreach ($methods as $name => $args) {
|
||||
$this->invokeMethod($name, $args);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws ReflectionException
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function setProperty(string $property, mixed $value): InflectorInterface
|
||||
{
|
||||
$this->properties[$property] = $this->resolveArguments([$value])[0];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ReflectionException
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function setProperties(array $properties): InflectorInterface
|
||||
{
|
||||
foreach ($properties as $property => $value) {
|
||||
$this->setProperty($property, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function inflect(object $object): void
|
||||
{
|
||||
if (true === $this->oncePerMatch && in_array($object, $this->inflected, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$properties = $this->resolveArguments(array_values($this->properties));
|
||||
$properties = array_combine(array_keys($this->properties), $properties);
|
||||
|
||||
// array_combine() can technically return false
|
||||
foreach ($properties ?: [] as $property => $value) {
|
||||
$object->{$property} = $value;
|
||||
}
|
||||
|
||||
foreach ($this->methods as $method => $args) {
|
||||
$args = $this->resolveArguments($args);
|
||||
$callable = [$object, $method];
|
||||
call_user_func_array($callable, $args);
|
||||
}
|
||||
|
||||
if ($this->callback !== null) {
|
||||
call_user_func($this->callback, $object);
|
||||
}
|
||||
|
||||
if (true === $this->oncePerMatch) {
|
||||
$this->inflected[] = $object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Inflector;
|
||||
|
||||
use Generator;
|
||||
use League\Container\ContainerAwareTrait;
|
||||
|
||||
class InflectorAggregate implements InflectorAggregateInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var Inflector[]
|
||||
*/
|
||||
protected array $inflectors = [];
|
||||
|
||||
public function add(string $type, ?callable $callback = null): Inflector
|
||||
{
|
||||
$inflector = new Inflector($type, callback: $callback);
|
||||
$this->inflectors[] = $inflector;
|
||||
return $inflector;
|
||||
}
|
||||
|
||||
public function inflect(mixed $object): mixed
|
||||
{
|
||||
foreach ($this as $inflector) {
|
||||
$type = $inflector->getType();
|
||||
|
||||
if ($object instanceof $type) {
|
||||
$inflector->setContainer($this->getContainer());
|
||||
$inflector->inflect($object);
|
||||
}
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function getIterator(): Generator
|
||||
{
|
||||
yield from $this->inflectors;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Inflector;
|
||||
|
||||
use IteratorAggregate;
|
||||
use League\Container\ContainerAwareInterface;
|
||||
|
||||
interface InflectorAggregateInterface extends ContainerAwareInterface, IteratorAggregate
|
||||
{
|
||||
public function add(string $type, ?callable $callback = null): Inflector;
|
||||
public function inflect(mixed $object): mixed;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\Inflector;
|
||||
|
||||
interface InflectorInterface
|
||||
{
|
||||
public function getType(): string;
|
||||
public function inflect(object $object): void;
|
||||
public function invokeMethod(string $name, array $args): InflectorInterface;
|
||||
public function invokeMethods(array $methods): InflectorInterface;
|
||||
public function setProperties(array $properties): InflectorInterface;
|
||||
public function setProperty(string $property, mixed $value): InflectorInterface;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container;
|
||||
|
||||
use League\Container\Argument\{ArgumentReflectorInterface,
|
||||
ArgumentReflectorTrait,
|
||||
ArgumentResolverInterface,
|
||||
ArgumentResolverTrait};
|
||||
use League\Container\Exception\NotFoundException;
|
||||
use Psr\Container\{ContainerExceptionInterface, ContainerInterface, NotFoundExceptionInterface};
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
|
||||
class ReflectionContainer implements ArgumentReflectorInterface, ArgumentResolverInterface, ContainerInterface
|
||||
{
|
||||
use ArgumentReflectorTrait;
|
||||
use ArgumentResolverTrait;
|
||||
use ContainerAwareTrait;
|
||||
|
||||
public const AUTO_WIRING = 0x01;
|
||||
public const ATTRIBUTE_RESOLUTION = 0x02;
|
||||
|
||||
protected array $cache = [];
|
||||
|
||||
public function __construct(
|
||||
protected bool $cacheResolutions = false,
|
||||
protected int $mode = self::AUTO_WIRING | self::ATTRIBUTE_RESOLUTION
|
||||
) {
|
||||
}
|
||||
|
||||
public function setMode(int $mode): void
|
||||
{
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
public function getMode(): int
|
||||
{
|
||||
return $this->mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function get(string $id, array $args = [])
|
||||
{
|
||||
if (true === $this->cacheResolutions && array_key_exists($id, $this->cache)) {
|
||||
return $this->cache[$id];
|
||||
}
|
||||
|
||||
if (!$this->has($id)) {
|
||||
throw new NotFoundException(
|
||||
sprintf('Alias (%s) is not an existing class and therefore cannot be resolved', $id)
|
||||
);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass($id);
|
||||
$construct = $reflector->getConstructor();
|
||||
|
||||
if ($construct && !$construct->isPublic()) {
|
||||
throw new NotFoundException(
|
||||
sprintf('Alias (%s) has a non-public constructor and therefore cannot be instantiated', $id)
|
||||
);
|
||||
}
|
||||
|
||||
$resolution = $construct === null
|
||||
? new $id()
|
||||
: $reflector->newInstanceArgs($this->reflectArguments($construct, $args))
|
||||
;
|
||||
|
||||
if ($this->cacheResolutions === true) {
|
||||
$this->cache[$id] = $resolution;
|
||||
}
|
||||
|
||||
return $resolution;
|
||||
}
|
||||
|
||||
public function has(string $id): bool
|
||||
{
|
||||
return class_exists($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function call(callable $callable, array $args = []): mixed
|
||||
{
|
||||
if (is_string($callable) && str_contains($callable, '::')) {
|
||||
$callable = explode('::', $callable);
|
||||
}
|
||||
|
||||
if (is_array($callable)) {
|
||||
if (is_string($callable[0])) {
|
||||
// if we have a definition container, try that first, otherwise, reflect
|
||||
try {
|
||||
$callable[0] = $this->getContainer()->get($callable[0]);
|
||||
} catch (ContainerExceptionInterface | NotFoundExceptionInterface) {
|
||||
$callable[0] = $this->get($callable[0]);
|
||||
}
|
||||
}
|
||||
|
||||
$reflection = new ReflectionMethod($callable[0], $callable[1]);
|
||||
|
||||
if ($reflection->isStatic()) {
|
||||
$callable[0] = null;
|
||||
}
|
||||
|
||||
return $reflection->invokeArgs($callable[0], $this->reflectArguments($reflection, $args));
|
||||
}
|
||||
|
||||
if (is_object($callable) && method_exists($callable, '__invoke')) {
|
||||
/** @var object $callable */
|
||||
$reflection = new ReflectionMethod($callable, '__invoke');
|
||||
return $reflection->invokeArgs($callable, $this->reflectArguments($reflection, $args));
|
||||
}
|
||||
|
||||
$reflection = new ReflectionFunction($callable(...));
|
||||
return $reflection->invokeArgs($this->reflectArguments($reflection, $args));
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\ServiceProvider;
|
||||
|
||||
use League\Container\ContainerAwareTrait;
|
||||
|
||||
abstract class AbstractServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
protected string $identifier;
|
||||
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
if (empty($this->identifier)) {
|
||||
$this->identifier = get_class($this);
|
||||
}
|
||||
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
public function setIdentifier(string $id): ServiceProviderInterface
|
||||
{
|
||||
$this->identifier = $id;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\ServiceProvider;
|
||||
|
||||
interface BootableServiceProviderInterface extends ServiceProviderInterface
|
||||
{
|
||||
/**
|
||||
* Method will be invoked on registration of a service provider implementing
|
||||
* this interface. Provides ability for eager loading of Service Providers.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(): void;
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\ServiceProvider;
|
||||
|
||||
use Generator;
|
||||
use League\Container\ContainerAwareTrait;
|
||||
use League\Container\Exception\ContainerException;
|
||||
|
||||
class ServiceProviderAggregate implements ServiceProviderAggregateInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var ServiceProviderInterface[]
|
||||
*/
|
||||
protected array $providers = [];
|
||||
protected array $registered = [];
|
||||
|
||||
public function add(ServiceProviderInterface $provider): ServiceProviderAggregateInterface
|
||||
{
|
||||
if (in_array($provider, $this->providers, true)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$provider->setContainer($this->getContainer());
|
||||
|
||||
if ($provider instanceof BootableServiceProviderInterface) {
|
||||
$provider->boot();
|
||||
}
|
||||
|
||||
$this->providers[] = $provider;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function provides(string $id): bool
|
||||
{
|
||||
foreach ($this as $provider) {
|
||||
if ($provider->provides($id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getIterator(): Generator
|
||||
{
|
||||
yield from $this->providers;
|
||||
}
|
||||
|
||||
public function register(string $service): void
|
||||
{
|
||||
if (false === $this->provides($service)) {
|
||||
throw new ContainerException(
|
||||
sprintf('(%s) is not provided by a service provider', $service)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this as $provider) {
|
||||
if (in_array($provider->getIdentifier(), $this->registered, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($provider->provides($service)) {
|
||||
$provider->register();
|
||||
$this->registered[] = $provider->getIdentifier();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\ServiceProvider;
|
||||
|
||||
use IteratorAggregate;
|
||||
use League\Container\ContainerAwareInterface;
|
||||
|
||||
interface ServiceProviderAggregateInterface extends ContainerAwareInterface, IteratorAggregate
|
||||
{
|
||||
public function add(ServiceProviderInterface $provider): ServiceProviderAggregateInterface;
|
||||
public function provides(string $id): bool;
|
||||
public function register(string $service): void;
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace League\Container\ServiceProvider;
|
||||
|
||||
use League\Container\ContainerAwareInterface;
|
||||
|
||||
interface ServiceProviderInterface extends ContainerAwareInterface
|
||||
{
|
||||
public function getIdentifier(): string;
|
||||
public function provides(string $id): bool;
|
||||
public function register(): void;
|
||||
public function setIdentifier(string $id): ServiceProviderInterface;
|
||||
}
|
||||
Reference in New Issue
Block a user