Skip to content

Commit

Permalink
Remove references to injecting a property in code (#382)
Browse files Browse the repository at this point in the history
  • Loading branch information
cspray committed Jun 5, 2024
1 parent 463ff49 commit 531f273
Show file tree
Hide file tree
Showing 51 changed files with 274 additions and 979 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,18 @@ Now, bootstrap your Container in your app.
require __DIR__ . '/vendor/autoload.php';

use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;
use Cspray\AnnotatedContainer\Event\Emitter;
use Cspray\AnnotatedContainer\Profiles;

$emitter = new Emitter();

// Add whatever Listeners are required for your application

// Include other active profiles in this list
// If the only active profile is default you can call this method without any arguments
$profiles = ['default'];
$container = (new Bootstrap())->bootstrapContainer($profiles);
$container = Bootstrap::fromMinimalSetup($emitter)->bootstrapContainer(
Profiles::fromList(['default'])
);

$storage = $container->get(BlobStorage::class); // instanceof FilesystemStorage
```
Expand Down
26 changes: 3 additions & 23 deletions annotated-container-definition.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@

<xs:complexType name="injectDefinitionType">
<xs:all>
<xs:element name="target" type="injectTargetType" />
<xs:element name="class" type="xs:token" />
<xs:element name="method" type="xs:token" />
<xs:element name="parameter" type="xs:token" />
<xs:element name="valueType" type="xs:token" />
<xs:element name="value" type="xs:string" />
<xs:element name="profiles" type="profilesType" />
Expand All @@ -110,13 +112,6 @@
</xs:all>
</xs:complexType>

<xs:complexType name="injectTargetType">
<xs:choice>
<xs:element name="classMethod" type="classMethodType" />
<xs:element name="classProperty" type="classPropertyType" />
</xs:choice>
</xs:complexType>

<xs:complexType name="profilesType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="profile" type="xs:token" />
Expand All @@ -129,19 +124,4 @@
<xs:enumeration value="Abstract" />
</xs:restriction>
</xs:simpleType>

<xs:complexType name="classMethodType">
<xs:all>
<xs:element name="class" type="xs:token" />
<xs:element name="method" type="xs:token" />
<xs:element name="parameter" type="xs:token" />
</xs:all>
</xs:complexType>

<xs:complexType name="classPropertyType">
<xs:all>
<xs:element name="class" type="xs:token" />
<xs:element name="property" type="xs:token" />
</xs:all>
</xs:complexType>
</xs:schema>
7 changes: 3 additions & 4 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
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. [Using Configuration Objects](./tutorials/07-using-configuration-objects.md)
8. [Autowire Aware Factory](./tutorials/08-autowire-aware-factory.md)
9. [Autowire aware Invoker](./tutorials/09-autowire-aware-invoker.md)
10. [Annotated Container Observer](./tutorials/10-annotated-container-observers.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)

## How To

Expand Down
5 changes: 3 additions & 2 deletions docs/how-to/01-add-third-party-services.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class ThirdPartyServicesProvider implements DefinitionProvider {
<scanDirectories>
<source>
<dir>src</dir>
<dir packagePrivate="true">tests</dir>
<dir>tests</dir>
</source>
</scanDirectories>
<cacheDir>.annotated-container-cache</cacheDir>
Expand All @@ -84,8 +84,9 @@ class ThirdPartyServicesProvider implements DefinitionProvider {

use Psr\Log\LoggerInterface;
use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;
use Cspray\AnnotatedContainer\Event\Emitter;

$container = (new Bootstrap())->bootstrapContainer();
$container = Bootstrap::fromMinimalSetup(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.
102 changes: 41 additions & 61 deletions docs/how-to/02-bootstrap-your-container.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,33 +27,11 @@ 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. By default, we also include a directory that stores a cached ContainerDefinition. Though the cache directory is not strictly required it is a good practice to include it. Caching drastically increases the performance of creating your Container. It is also required if you want to use the `./vendor/bin/annotated-container build` command to generate a ContainerDefinition ahead-of-time, for example in production.

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.
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.

## Step 2 - Setup Logging (optional)
## Step 2 - Setup Third Party Services (optional)

Annotated Container statically analyzes your codebase for uses of specific Attributes and then wires all that stuff into a usable Container. When you consider all the different responsibilities this adds up to a lot of things happening. Logging provides every single detail for what is parsed and how that gets turned into a Container.

Bootstrapping allows logging to a file or logging to stdout. In our example the logs will be sent to a file in `logs/` directory located in our project's root. You should ensure that the `logs/` directory exists and is writable.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<annotatedContainer xmlns="https://annotated-container.cspray.io/schema/annotated-container.xsd">
<scanDirectories>
<source>
<dir>src</dir>
<dir>tests</dir>
</source>
</scanDirectories>
<cacheDir>.annotated-container-cache</cacheDir>
<logging>
<file>logs/annotated-container.log</file>
</logging>
</annotatedContainer>
```

## Step 3 - 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 this implementation 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:

Expand Down Expand Up @@ -92,7 +70,7 @@ Now, upgrade the configuration to let bootstrapping know which class to use.
</annotatedContainer>
```

### Step 4 - Provide your custom ParameterStore (optional)
### 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.

Expand Down Expand Up @@ -138,7 +116,7 @@ Next, update your configuration.
</annotatedContainer>
```

### Step 5 - Create Your Container
### Step 4 - Create Your Container

Before completing this step go put some Attributes on the services in your codebase!

Expand All @@ -150,25 +128,34 @@ Now that the configuration file has been modified, and you've attributed your co
namespace Acme\Demo;

use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;
use Cspray\AnnotatedContainer\Event\Emitter;

$emitter = new Emitter();

$container = (new Bootstrap())->bootstrapContainer();
// Add Listeners to respond to events emitted by Annotated Container

$container = Bootstrap::fromMinimalSetup($emitter)->bootstrapContainer();
```

You have the ability to control specific aspects of the Bootstrapping process by providing different arguments to the `bootstrapContainer` method and different dependencies to the constructor. I'll talk about the method arguments first, then take a look at how you can provide different constructor dependencies.
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.

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`.

#### Specifying Profiles

The first argument, `$profiles`, passed to `bootstrapContainer` should be an array of active profiles. If you don't pass any arguments the default value `['default']` will be used.
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.

```php
<?php declare(strict_types=1);

namespace Acme\Demo;

use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;
use Cspray\AnnotatedContainer\Event\Emitter;
use Cspray\AnnotatedContainer\Profiles;

// If you specify 'dev', 'test, 'prod' profiles this container will be ready for production
$container = (new Bootstrap())->bootstrapContainer(profiles: ['default', 'prod']);
$container = Bootstrap::fromMinimalSetup(new Emitter())
->bootstrapContainer(Profiles::fromList(['default', 'prod']));
```

#### Changing the Configuration File
Expand All @@ -181,46 +168,30 @@ Perhaps you didn't name your configuration file the default, it is recommended y
namespace Acme\Demo;

use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;
use Cspray\AnnotatedContainer\Event\Emitter;use Cspray\AnnotatedContainer\Profiles;

// If you specify 'dev', 'test, 'prod' profiles this container will be ready for production
$container = (new Bootstrap())->bootstrapContainer(configurationFile: 'my-container.xml');
$container = Bootstrap::fromMinimalSetup(new Emitter)
->bootstrapContainer(configurationFile: 'my-container.xml');
```

#### Overriding Logging

Perhaps the out-of-the-box logging provided by Annotated Container isn't sufficient. Perhaps you're working in an app with a logging system already in place. Whatever the reason, if you provide a `Psr\Log\LoggerInterface` to the `$logger` construct argument that implementation will be used.

```php
<?php declare(strict_types=1);

namespace Acme\Demo;

use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;

// This method is an implementation provided by the reader
$logger = MyLoggerFactory::create();

$container = (new Bootstrap(logger: $logger))->bootstrapContainer();
```

**Any logger passed to this constructor will override the logging set in the configuration!**

#### 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 `$definitionProviderFactory` construct argument.
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.

```php
<?php declare(strict_types=1);

namespace Acme\Demo;

use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;
use Cspray\AnnotatedContainer\Event\Emitter;

// This method is an implementation provided by the reader
$definitionProviderFactory = MyDefinitionProviderFactory::create();

$container = (new Bootstrap(
definitionProviderFactory: $definitionProviderFactory
$container = Bootstrap::fromMinimalSetup(
new Emitter(),
definitionProviderFactory: $definitionProviderFactory
))->bootstrapContainer();
```

Expand All @@ -234,30 +205,39 @@ The custom `ParameterStore` implementations you use might require some dependenc
namespace Acme\Demo;

use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;
use Cspray\AnnotatedContainer\Event\Emitter;

// This method is an implementation provided by the reader
$parameterStoreFactory = MyParameterStoreFactory::create();

$container = (new Bootstrap(
$container = Bootstrap::fromMinimalSetup(
new Emitter(),
parameterStoreFactory: $parameterStoreFactory
))->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` and passed to `$directoryResolver` contruct argument.
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.

```php
<?php declare(strict_types=1);

namespace Acme\Demo;

use Cspray\AnnotatedContainer\Bootstrap;
use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;use Cspray\AnnotatedContainer\Bootstrap\DefaultDefinitionProviderFactory;use Cspray\AnnotatedContainer\Bootstrap\DefaultParameterStoreFactory;use Cspray\AnnotatedContainer\ContainerFactory\PhpDiContainerFactory;use Cspray\AnnotatedContainer\Event\Emitter;

// This method is an implementation provided by the reader
$directoryResolver = MyDirectoryResolver::create();

$container = (new Bootstrap(
directoryResolver: $directoryResolver
$emitter = new Emitter();

$container = Bootstrap::fromCompleteSetup(
// if you aren't using php-di replace this with your appropriate implementation
new PhpDiContainerFactory($emitter),
$emitter,
$directoryResolver,
new DefaultParameterStoreFactory(),
new DefaultDefinitionProviderFactory()
))->bootstrapContainer();
```
9 changes: 0 additions & 9 deletions docs/references/02-functional-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,6 @@ This document lists the functions for each purpose.
string $from = null
) : \Cspray\AnnotatedContainer\Definition\InjectDefinition;

\Cspray\AnnotatedContainer\injectProperty(
\Cspray\AnnotatedContainer\StaticAnalysis\DefinitionProviderContext $context,
\Cspray\Typiphy\ObjectType $service,
string $property,
\Cspray\Typiphy\Type|\Cspray\Typiphy\TypeUnion|\Cspray\Typiphy\TypeIntersect $type,
mixed $value,
array $profiles = [],
string $from = null
) : \Cspray\AnnotatedContainer\Definition\InjectDefinition;
```

## Autowireable Parameters
Expand Down
Loading

0 comments on commit 531f273

Please sign in to comment.