diff --git a/CHANGELOG.md b/CHANGELOG.md
index e78d5903..3c567c36 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,10 @@
All notable changes to this project will be documented in this file.
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [v3.0.0](https://github.com/cspray/annotated-container/tree/v3.0.0) - 2024-12-15
+## [v3.0.0](https://github.com/cspray/annotated-container/tree/v3.0.0)
The v3.0 release represents a substantial improvement in several areas of the project, but also includes several backwards compatability breaks; particularly if you were using container caching, providing Definitions via a DefinitionProvider, or utilizing the Bootstrap observer system.
@@ -27,7 +28,6 @@ The v3.0 release represents a substantial improvement in several areas of the pr
- Updated the version of `nikic/php-parser` used to 5.3.
- All interactions with the `Bootstrap` object are available through named, static methods. The `Bootstrap` constructor is now private and the `new` construct cannot be used. Generally speaking, you should make use of the new `Bootstrap::fromAnnotatedContainerConventions` method.
- Container factory to use is no longer implicit and must be explicitly provided during your bootstrapping.
-- `AnnotatedContainer::getBackingContainer` was renamed to `backingContainer`.
- All Definitions that had a nullable `attribute` method were changed to require a non-null Attribute value. Please see the ADR "Require Definitions To Provide Attribute" for more information.
- Improved the `XmlContainerDefinitionSerializer` and corresponding XSD to remove redundant information being stored in the Attribute.
- The `AnnotatedContainerVersion` class was updated to use the `composer/runtime` dependency directly, instead of using `ocramius/package-versions`.
@@ -35,6 +35,7 @@ The v3.0 release represents a substantial improvement in several areas of the pr
- Several implementations had their `get` prefix removed, as it was redundant and not necessary.
- Moved test code that was in `fixture_src` under `test/Fixture`.
- When utilizing the `composer.json` configuration for configuring third-party initializers, more checks are made to ensure that a valid data structure has been passed. Previously, this had undefined behavior when invalid data types or values were present.
+- Refactored the `ContainerFactoryOptions` interface into a value object.
### Fixed
@@ -58,6 +59,8 @@ The v3.0 release represents a substantial improvement in several areas of the pr
- `Cspray\AnnotatedContainer\Definition\ConfigurationDefinitionBuilder`
- `Cspray\AnnotatedContainer\Attribute\ConfigurationAttribute`
- `Cspray\AnnotatedContainer\Attribute\Configuration`
+- Removed the `Cspray\AnnotatedContainer\ContainerFactory\ContainerFactoryOptionsBuilder` object. Please use the `ContainerFactoryOptions` value object directly instead.
+- Removed the `Cspray\AnnotatedContainer\StaticAnalysis\ContainerDefinitionAnalysisOptionsBuilder`. Please use the `ContainerFactoryOptions` value object directly instead.
- Removed built-in file caching used in bootstrapping. This functionality is replaced by implementing your own `ContainerDefinitionCache` implementation, or explicitly using the `FileBackedContainerDefinitionCache` provided out-of-the-box.
- Removed built-in logging used throughout the library. This functionality will be replaced at a future date with a set of listeners that will log the same information. These listeners will require installing a separate repo and explicitly opting in.
- Removed the following Composer dependencies:
@@ -71,7 +74,7 @@ The v3.0 release represents a substantial improvement in several areas of the pr
### Added
- Added the ability to inject a collection of services as an array or a custom collection by passing an implementation of `Cspray\AnnotatedContainer\ContainerFactory\ListOf` to an `#[Inject]` attribute.
-- Added `Cspray\AnnotatedContainer\ContainerFactory\ListOfAsArray` implementation of to allow implementing a collection of services as an array out-of-the-box
+- Added `Cspray\AnnotatedContainer\ContainerFactory\ListOfAsArray` implementation of to allow implementing a collection of services as an array out-of-the-box
### Deprecated
@@ -117,14 +120,14 @@ In v3 caching functionality is drastically improved and much more control is pro
### Changed
- Changed static analysis step to no longer throw an error if a ServiceDelegate is encountered without an explicitly
-defined Service. Now, a ServiceDefinition will be implicitly added as if the corresponding class was added with all
-default parameters using the functional API.
+ defined Service. Now, a ServiceDefinition will be implicitly added as if the corresponding class was added with all
+ default parameters using the functional API.
### Deprecated
- All observers have been deprecated. They will be replaced in 3.0.0. Please see our ADR document for more details.
-- All implementations in Cspray\AnnotatedContainer\Profiles have been deprecated. They will be replaced with a single
-value object in 3.0.0. Please see our ADR document for more details.
+- All implementations in Cspray\AnnotatedContainer\Profiles have been deprecated. They will be replaced with a single
+ value object in 3.0.0. Please see our ADR document for more details.
## [v2.2.0](https://github.com/cspray/annotated-container/tree/v2.2.0) - 2023-05-29
@@ -330,7 +333,7 @@ This release only deprecates code constructs replaced in v2.
## [v1.3.0](https://github.com/cspray/annotated-container/tree/v1.3.0) - 2022-08-06
-### Added
+### Added
- Added an event system for programmatic access to the ContainerDefinition and Container before and after each is created.
@@ -415,7 +418,7 @@ This release only deprecates code constructs replaced in v2.
### Removed
- Removed the ability to mark a `#[Service]` as shared or not. All services are shared by default, and you cannot "unshare" a service. This functionality has a lot of odd behavior around it and other mechanisms should be used to gain this functionality.
-- Removed the `AnnotatedTarget`, `AnnotatatedTargetParser`, and `StaticAnalysisAnnotatedTargetParser`.
+- Removed the `AnnotatedTarget`, `AnnotatatedTargetParser`, and `StaticAnalysisAnnotatedTargetParser`.
### Changed
@@ -432,7 +435,7 @@ This release only deprecates code constructs replaced in v2.
- A new `fixture_src/` directory that stores example source code used for the automated test suite.
- Several improvements to the way that Fixtures are handled in the test suite such that the code examples in `fixture_src/` have a first-class representation in the test suite through the `Cspray\AnnotatedContainerFixture\Fixtures` class.
-- Implementations for
+- Implementations for
### Changed
@@ -490,7 +493,7 @@ This release only deprecates code constructs replaced in v2.
- An error in the README documentation referencing an incorrect variable.
- Directory paths in all tests point to new directory structure.
- A dev-only dependency, `mikey179/vfsStream` was inadvertently included in the `require` section. This dependency is now properly a `require-dev` dependency.
-- Arguments passed to Attributes better differentiates between compile and runtime values by introducing an AnnotationValue. Many
+- Arguments passed to Attributes better differentiates between compile and runtime values by introducing an AnnotationValue. Many
### Removed
@@ -532,7 +535,7 @@ This release only deprecates code constructs replaced in v2.
on a `Service` constructor or `ServicePrepare` method.
- `InjectorDefinitionCompiler` to turn annotated PHP source code in a directory into an `InjectorDefinition` which defines how to construct
the corresponding `Injector`. An implementation using PHP-Parser is also provided.
-- `InjectorFactory` to take an `InjectorDefinition` and turn it into a DI container. An implementation that
+- `InjectorFactory` to take an `InjectorDefinition` and turn it into a DI container. An implementation that
wires an Auryn `Injector` is also provided.
diff --git a/README.md b/README.md
index 4685b6dc..961cea7a 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,8 @@
[](https://github.com/cspray/annotated-container/actions/workflows/php.yml)
-A Dependency Injection framework for creating an autowired, feature-rich, [PSR-11](https://www.php-fig.org/psr/psr-11/) compatible Container using PHP 8 Attributes!
+A Dependency Injection framework for creating an autowired, feature-rich, [PSR-11](https://www.php-fig.org/psr/psr-11/) compatible Container using
+PHP 8 Attributes!
- Designate an interface as a Service and easily configure which concrete implementations to use
- Delegate service construction to a factory
@@ -15,7 +16,9 @@ A Dependency Injection framework for creating an autowired, feature-rich, [PSR-1
## Quick Start
-This quick start is intended to get you familiar with Annotated Container's core functionality and get a working Container created. First, a simple example showing how an interface can be aliased to a concrete Service. After that we'll show you how to get a Container to create the Service.
+This quick start is intended to get you familiar with Annotated Container's core functionality and get a working
+Container created. First, a simple example showing how an interface can be aliased to a concrete Service. After that
+we'll show you how to get a Container to create the Service.
### Code Example
@@ -54,19 +57,28 @@ This example is built upon in the docs. Check out the tutorials for more example
### Bootstrapping Your Container
-Annotated Container ships with a built-in CLI tool to easily create a configuration detailing how to build your Container and a corresponding `Cspray\AnnotatedContainer\Bootstrap\Bootstrap` implementation to create your Container using that configuration. It is highly recommended to use the provided tooling to create your Container.
+Annotated Container ships with a built-in CLI tool to easily create a configuration detailing how to build your
+Container and a corresponding `Cspray\AnnotatedContainer\Bootstrap\Bootstrap` implementation to create your Container
+using that configuration. It is highly recommended to use the provided tooling to create your Container.
-> The CLI tool offers extensive documentation detailing how to run commands and what options are available. If you're ever looking for more info run: `./vendor/bin/annotated-container help`
+> The CLI tool offers extensive documentation detailing how to run commands and what options are available. If you're
+> ever looking for more info run: `./vendor/bin/annotated-container help`
-The first step is to create the configuration. By default, the tooling will look at your `composer.json` to determine what directories to scan and create a directory that the ContainerDefinition can be cached in. Run the following command to complete this step.
+The first step is to create the configuration. By default, the tooling will look at your `composer.json` to determine
+what directories to scan and create a directory that the ContainerDefinition can be cached in. Run the following
+command to complete this step.
```
./vendor/bin/annotated-container init
```
-The configuration file will be created in the root of your project named "annotated-container.xml". The cache directory will also be created in the root of your project named ".annotated-container-cache". Check out the command's help documentation for available options, including how to customize these values.
+The configuration file will be created in the root of your project named "annotated-container.xml". The cache directory
+will also be created in the root of your project named ".annotated-container-cache". Check out the command's help
+documentation for available options, including how to customize these values.
-Be sure to review the generated configuration! A "normal" Composer setup might result in a configuration that looks like the following. If there are any directories that should be scanned but aren't listed in `` be sure to include them. Conversely, if there are directories included that _shouldn't_ be scanned be sure to remove them.
+Be sure to review the generated configuration! A "normal" Composer setup might result in a configuration that looks
+like the following. If there are any directories that should be scanned but aren't listed in `` be
+sure to include them. Conversely, if there are directories included that _shouldn't_ be scanned be sure to remove them.
```xml
@@ -89,10 +101,7 @@ have `php-di/php-di` installed.
// app bootstrap in __DIR__ . '/app.php'
require __DIR__ . '/vendor/autoload.php';
-use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;
-use Cspray\AnnotatedContainer\Event\Emitter;
-use Cspray\AnnotatedContainer\Profiles;
-use Cspray\AnnotatedContainer\ContainerFactory\PhpDiContainerFactory;
+use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;use Cspray\AnnotatedContainer\ContainerFactory\PhpDi\PhpDiContainerFactory;use Cspray\AnnotatedContainer\Event\Emitter;use Cspray\AnnotatedContainer\Profiles;
$emitter = new Emitter();
@@ -122,7 +131,9 @@ composer require cspray/annotated-container
### Choose a Backing Container
-AnnotatedContainer does not provide any of the actual Container functionality. We provide Attributes and definition objects that can determine how actual implementations are intended to be setup. AnnotatedContainer currently supports the following backing Containers:
+AnnotatedContainer does not provide any of the actual Container functionality. We provide Attributes and definition
+objects that can determine how actual implementations are intended to be setup. AnnotatedContainer currently supports
+the following backing Containers:
#### [rdlowrey/auryn](https://github.com/rdlowrey/auryn)
@@ -144,13 +155,20 @@ composer require illuminate/container
## Documentation
-This library is thoroughly documented in-repo under the `/docs` directory. The documentation is split into three parts; Tutorials, How Tos, and References.
+This library is thoroughly documented in-repo under the `/docs` directory. The documentation is split into three parts;
+Tutorials, How Tos, and References.
-**Tutorials** are where you'll want to start. It'll expand on the examples in the "Quick Start" and teach you how to do most of the things you'll want to do with the library. This documentation tends to split the difference between the amount of code and the amount of explanation.
+**Tutorials** are where you'll want to start. It'll expand on the examples in the "Quick Start" and teach you how to do
+most of the things you'll want to do with the library. This documentation tends to split the difference between the
+amount of code and the amount of explanation.
-**How Tos** are where you'll go to get step-by-step guides on how to achieve specific functionality. These documents tend to be more code and less explanation. We assume you've gotten an understanding of the library and have questions on how to do something beyond the "normal" use cases.
+**How Tos** are where you'll go to get step-by-step guides on how to achieve specific functionality. These documents
+tend to be more code and less explanation. We assume you've gotten an understanding of the library and have questions
+on how to do something beyond the "normal" use cases.
-**References** are where you can get into the real internal, technical workings of the library. List of APIs and more technically-explicit documentation can be found here. References may be a lot of code, a lot of explanation, or a split between the two depending on the context.
+**References** are where you can get into the real internal, technical workings of the library. List of APIs and more
+technically-explicit documentation can be found here. References may be a lot of code, a lot of explanation, or a split
+between the two depending on the context.
## Roadmap
diff --git a/docs/README.md b/docs/README.md
index 7fc226d0..af2a7195 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,10 @@
-# AnnotatedContainer Docs Table of Contents
+# AnnotatedContainer Docs
-## Tutorials
+
+
+## Table of Contents
+
+### Tutorials
1. [Getting Started](./tutorials/01-getting-started.md)
2. [Alias Resolution with Profiles](./tutorials/02-alias-resolution-with-profiles.md)
@@ -8,16 +12,17 @@
4. [Using Service Factories](./tutorials/04-using-service-factories.md)
5. [Calling Post Construct Methods](./tutorials/05-calling-post-construct-methods.md)
6. [Injecting Scalar Values](./tutorials/06-injecting-scalar-values.md)
-7. [Autowire Aware Factory](./tutorials/08-autowire-aware-factory.md)
-8. [Autowire aware Invoker](./tutorials/09-autowire-aware-invoker.md)
-9. [Annotated Container Observer](./tutorials/10-annotated-container-observers.md)
+7. [Autowire Aware Factory](tutorials/07-autowire-aware-factory.md)
+8. [Autowire aware Invoker](tutorials/08-autowire-aware-invoker.md)
+9. [Annotated Container Observer](./tutorials/09-annotated-container-events)
-## How To
+### How To
1. [Add Third-Party Services](./how-to/01-add-third-party-services.md)
2. [Bootstrap Your Container](./how-to/02-bootstrap-your-container.md)
-## References
+### References
1. [Attributes List](./references/01-attributes-list.md)
-2. [Functional API](./references/02-functional-api.md)
\ No newline at end of file
+2. [Functional API](./references/02-functional-api.md)
+3. [Event Listeners](./references/03-event-listeners.md)
\ No newline at end of file
diff --git a/docs/how-to/01-add-third-party-services.md b/docs/how-to/01-add-third-party-services.md
index ac65328b..e1852ff9 100644
--- a/docs/how-to/01-add-third-party-services.md
+++ b/docs/how-to/01-add-third-party-services.md
@@ -2,15 +2,53 @@
It is very likely that you'll need to add some service to the Container that can't be annotated. AnnotatedContainer offers a set of functions to easily add third-party services with all the feature-parity and functionality available to annotated code. This guide goes through a step-by-step guide on how to integrate the popular [Monolog](https://github.com/Seldaek/monolog) library with [PSR-3](https://www.php-fig.org/psr/psr-3/) services.
+Starting with Annotated Container 2.3.0, new functionality was added that allows much easier, implicit setup of third-party services. The "Implicit Setup", detailed below, is the preferred method of adding third-party services to your container. The "Explicit Setup" details what was the documented approach for versions prior to 2.3. It also uses an approach that does not rely on Attributes of any kind. If you're using Annotated Container without Attributes, this is the preferred approach for your use case.
+
> This guide assumes a basic understanding on how to interact with this library. If you're unsure of something we discuss here it is recommended you checkout the rest of the /docs/tutorials section.
+
+## Implicit Setup
+
+## Step 1 - Install PSR-3 and Monolog
+
+```shell
+composer require monolog/monolog psr/log
+```
+
+## Step 2 - Create Factory and Assign ServiceDelegate
+
+```php
+pushHandler(new StreamHandler('php://stdout'));
+
+ return $log;
+ }
+
+}
+```
+
+This is all that's required for the implicit setup. When we encounter a `#[ServiceDelegate]` for a type that has not been defined as a service, we add the service implicitly and assign the appropriate factory method for creating that service.
+
+## Explicit Setup
+
## Step 1 - Install PSR-3 and Monolog
```shell
composer require monolog/monolog psr/log
```
-## Step 2 - Create a Service Factory
+## Step 2 - Create Factory
```php
addServiceDefinition(service($loggerType = objectType(LoggerInterface::class)));
+ $context->addServiceDefinition(
+ service(types()->class(LoggerInterface::class))
+ );
$context->addServiceDelegateDefinition(
- serviceDelegate(objectType(MonologLoggerFactory::class), 'createLogger')
+ serviceDelegate(
+ types()->class(MonologLoggerFactory::class), 'createLogger'
+ )
);
$context->addServicePrepareDefinition(
servicePrepare(
- objectType(LoggerAwareInterface::class),
- 'setLogger'
+ types()->class(LoggerAwareInterface::class), 'setLogger'
)
);
}
@@ -66,7 +107,9 @@ class ThirdPartyServicesProvider implements DefinitionProvider {
```xml
-
+src
@@ -88,7 +131,10 @@ use Psr\Log\LoggerInterface;
use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;
use Cspray\AnnotatedContainer\Event\Emitter;
-$container = Bootstrap::from(new Emitter())->bootstrapContainer();
+$container = Bootstrap::fromAnnotatedContainerConventions(
+ new YourContainerFactory(),
+ new Emitter()
+)->bootstrapContainer();
```
Now, your PSR Logger will be created through a factory. Any services can inject a `LoggerInterface` directly in the constructor, preferred, or implement the `LoggerAwareInterface` to have it injected automatically after construction.
\ No newline at end of file
diff --git a/docs/how-to/02-bootstrap-your-container.md b/docs/how-to/02-bootstrap-your-container.md
index a6fd2892..5a913484 100644
--- a/docs/how-to/02-bootstrap-your-container.md
+++ b/docs/how-to/02-bootstrap-your-container.md
@@ -1,20 +1,22 @@
# Bootstrap Your Container
-As Annotated Container has added more and more functionality the bootstrapping it requires has necessarily grown. It is possible to get up and running without using the provided tooling, but we highly recommend using the CLI tool and corresponding `Cspray\AnnotatedContainer\Bootstrap` to create your Container. This document details how to take advantage of Annotated Container's functionality using this tooling.
+As Annotated Container has added more and more functionality, the bootstrapping it requires has necessarily grown. It is possible to get up and running without using the provided tooling, but we highly recommend using the CLI tool and corresponding `Cspray\AnnotatedContainer\Bootstrap\Bootstrap` to create your Container. This document details how to take advantage of Annotated Container's functionality using this tooling.
## Step 1 - Init Your Configuration
-The first step is to create a configuration file that details how Annotated Container should bootstrap itself. As long as you have a `composer.json` in your project's root directory, and it defines at least one directory with a PSR-4 or PSR-0 namespace then the tooling can figure out which directories to scan. Run the following shell command:
+The first step is to create a configuration file that details how Annotated Container should bootstrap itself. As long as you have a `composer.json` in your project's root directory, and it defines at least one directory with a PSR-4 or PSR-0 namespace, then the tooling can figure out which directories to scan. Run the following shell command:
```shell
./vendor/bin/annotated-container init
```
-If successful you'll get a configuration file named `annotated-container.xml` in the root of your project. In most setups it'll look something like this:
+If successful, you'll get a configuration file named `annotated-container.xml` in the root of your project. In most setups it'll look something like this:
```xml
-
+src
@@ -24,13 +26,13 @@ If successful you'll get a configuration file named `annotated-container.xml` in
```
-The most important, and the only thing that's actually required, is to define at least 1 source directory to scan. It should be noted that **all** directories autoloaded in your `composer.json` will be scanned, including any `autoload-dev` entries. If this is not desired, be sure to remove these directories after the configuration is generated.
+The only required configuration is to define at least 1 source directory to scan. It should be noted that **all** directories autoloaded in your `composer.json` will be scanned, including any `autoload-dev` entries. If this is not desired, be sure to remove these directories after the configuration is generated.
-The rest of this guide will add new elements to this configuration. The steps below are optional, if you don't require any "bells & whistles" skip to Step 4.
+The rest of this guide will add new elements to this configuration. The steps below are optional, if you don't require any "bells & whistles" skip to Step 5.
## Step 2 - Setup Third Party Services (optional)
-To define services that can't be annotated you can make use of a `Cspray\AnnotatedContainer\StaticAnalysis\DefinitionProvider` implementation. Implementing this interface allows you to use the [functional API](../references/03-functional-api.md) to augment your ContainerDefinition without using Attributes. Out-of-the-box, it is expected `DefinitionProvider` implementations will have a zero-argument constructor. Later on in this document I will discuss ways that you can override construction if your implementation has dependencies. Primarily this should be used to integrate third-party libraries that can't have Attributes assigned to them.
+To define services that can't be annotated, you can make use of a `Cspray\AnnotatedContainer\StaticAnalysis\DefinitionProvider` implementation. Implementing this interface allows you to use the [functional API](../references/03-functional-api.md) to augment your ContainerDefinition without using Attributes. Out-of-the-box, it is expected `DefinitionProvider` implementations will have a zero-argument constructor. Later on in this document I will discuss ways that you can override construction if your implementation has dependencies. Primarily this should be used to integrate third-party libraries that can't have Attributes assigned to them.
Somewhere in your source code:
@@ -55,7 +57,9 @@ Now, upgrade the configuration to let bootstrapping know which class to use.
```xml
-
+src
@@ -70,7 +74,7 @@ Now, upgrade the configuration to let bootstrapping know which class to use.
### Step 3 - Provide your custom ParameterStore (optional)
-In any sufficiently large enough application you'll probably want to take advantage of parameter stores to have complete programmatic control over what non-service values get injected. You can define a list of ParameterStore implementations that should be added during bootstrapping. Out-of-the-box, it is expected that these implementations will have a no argument constructor. Later in this document I'll go over how to override construction of these implementations if they require dependencies.
+In any sufficiently large enough application you'll probably want to take advantage of parameter stores to have complete programmatic control over what non-service values get injected. You can define a list of ParameterStore implementations that should be added during bootstrapping. Out-of-the-box, it is expected that these implementations will have a no argument constructor. Later in this document, I'll go over how to override construction of these implementations if they require dependencies.
Somewhere in your source code:
@@ -80,10 +84,12 @@ Somewhere in your source code:
namespace Acme\Demo;
use Cspray\Annotatedcontainer\ContainerFactory\ParameterStore;
+use Cspray\AnnotatedContainer\Reflection\Type;
+use Cspray\AnnotatedContainer\Reflection\TypeUnion;
+use Cspray\AnnotatedContainer\Reflection\TypeIntersect;
final class MyCustomParameterStore implements ParameterStore {
-
public function getName() : string {
return 'my-store';
}
@@ -97,10 +103,11 @@ final class MyCustomParameterStore implements ParameterStore {
Next, update your configuration.
-
```xml
-
+src
@@ -113,34 +120,55 @@ Next, update your configuration.
```
-### Step 4 - Create Your Container
-Before completing this step go put some Attributes on the services in your codebase!
+### Step 4 - Register your Listeners (optional)
+
+Starting with Annotated Container v3, the previously used Observer system was removed and replaced with a more complete, advanced Event system. Pretty much everything that Annotated Container does is covered by an Event and corresponding Listener. For a complete list of all available Listeners, check out the interfaces available in `Cspray\AnnotatedContainer\Event\Listener`. You might also be interested in extending the `Cspray\AnnotatedContainer\Bootstrap\Listener\ServiceWiringListener`.
-Now that the configuration file has been modified, and you've attributed your codebase, to fit your needs you can create your container! If you're using only out-of-the-box functionality this can be done with the following code snippet.
+Listeners allow you to log information, do complex dependency wiring, and programmatically respond when things happen within the lifecycle of Annotated Container. You can define a list of these Listeners in the configuration, that will automatically be registered with the Emitter. Out-of-the-box, it is expected these implementations will have a no argument constructor. Later in this document, I'll go over ways to customize the construction of your Listeners.
+
+In our example, we're going to implement the `Cspray\AnnotatedContainer\Event\Listener\Bootstrap\BeforeBoostrap` Listener. Somewhere in your source code:
```php
bootstrapContainer();
+}
```
-It is important in this code that you add whatever Listeners might be appropriate to the `$emitter` using `Emitter::addListener`. This includes any Listeners that you might be using from third-party libraries. There is currently no plans to allow Listeners to be defined through configuration and MUST be added as part of your bootstrapping. The Emitter is one of the few pieces of Bootstrapping that is ALWAYS required. It cannot be stressed enough how important setting up your Listeners are in this section of your code.
+```xml
+
+
+
+
+ src
+ tests
+
+
+
+ Acme\Demo\DemoListener
+
+
+```
-You have the ability to control specific aspects of the Bootstrapping process by providing different arguments to the `bootstrapContainer` method, using different static constructor methods, or adjust the optional parameters to `Bootstrap::fromMinimalSetup`.
+### Step 5 - Create Your Container
-#### Specifying Profiles
+Before completing this step go put some Attributes on the services in your codebase!
-The first argument, `$profiles`, passed to `bootstrapContainer` should be an instance of `Cspray\AnnotatedContainer\Profiles`. This value object has a variety of static constructor methods on it that allow creating an instance with the appropriate values for your use case. If you don't provide any instance of this value object the active profiles will be `['default']`. If you specify your own `Profiles` instance it is HIGHLY RECOMMENDED you included the `default` profile. Otherwise, it is highly expected that your Container will not be wired correctly.
+Now that the configuration file has been modified, and you've attributed your codebase, you can create your container! If you're using only out-of-the-box functionality this can be done with the following code snippet.
```php
bootstrapContainer(Profiles::fromList(['default', 'prod']));
+$container = Bootstrap::fromAnnotatedContainerConventions(
+ // replace this with the ContainerFactory implementation
+ // appropriate for your use case
+ new YourContainerFactory(),
+)->bootstrapContainer();
```
-#### Changing the Configuration File
+You have the ability to control specific aspects of the Bootstrapping process by providing different arguments to the `bootstrapContainer` method, using different static constructor methods, or adjust the optional parameters to `Bootstrap::fromAnnotatedContainerConventions`.
+
+#### Specifying Profiles
-Perhaps you didn't name your configuration file the default, it is recommended you do so but perhaps there's good reasons to change it. You can pass the second argument, `$configurationFile`, to `bootstrapContainer` that defines the name of the configuration file. If you don't pass any arguments the default value `annotated-container.xml` will be used.
+The only argument, `$profiles`, passed to `bootstrapContainer` should be an instance of `Cspray\AnnotatedContainer\Profiles`. This value object has a variety of static constructor methods on it that allow creating an instance with the appropriate values for your use case. If you don't provide any instance of this value object the active profiles will be `['default']`. If you specify your own `Profiles` instance it is HIGHLY RECOMMENDED you include the `default` profile. Otherwise, it is highly expected that your Container will not be wired correctly.
```php
bootstrapContainer(bootstrappingConfigurationProvider: new XmlBootstrappingConfigurationProvider('my-container.xml'));
+$container = Bootstrap::fromAnnotatedContainerConventions(
+ new YourContainerFactory(),
+)->bootstrapContainer(Profiles::fromList(['default', 'prod']));
```
-If you require a configuration that is not the default XML files, you can implement your own `Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfigurationProvider` instead.
+#### Providing Your Own Configuration
+
+It is possible for you to provide your own `BootstrappingConfiguration`, in case you don't want to use the XML config that comes out-of-the-box. Or, you may have moved the XML config file to a new location, outside your root directory. In these situations, you'll want to provide your own configuration object.
+
+If you provide your own Configuration, you can no longer make use of Annotated Container conventions. You'll need to use the `Bootstrap::fromCompleteSetup` method, which also requires you to provide a `BootstrappingDirectoryResolver`.
+
+```php
+bootstrapContainer();
+```
#### Constructing DefinitionProvider
-There might be dependencies you need to determine what third-party services should be included in your `DefinitionProvider` implementations. If so, there's a `Cspray\AnnotatedContainer\Bootstrap\DefinitionProviderFactory` interface that you can implement and then pass that instance to the `Bootstrap::minimalSetup()` method.
+There might be dependencies you need to determine what third-party services should be included in your `DefinitionProvider` implementations. If so, there's a `Cspray\AnnotatedContainer\Bootstrap\DefinitionProviderFactory` interface that you can implement and then pass that instance to the `Bootstrap::fromAnnotatedContainerConventions()` method.
```php
bootstrapContainer();
+)->bootstrapContainer();
```
#### Constructing ParameterStore
-The custom `ParameterStore` implementations you use might require some dependency to gather the appropriate values. In this case, the `Cspray\AnnotatedContainer\Bootstrap\ParameterStoreFactory` interface can be implemented and passed to the `$parameterStoreFactory` construct argument.
+The custom `ParameterStore` implementations you use might require some dependency to gather the appropriate values. In this case, the `Cspray\AnnotatedContainer\Bootstrap\ParameterStoreFactory` interface can be implemented and passed to the `Bootstrap::fromAnnotatedContainerConventions()` method.
```php
bootstrapContainer();
+)->bootstrapContainer();
+```
+
+#### Constructing Listeners
+
+The Listeners you register in your configuration might require some dependency. In this case, the `Cspray\AnnotatedContainer\Bootstrap\ListenerFactory` interface can be implemented and passed to the `Bootstrap::fromAnnotatedContainerConventions()` method.
+
+
+```php
+bootstrapContainer();
```
#### Changing Resolved Paths
-By default, boostrapping expects all the path fragments in your configuration to be in the root of your project. You can have explicit control over which absolute path is used by implementing a `Cspray\AnnotatedContainer\Bootstrap\BootstrappingDirectoryResolver`. You'll need to use the `Bootstrap::fromCompleteSetup` and be prepared to provide more dependencies as well. In our code example we use the default, provided implementations, but you can use whatever implementation is appropriate.
+By default, bootstrapping expects all the path fragments in your configuration to be in the root of your project. You can have explicit control over which absolute path is used by implementing a `Cspray\AnnotatedContainer\Bootstrap\BootstrappingDirectoryResolver`. You'll need to use the `Bootstrap::fromCompleteSetup` and be prepared to provide more dependencies as well. In our code example we use the default, provided implementations, but you can use whatever implementation is appropriate.
```php
bootstrapContainer();
```
@@ -245,25 +331,42 @@ $container = Bootstrap::fromCompleteSetup(
The static analysis portion of Annotated Container can, like most static analysis tools, be relatively time-consuming. In PHP applications that act as long-running processes, the type this maintainer tends to develop using Annotated Container, this cost is negligible. It happens just 1 time and is just a small part of the initial startup costs. However, in traditional PHP applications that only live for the length of the request this can be costly. In this situation, it is recommended you configure your bootstrap to cache the ContainerDefinition.
-Setting up caching is something that you must explicitly opt into during your bootstrapping. In the 2.x series it was possible to configure a directory to use as a cache. This was removed in 3.0 in favor of a much more robust caching mechanism. The below snippet of code is how to effectively setup your 3.0 Annotated Container to cache similarly to 2.0.
+Setting up caching is something that you must explicitly opt into during your bootstrapping. In the 2.x series it was possible to configure a directory to use as a cache. This was removed in 3.0, in favor of a much more robust caching mechanism. The below snippet of code is how to effectively setup your 3.0 Annotated Container to cache similarly to 2.0.
```php
bootstrapContainer(
- bootstrappingConfigurationProvider: new CacheAwareBootstrappingConfigurationProvider(
- new XmlBootstrappingConfigurationProvider(),
- new FileBackedContainerDefinitionCache(
- new XmlContainerDefinitionSerializer(),
- __DIR__ . '/.annotated-container-cache'
- )
+$container = Bootstrap::fromCompleteSetup(
+ new CacheAwareBootstrappingConfiguration(
+ new XmlBootstrappingConfiguration(
+ new PhpFunctionsFilesystem(),
+ 'annotated-container.xml',
+ ),
+ new FileBackedContainerDefinitionCache(
+ new XmlContainerDefinitionSerializer(),
+ __DIR__ . '/.annotated-container-cache'
)
- );
+ ),
+ new YourContainerFactory(),
+ new Emitter(),
+ new VendorPresenceBasedBootstrappingDirectoryResolver()
+)->bootstrapContainer();
```
If the cache implementations provided by Annotated Container are not sufficient, you can create your own `Cspray\AnnotatedContainer\Definition\Cache\ContainerDefinitionCache` appropriate for your use case.
diff --git a/docs/references/01-attributes-list.md b/docs/references/01-attributes-list.md
index 1b4fda7b..a679f2f7 100644
--- a/docs/references/01-attributes-list.md
+++ b/docs/references/01-attributes-list.md
@@ -9,4 +9,3 @@ The following Attributes are made available through this library. All Attributes
| `ServicePrepare` | `Attribute::TARGET_METHOD` | Describes a method, on an interface or class, that should be invoked when that type is created. |
| `ServiceDelegate` | `Attribute::TARGET_METHOD` | Defines a method that will be used to generate a defined type. |
| `Inject` | `Attribute::TARGET_PARAMETER`, `Attribute::TARGET_PROPERTY` | Defines a value that should be injected, can handle either scalar or service values based on the type-hint of the parameter. Inject Attributes on properties are only recognized if on `[#Configuration]` objects. |
-| `Configuration` | `Attribute::TARGET_CLASS` | Defines an instance as being a Configuration object. Properties can have values injected into them. |
diff --git a/docs/references/02-functional-api.md b/docs/references/02-functional-api.md
index e615c138..7195dd58 100644
--- a/docs/references/02-functional-api.md
+++ b/docs/references/02-functional-api.md
@@ -10,54 +10,73 @@ This document lists the functions for each purpose.
## Defining Services
```php
-\Cspray\AnnotatedContainer\service(
- \Cspray\Typiphy\ObjectType $service,
+ The rest of this guide assumes you've installed a backing container library! Please check out the README Installation for more details.
+> The rest of this guide assumes you've installed a backing container library! Please check out the README Installation
+> for more details.
## Abstract Services
@@ -38,7 +43,12 @@ interface BlobStorageEventEmitter {
}
```
-Here we have annotated 2 interfaces, `BlobStorage` and `BlobStorageEventEmitter`. When an interface or abstract class is annotated with the `#[Service]` attribute they are called _abstract services_. Abstract services cannot be instantiated, but you want to type-hint in constructors and "share" with the container. Sharing a service with the container ensures that 1 instance is created and shared wherever you might type-hint it or whenever you call `ContainerInterface::get()`. In other words, for our example, you can call `ContainerInterface::get(BlobStorage::class)` and get the same instance every time.
+Here we have annotated 2 interfaces, `BlobStorage` and `BlobStorageEventEmitter`. When an interface or abstract class is
+annotated with the `#[Service]` attribute they are called _abstract services_. Abstract services cannot be instantiated,
+but you want to type-hint in constructors and "share" with the container. Sharing a service with the container ensures
+that 1 instance is created and shared wherever you might type-hint it or whenever you call `ContainerInterface::get()`.
+In other words, for our example, you can call `ContainerInterface::get(BlobStorage::class)` and get the same
+instance every time.
## Concrete Services
@@ -94,11 +104,21 @@ class FilesystemStorage implements BlobStorage {
}
```
-We also annotated 2 concrete classes, `BlobStorageEventEmitter` and `FilesystemStorage`, which are called _concrete services_. While it is not necessary for a concrete service to satisfy a contract from an abstract service it is expected that the Annotated Container will often be used in this manner. Because there's only 1 concrete service for each abstract service we know, for example, when `ContainerInterface::get(BlobStorage::class)`is called you really want to get an instance of`FilesystemStorage`.
+We also annotated 2 concrete classes, `BlobStorageEventEmitter` and `FilesystemStorage`, which are called
+_concrete services_. While it is not necessary for a concrete service to satisfy a contract from an abstract service it
+is expected that the Annotated Container will often be used in this manner. Because there's only 1 concrete service for
+each abstract service we know, for example, when `ContainerInterface::get(BlobStorage::class)`is called you really want
+to get an instance of`FilesystemStorage`.
-When a concrete service is used to instantiate an abstract service it is referred to as an _alias_; an alias that is inferred from the static analysis of your codebase is considered _implicit_. It is possible for an abstract service to have multiple concrete services, and an alias can't be inferred. We'll talk about how to resolve those types of conflicts in a separate document.
+When a concrete service is used to instantiate an abstract service it is referred to as an _alias_; an alias that is
+inferred from the static analysis of your codebase is considered _implicit_. It is possible for an abstract service to
+have multiple concrete services, and an alias can't be inferred. We'll talk about how to resolve those types of
+conflicts in a separate document.
-The `FilesystemStorage` service has been refactored from our example in README.md by adding an event emitter. We added the`BlobStorageEventEmitter` type-hint to our constructor. Our Container is autowired and knows that you're looking for a Service to be injected. Since the `BlobStorageEventEmitter` has an implicit alias we don't need to specify which concrete implementation to use.
+The `FilesystemStorage` service has been refactored from our example in README.md by adding an event emitter. We
+added the`BlobStorageEventEmitter` type-hint to our constructor. Our Container is autowired and knows that you're
+looking for a Service to be injected. Since the `BlobStorageEventEmitter` has an implicit alias we don't need to specify
+which concrete implementation to use.
## Using our Services
@@ -109,7 +129,9 @@ require __DIR__ . '/vendor/autoload.php';
use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;
-$container = (new Bootstrap())->bootstrapContainer();
+$container = Bootstrap::fromAnnotatedContainerConventions(
+ new YourContainerFactory()
+)->bootstrapContainer();
$emitter = $container->get(BlobStorageEventEmitter::class);
$emitter->onStore(fn(string $identifier) -> echo "Stored $identifier");
@@ -118,11 +140,17 @@ $storage = $container->get(BlobStorage::class);
$storage->store('foo.txt', 'bar');
```
-A key aspect of AnnotatedContainer is how each Service has 1 instance associated with it. See in the calling code that uses the services we were able to call `ContainerInterface::get(BlobStorageEventEmitter::class)`. Changes to that object were present in the service injected into `FilesystemStorage`. All Services are shared and will have the same instance injected through autowiring and returned from `ContainerInterface::get()`.
+A key aspect of AnnotatedContainer is how each Service has 1 instance associated with it. See in the calling code that
+uses the services we were able to call `ContainerInterface::get(BlobStorageEventEmitter::class)`. Changes to that object
+were present in the service injected into `FilesystemStorage`. All Services are shared and will have the same instance
+injected through autowiring and returned from `ContainerInterface::get()`.
## Wrapping Up
-In this document we learned some important concepts in AnnotatedContainer; abstract and concrete services, aliasing abstract services, how to auto-wire service injection, and how all services are effectively singletons. This example is meant to show you some bread & butter aspects of AnnotatedContainer that we expect is the "regular" use-case. There's a lot more functionality available! We highly recommend you checking out the rest of the documents for more details.
+In this document we learned some important concepts in AnnotatedContainer; abstract and concrete services, aliasing
+abstract services, how to auto-wire service injection, and how all services are effectively singletons. This example is
+meant to show you some bread & butter aspects of AnnotatedContainer that we expect is the "regular" use-case. There's a
+lot more functionality available! We highly recommend you checking out the rest of the documents for more details.
## Next Steps
@@ -131,5 +159,5 @@ In this document we learned some important concepts in AnnotatedContainer; abstr
- [Using Factory to Create Services](./04-using-service-factories.md)
- [Invoking Methods Post-Construct](./05-calling-post-construct-methods.md)
- [Injecting Non-Service Values](./06-injecting-scalar-values.md)
-- [Using Autowire Aware Factory](./08-autowire-aware-factory.md)
-- [Using Autowire Aware Invoker](./09-autowire-aware-invoker.md)
\ No newline at end of file
+- [Using Autowire Aware Factory](07-autowire-aware-factory.md)
+- [Using Autowire Aware Invoker](08-autowire-aware-invoker.md)
\ No newline at end of file
diff --git a/docs/tutorials/02-alias-resolution-with-profiles.md b/docs/tutorials/02-alias-resolution-with-profiles.md
index e33f1ae6..1137d06b 100644
--- a/docs/tutorials/02-alias-resolution-with-profiles.md
+++ b/docs/tutorials/02-alias-resolution-with-profiles.md
@@ -1,10 +1,19 @@
# Alias Resolution with Profiles
-Resolving aliases properly is an important part of any autowired dependency injection solution. In the code examples in the README there's only 1 concrete service, but it is common for multiple implementations to be available for an abstract service. In those types of situations AnnotatedContainer can't infer the appropriate service to use. You'll need to provide a little more configuration to solve this problem; profiles are a great way to deal with this issue.
+Resolving aliases properly is an important part of any autowired dependency injection solution. In the code examples in
+the README there's only 1 concrete service, but it is common for multiple implementations to be available for an
+abstract service. In those types of situations AnnotatedContainer can't infer the appropriate service to use. You'll
+need to provide a little more configuration to solve this problem; profiles are a great way to deal with this issue.
-Profiles are an important part of AnnotatedContainer. They provide a way to narrowly specify which services are available for a given runtime of your application. All Services always have at least 1 profile and can contain any number. Services implicitly belong to the 'default' profile if one hasn't been explicitly set. At time of static analysis active profiles are specified and only those services matching the profile are included in the container.
+Profiles are an important part of AnnotatedContainer. They provide a way to narrowly specify which services are
+available for a given runtime of your application. All Services always have at least 1 profile and can contain any
+number. Services implicitly belong to the 'default' profile if one hasn't been explicitly set. At time of static
+analysis active profiles are specified and only those services matching the profile are included in the container.
-We're going to expand on the example found in our README by adding a second concrete service. We'll then show how you could use profiles to specify which concrete service to use based on different runtime requirements. Assume we've been given requirements to create a new implementation that will store the blob on some cloud storage provider instead of the local filesystem. Here's our example from the README with our new implementation.
+We're going to expand on the example found in our README by adding a second concrete service. We'll then show how you
+could use profiles to specify which concrete service to use based on different runtime requirements. Assume we've been
+given requirements to create a new implementation that will store the blob on some cloud storage provider instead of the
+local filesystem. Here's our example from the README with our new implementation.
```php
bootstrapContainer(profiles: ['default', 'cloud']);
+$container = Bootstrap::fromAnnotatedContainerConventions(
+ new YourContainerFactory(),
+)->bootstrapContainer(Profiles::fromList(['default', 'cloud']));
```
If we were on a local machine we'd want to build the options with the following code:
@@ -103,12 +122,22 @@ If we were on a local machine we'd want to build the options with the following
bootstrapContainer(profiles: ['default', 'local']);
+$container = Bootstrap::fromAnnotatedContainerConventions(
+ new YourContainerFactory(),
+)->bootstrapContainer(Profiles::fromList(['default', 'local']));
```
-It is important...**always include the 'default' profile**! Many services will not be annotated with a specific profile and will be implicitly added to the 'default' profile. Failure to include it in the list of active profiles will likely make the vast majority of services configured incorrectly or unavailable.
+It is important...**always include the 'default' profile**! Many services will not be annotated with a specific profile
+and will be implicitly added to the 'default' profile. Failure to include it in the list of active profiles will likely
+make the vast majority of services configured incorrectly or unavailable.
## Profiles != Environments
-It is important to keep in mind that AnnotatedContainer profiles are _not_ a 1-for-1 mapping to environments. Sometimes a profile name will make sense to match an environment name. Other times, it won't. Imagine a third `BlobStorage` implementation might come along for a different cloud-provider or a client that still uses FTP. In this instance they'd both be considered a "production" environment, perhaps, but that wouldn't be enough to distinguish which service to use. If it helps, think of Profiles as a way to tag and identify certain attributes of an environment that helps determine which service might be used.
+It is important to keep in mind that AnnotatedContainer profiles are _not_ a 1-for-1 mapping to environments. Sometimes
+a profile name will make sense to match an environment name. Other times, it won't. Imagine a third `BlobStorage`
+implementation might come along for a different cloud-provider or a client that still uses FTP. In this instance they'd
+both be considered a "production" environment, perhaps, but that wouldn't be enough to distinguish which service to use.
+If it helps, think of Profiles as a way to tag and identify certain attributes of an environment that helps determine
+which service might be used.
diff --git a/docs/tutorials/03-alias-resolution-with-attributes.md b/docs/tutorials/03-alias-resolution-with-attributes.md
index a618ef6a..ba038394 100644
--- a/docs/tutorials/03-alias-resolution-with-attributes.md
+++ b/docs/tutorials/03-alias-resolution-with-attributes.md
@@ -1,8 +1,15 @@
# Alias Resolution with Attributes
-Resolving aliases properly is an important part of any autowired dependency injection solution. In the code examples in the README there's only 1 concrete service, but it is common for multiple implementations to be available for an abstract service. In those types of situations AnnotatedContainer can't infer the appropriate service to use. You'll need to provide a little more configuration to solve this problem; explicitly defining which service to inject might be an appropriate solution.
+Resolving aliases properly is an important part of any autowired dependency injection solution. In the code examples in
+the README there's only 1 concrete service, but it is common for multiple implementations to be available for an
+abstract service. In those types of situations AnnotatedContainer can't infer the appropriate service to use. You'll
+need to provide a little more configuration to solve this problem; explicitly defining which service to inject might be
+an appropriate solution.
-Normally it's recommended that you use Profiles to specify which aliases to use. Sometimes that might not be a viable solution. Instead, you can specify exactly which service to use where you type-hint the Service in your constructors and container-executed methods. We'll use the same code from `/docs/tutorials/02-alias-resolution-with-profiles.md` and show a different way of resolving the alias.
+Normally it's recommended that you use Profiles to specify which aliases to use. Sometimes that might not be a viable
+solution. Instead, you can specify exactly which service to use where you type-hint the Service in your constructors
+and container-executed methods. We'll use the same code from `/docs/tutorials/02-alias-resolution-with-profiles.md`
+and show a different way of resolving the alias.
```php
bootstrapContainer();
+
+$fooWidget = $autowiredFactory->make(FooWidget::class);
+$fooWidget->service instanceof FooService; // true
+
+$barWidget = $autowiredFactory->make(BarWidget::class, autowiredParams(rawParam('foo', 'bar')));
+$barWidget->service instanceof BarService; // true
+$barWidget->foo === 'bar'; // true
+```
+
+## Making Defined Services
+
+It should be noted that using the `AutowireableFactory` interface to create defined Services is not recommended. Making
+a shared service won't recreate the instance the way you'd might expect. Which means if you call
+`AutowireableFactory::make` and attempt to override the parameters that are used it will still use what is defined in
+the Container. If you have defined the class in the Container you should use the `ContainerInterface::get()` method to
+retrieve it.
+
+
diff --git a/docs/tutorials/08-autowire-aware-factory.md b/docs/tutorials/08-autowire-aware-factory.md
deleted file mode 100644
index 1c1660d3..00000000
--- a/docs/tutorials/08-autowire-aware-factory.md
+++ /dev/null
@@ -1,65 +0,0 @@
-# Autowire Aware Factory
-
-Sometimes you might want to take advantage of the autowiring capabilities of Annotated Container when you're creating an object that isn't a Service. The Container returned from a `ContainerFactory` is a [type intersect](https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.composite.intersection) that includes the `Cspray\AnnotatedContainer\AutowireableFactory` interface. You can depend on this type in your factory constructors to create autowired objects!
-
-## Example
-
-In our example we're going to create a `WidgetFactory` that creates `Widget` implementations. Some of those implementations depend on services from the Container, while other implementations depend on scalar values that must be provided when you create the object. Before we look at how to use the `AutowireableFactory` let's take a look at an example codebase.
-
-```php
-bootstrapContainer();
-
-$fooWidget = $autowiredFactory->make(FooWidget::class);
-$fooWidget->service instanceof FooService; // true
-
-$barWidget = $autowiredFactory->make(BarWidget::class, autowiredParams(rawParam('foo', 'bar')));
-$barWidget->service instanceof BarService; // true
-$barWidget->foo === 'bar'; // true
-```
-
-## Making Defined Services
-
-It should be noted that using the `AutowireableFactory` interface to create defined Services is not recommended. Making a shared service won't recreate the instance the way you'd might expect. Which means if you call `AutowireableFactory::make` and attempt to override the parameters that are used it will still use what is defined in the Container. If you have defined the class in the Container you should use the `ContainerInterface::get()` method to retrieve it.
-
-
diff --git a/docs/tutorials/09-autowire-aware-invoker.md b/docs/tutorials/08-autowire-aware-invoker.md
similarity index 69%
rename from docs/tutorials/09-autowire-aware-invoker.md
rename to docs/tutorials/08-autowire-aware-invoker.md
index 2bd1b8ec..cfcaeedf 100644
--- a/docs/tutorials/09-autowire-aware-invoker.md
+++ b/docs/tutorials/08-autowire-aware-invoker.md
@@ -1,10 +1,16 @@
# Autowire Aware Invoker
-It is common in PHP to use callables for a wide variety of functionality. Annotated Container provides functionality to invoke a callable and recursively autowire the dependencies it requires from available services. The Container returned from a `ContainerFactory` is a [type intersect](https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.composite.intersection) that includes the `Cspray\AnnotatedContainer\AutowireableInvoker`. You can depend on this type in your constructors to invoke autowired callables!
+It is common in PHP to use callables for a wide variety of functionality. Annotated Container provides functionality to
+invoke a callable and recursively autowire the dependencies it requires from available services. The Container returned
+from a `ContainerFactory` is a [type intersect](https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.composite.intersection) that includes the `Cspray\AnnotatedContainer\AutowireableInvoker`.
+You can depend on this type in your constructors to invoke autowired callables!
## Example
-In our example we're going to create some callables that interact with `Widget` implementations. Some of those implementations depend on services from the Container, while other implementations depend on scalar values that must be provided when you create the object. Before we look at how to use the `AutowireableInvoker` let's take a look at an example codebase.
+In our example we're going to create some callables that interact with `Widget` implementations. Some of those
+implementations depend on services from the Container, while other implementations depend on scalar values that must be
+provided when you create the object. Before we look at how to use the `AutowireableInvoker` let's take a look at an
+example codebase.
```php
cache() === null) {
+ throw new RuntimeException('A container definition cache must be provided with bootstrapping configuration');
+ }
+ }
+
+}
+```
+
+Now that we have a listener implemented we need to make sure it is added to the Emitter to be invoked at the appropriate
+time. You can do this through your configuration or by programmatically constructing the Listener and adding it to the
+Emitter. Check out the [How To: Bootstrap Your Container](../how-to/02-bootstrap-your-container.md) for more information
+on how to register your listener.
+
+
diff --git a/docs/tutorials/10-annotated-container-observers.md b/docs/tutorials/10-annotated-container-observers.md
deleted file mode 100644
index 5568d600..00000000
--- a/docs/tutorials/10-annotated-container-observers.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# Annotated Container Observers
-
-Annotated Container has a bootstrapping observer system that allows you to get access to pertinent information during the creation of your container. This system could be used to gather information about the static analysis process, perform action on the Container post creation, or some other action that might be necessary. Below we'll talk about how to take advantage of this system to bring more complex functionality to applications powered by Annotated Container.
-
-## Registering Observers
-
- First, you'll need to decide on what type of observer you want to implement. The example will implement all possible interfaces, to show what's possible. You do not have to implement all of them; you can choose any of them to implement.
-
-```php
-addObserver(new MyContainerObserver());
-$container = $bootstrap->bootstrapContainer();
-```
-
diff --git a/known-issues.xml b/known-issues.xml
index 46c2a7c3..bc92a408 100644
--- a/known-issues.xml
+++ b/known-issues.xml
@@ -1,10 +1,5 @@
-
-
-
-
-
-
+
@@ -31,27 +26,16 @@
}]]>
-
-
- valueType->name()]]>
-
-
- name]]]>
-
-
- |object]]>
-
-
- listOf->toCollection($values)]]>
-
-
-
+
- $this->serviceCollectorReferenceToListOfServices($containerBuilder, $state, $definition, $value)]]>
+ $state->serviceCollectorReferenceToListOfServices(]]>
+
+ make(...)]]>
+
@@ -60,73 +44,90 @@
-
+
- make($definition->type()->name())]]>execute(
[$serviceDelegateDefinition->classMethod()->class()->name(), $serviceDelegateDefinition->classMethod()->methodName()],
- $this->parametersForServiceDelegateToArray($injector, $state, $serviceDelegateDefinition),
+ $parameterResolver->resolveParametersForServiceDelegate($injector, $state, $serviceDelegateDefinition),
)]]>injector->make($id)]]>
-
-
-
-
-
-
-
-
-
+
- $containerBuilder->get($value->name)]]>
- $this->serviceCollectorReferenceToListOfServices($containerBuilder, $state, $definition, $value)]]> $closure()]]> $closure()]]>
+
+
+
- name()]]]>
-
-
- call(
[$target, $serviceDelegateDefinition->classMethod()->methodName()],
- array_map(static fn(Closure $closure) => $closure(), $this->parametersForServiceDelegateToArray($container, $state, $serviceDelegateDefinition)),
+ array_map(
+ static fn(Closure $closure) => $closure(),
+ $this->parameterResolver->resolveParametersForServiceDelegate(
+ $container,
+ $this->state,
+ $serviceDelegateDefinition
+ )
+ ),
)]]>
- get($definition->type()->name())]]>
- get($definition->type()->name())]]>
+
+
+
+
+
+
+
+ $container->get($value->name)]]>
+ $state->serviceCollectorReferenceToListOfServices($value, $injectDefinition, $container->get(...))]]>
+
+
+ get(...)]]>
+
+
+ name()]]]>
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+ name()]]>
+
+ get(...)]]>
+ name()]]]>
@@ -134,7 +135,6 @@
-
@@ -142,9 +142,8 @@
call(
[$serviceDelegateDefinition->classMethod()->class()->name(), $serviceDelegateDefinition->classMethod()->methodName()],
- $this->parametersForServiceDelegateToArray($container, $state, $serviceDelegateDefinition),
+ $parameterResolver->resolveParametersForServiceDelegate($container, $state, $serviceDelegateDefinition),
)]]>
- get($definition->type()->name())]]>
@@ -157,15 +156,27 @@
+ valueType->name()]]>
+
+
+
+
+ listOf->toCollection($values)]]>
+
+
+
+ name]]]>
+
+
@@ -238,9 +249,4 @@
-
-
- directories]]>
-
-
diff --git a/phpunit.xml b/phpunit.xml
index 493e4762..d9d5d70a 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -20,10 +20,8 @@
-
diff --git a/src/Bootstrap/Bootstrap.php b/src/Bootstrap/Bootstrap.php
index fa2f0ec6..1e2db7f2 100644
--- a/src/Bootstrap/Bootstrap.php
+++ b/src/Bootstrap/Bootstrap.php
@@ -15,7 +15,7 @@
use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\BootstrappingDirectoryResolver;
use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\VendorPresenceBasedBootstrappingDirectoryResolver;
use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactory;
-use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactoryOptionsBuilder;
+use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactoryOptions;
use Cspray\AnnotatedContainer\Definition\ContainerDefinition;
use Cspray\AnnotatedContainer\Event\Emitter;
use Cspray\AnnotatedContainer\Filesystem\Filesystem;
@@ -42,7 +42,7 @@ private function __construct(
public static function fromAnnotatedContainerConventions(
ContainerFactory $containerFactory,
- Emitter $emitter,
+ Emitter $emitter = new Emitter(),
ParameterStoreFactory $parameterStoreFactory = new DefaultParameterStoreFactory(),
ListenerFactory $listenerFactory = new DefaultListenerFactory(),
DefinitionProviderFactory $definitionProviderFactory = new DefaultDefinitionProviderFactory(),
@@ -156,9 +156,9 @@ private function createContainer(
$this->containerFactory->addParameterStore($parameterStore);
}
- $factoryOptions = ContainerFactoryOptionsBuilder::forProfiles($activeProfiles);
+ $factoryOptions = ContainerFactoryOptions::fromProfiles($activeProfiles);
- return $this->containerFactory->createContainer($containerDefinition, $factoryOptions->build());
+ return $this->containerFactory->createContainer($containerDefinition, $factoryOptions);
}
private function createAnalytics(
diff --git a/src/Bootstrap/Configuration/BootstrappingConfiguration.php b/src/Bootstrap/Configuration/BootstrappingConfiguration.php
index d79bd7fd..df46c617 100644
--- a/src/Bootstrap/Configuration/BootstrappingConfiguration.php
+++ b/src/Bootstrap/Configuration/BootstrappingConfiguration.php
@@ -11,7 +11,7 @@
interface BootstrappingConfiguration {
/**
- * @return list
+ * @return list
*/
public function scanDirectories() : array;
diff --git a/src/Bootstrap/Configuration/ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration.php b/src/Bootstrap/Configuration/ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration.php
index f7b7af79..63095086 100644
--- a/src/Bootstrap/Configuration/ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration.php
+++ b/src/Bootstrap/Configuration/ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration.php
@@ -19,12 +19,18 @@ public function create() : ContainerDefinitionAnalysisOptions {
foreach ($this->configuration->scanDirectories() as $scanDirectory) {
$scanPaths[] = $this->directoryResolver->rootPath($scanDirectory);
}
- $analysisOptions = ContainerDefinitionAnalysisOptionsBuilder::scanDirectories(...$scanPaths);
+ /** @var list $scanPaths */
$containerDefinitionConsumer = $this->configuration->containerDefinitionProvider();
- if ($containerDefinitionConsumer !== null) {
- $analysisOptions = $analysisOptions->withDefinitionProvider($containerDefinitionConsumer);
+
+ if ($containerDefinitionConsumer === null) {
+ $analysisOptions = ContainerDefinitionAnalysisOptions::fromScanDirectories($scanPaths);
+ } else {
+ $analysisOptions = ContainerDefinitionAnalysisOptions::fromScanDirectoriesAndDefinitionProvider(
+ $scanPaths,
+ $containerDefinitionConsumer
+ );
}
- return $analysisOptions->build();
+ return $analysisOptions;
}
}
diff --git a/src/Bootstrap/Configuration/XmlBootstrappingConfiguration.php b/src/Bootstrap/Configuration/XmlBootstrappingConfiguration.php
index 10abcd29..40181279 100644
--- a/src/Bootstrap/Configuration/XmlBootstrappingConfiguration.php
+++ b/src/Bootstrap/Configuration/XmlBootstrappingConfiguration.php
@@ -19,7 +19,7 @@
final class XmlBootstrappingConfiguration implements BootstrappingConfiguration {
/**
- * @var list
+ * @var list
*/
private readonly array $directories;
private readonly ?DefinitionProvider $definitionProvider;
@@ -37,15 +37,19 @@ final class XmlBootstrappingConfiguration implements BootstrappingConfiguration
public function __construct(
private readonly Filesystem $filesystem,
private readonly string $xmlFile,
- private readonly ParameterStoreFactory $parameterStoreFactory,
- private readonly DefinitionProviderFactory $definitionProviderFactory,
- private readonly ListenerFactory $listenerFactory,
+ private readonly ParameterStoreFactory $parameterStoreFactory = new DefaultParameterStoreFactory(),
+ private readonly DefinitionProviderFactory $definitionProviderFactory = new DefaultDefinitionProviderFactory(),
+ private readonly ListenerFactory $listenerFactory = new DefaultListenerFactory(),
) {
if (!$this->filesystem->isFile($this->xmlFile)) {
throw InvalidBootstrapConfiguration::fromFileMissing($this->xmlFile);
}
try {
+ // There are several assertions on the types of data we expect in the below code. This is because we
+ // expect the schema to define a document that specifies certain minimum lengths and expectations of
+ // data being present. Validating the schema will throw an error if these expectations are not met, and
+ // there's no way to test type expectations because if there are any it will not pass schema validation.
$schemaFile = dirname(__DIR__, 3) . '/annotated-container.xsd';
$dom = new DOMDocument();
$dom->loadXML($this->filesystem->read($this->xmlFile));
@@ -61,7 +65,7 @@ public function __construct(
$scanDirectories = [];
foreach ($scanDirectoriesNodes as $scanDirectory) {
$sourceDirectory = $scanDirectory->nodeValue;
- assert($sourceDirectory !== null);
+ assert($sourceDirectory !== null && $sourceDirectory !== '');
$scanDirectories[] = $sourceDirectory;
}
diff --git a/src/Cli/AnnotatedContainerCliRunner.php b/src/Cli/AnnotatedContainerCliRunner.php
index 9a864245..13b64310 100644
--- a/src/Cli/AnnotatedContainerCliRunner.php
+++ b/src/Cli/AnnotatedContainerCliRunner.php
@@ -113,9 +113,6 @@ public function commands() : array {
* @param list $argv
*/
public function run(array $argv) : void {
- $this->commandExecutor->execute(
- (new InputParser())->parse($argv),
- new TerminalOutput()
- );
+ $this->commandExecutor->execute((new InputParser())->parse($argv), new TerminalOutput());
}
}
diff --git a/src/ContainerFactory/AbstractContainerFactory.php b/src/ContainerFactory/AbstractContainerFactory.php
index 7f026bbb..250b8e28 100644
--- a/src/ContainerFactory/AbstractContainerFactory.php
+++ b/src/ContainerFactory/AbstractContainerFactory.php
@@ -6,14 +6,8 @@
use Cspray\AnnotatedContainer\ContainerFactory\AliasResolution\AliasDefinitionResolver;
use Cspray\AnnotatedContainer\ContainerFactory\AliasResolution\StandardAliasDefinitionResolver;
use Cspray\AnnotatedContainer\ContainerFactory\State\ContainerFactoryState;
-use Cspray\AnnotatedContainer\ContainerFactory\State\InjectParameterValue;
-use Cspray\AnnotatedContainer\ContainerFactory\State\ServiceCollectorReference;
use Cspray\AnnotatedContainer\Definition\ContainerDefinition;
-use Cspray\AnnotatedContainer\Definition\InjectDefinition;
use Cspray\AnnotatedContainer\Definition\ProfilesAwareContainerDefinition;
-use Cspray\AnnotatedContainer\Definition\ServiceDefinition;
-use Cspray\AnnotatedContainer\Definition\ServiceDelegateDefinition;
-use Cspray\AnnotatedContainer\Definition\ServicePrepareDefinition;
use Cspray\AnnotatedContainer\Event\ContainerFactoryEmitter;
use Cspray\AnnotatedContainer\Profiles;
@@ -56,94 +50,7 @@ final public function createContainer(ContainerDefinition $containerDefinition,
return $container;
}
- /**
- * @param ContainerBuilder $containerBuilder
- * @return array
- */
- final protected function parametersForServiceConstructorToArray(
- object $containerBuilder,
- ContainerFactoryState $state,
- ServiceDefinition $serviceDefinition
- ) : array {
- return $this->listOfInjectDefinitionsToArray(
- $containerBuilder,
- $state,
- $state->constructorInjectDefinitionsForServiceDefinition($serviceDefinition)
- );
- }
-
- /**
- * @param ContainerBuilder $containerBuilder
- * @return array
- */
- final protected function parametersForServicePrepareToArray(
- object $containerBuilder,
- ContainerFactoryState $state,
- ServicePrepareDefinition $definition,
- ) : array {
- return $this->listOfInjectDefinitionsToArray(
- $containerBuilder,
- $state,
- $state->injectDefinitionsForServicePrepareDefinition($definition)
- );
- }
-
- /**
- * @param ContainerBuilder $containerBuilder
- * @return array
- */
- final protected function parametersForServiceDelegateToArray(
- object $containerBuilder,
- ContainerFactoryState $state,
- ServiceDelegateDefinition $definition,
- ) : array {
- return $this->listOfInjectDefinitionsToArray(
- $containerBuilder,
- $state,
- $state->injectDefinitionsForServiceDelegateDefinition($definition)
- );
- }
-
- /**
- * @param IntermediaryContainer $container
- * @param ServiceCollectorReference $reference
- * @return list