-
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Frank Brückner <[email protected]>
- Loading branch information
1 parent
d2636bc
commit 09c338d
Showing
12 changed files
with
78 additions
and
1,197 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,105 +1,6 @@ | ||
# Code Generator | ||
|
||
laminas-di comes with [Ahead-of-Time (AoT)](https://en.wikipedia.org/wiki/Ahead-of-time_compilation) | ||
generators to create optimized code for production. These generators will | ||
inspect the provided classes, resolve their dependencies, and generate factories | ||
based on these results. | ||
|
||
> ### Removal of laminas-code dependencies | ||
> | ||
> Before version 3.1, this feature required [laminas-code](https://docs.laminas.dev/laminas-code/), | ||
> which you can add to your project using Composer: | ||
> | ||
> ```bash | ||
> $ composer require --dev laminas/laminas-code | ||
> ``` | ||
> | ||
> **Since version 3.1 and up, this is no longer required.** | ||
## Generating an optimized injector | ||
The `Laminas\Di\CodeGenerator\InjectorGenerator` class offers an implementation to | ||
generate an optimized injector based on the runtime configuration and a resolver | ||
instance. | ||
```php | ||
use Laminas\Di\Config; | ||
use Laminas\Di\Definition\RuntimeDefinition; | ||
use Laminas\Di\Resolver\DependencyResolver; | ||
use Laminas\Di\CodeGenerator\InjectorGenerator; | ||
$config = new Config(); | ||
$resolver = new DependencyResolver(new RuntimeDefinition(), $config); | ||
$generator = new InjectorGenerator($config, $resolver); | ||
// It is highly recommended to set the container that is used at runtime: | ||
$resolver->setContainer($container); | ||
$generator->setOutputDirectory('/path/to/generated/files'); | ||
$generator->generate([ | ||
MyClassA::class, | ||
MyClassB::class, | ||
// ... | ||
]); | ||
``` | ||
You can also utilize `Laminas\Code\Scanner` to scan your code for classes: | ||
```php | ||
$scanner = new DirectoryScanner(__DIR__); | ||
$generator->generate($scanner->getClassNames()); | ||
``` | ||
## MVC and Mezzio integration | ||
When you are using laminas-di's `ConfigProvider` with Mezzio or consuming the | ||
`Module` class via laminas-mvc, you can obtain the generator instance from the | ||
service manager: | ||
```php | ||
$generator = $serviceManager->get(\Laminas\Di\CodeGenerator\InjectorGenerator::class); | ||
``` | ||
### AoT Config Options | ||
The service factory uses options in your `config` service, located under the key | ||
`dependencies.auto.aot`. This should be defined as an associative array of | ||
options for creating the code generator instance. This array respects the | ||
following keys (unknown keys are ignored): | ||
- `namespace`: This will be used as base namespace to prefix the namespace of | ||
the generated classes. It will be passed to the constructor of | ||
`Laminas\Di\CodeGenerator\InjectorGenerator`; the default value is | ||
`Laminas\Di\Generated`. | ||
- `directory`: The directory where the generated PHP files will be stored. If | ||
this value is not provided, you will need to set it with the generator's | ||
`setOutputDirectory()` method before calling `generate()`. | ||
- `logger`: must be resolvable in container and must be an instance of `Psr\Log\LoggerInterface.` | ||
By default `Psr\Log\NullLogger` is used. See the [Logging section](#logging) for details. | ||
Below is an example detailing configuration of the generator factory: | ||
```php | ||
return [ | ||
'dependencies' => [ | ||
'auto' => [ | ||
'aot' => [ | ||
'namespace' => 'AppAoT\Generated', | ||
'directory' => __DIR__ . '/../gen', | ||
'logger' => Psr\Log\LoggerInterface::class, | ||
], | ||
], | ||
], | ||
]; | ||
``` | ||
## Logging | ||
The `InjectorGenerator` allows passing a [PSR-3 logger](http://www.php-fig.org/psr/psr-3/) instance | ||
via an optional fourth constructor parameter. | ||
The generator will log the following information: | ||
* When a factory is about to be generated for a class or alias (Log level: Debug) | ||
* When the factory generation caused an exception (Log level: Error) | ||
<noscript><meta http-equiv="refresh" content="0; url=/laminas-di/v3/codegen/"></noscript> | ||
<script> | ||
document.addEventListener("DOMContentLoaded", function (event) { | ||
window.location.pathname = '/laminas-di/v3/codegen/'; | ||
}); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,226 +1,6 @@ | ||
# Configuration | ||
|
||
Configuration detailing how types are constructed and dependencies should be | ||
resolved can be provided as an associative array when instantiating | ||
`Laminas\Di\Config`. A type may be an actual class name or an alias to a class | ||
name. | ||
|
||
The configuration array respects the following keys (unknown keys are ignored): | ||
|
||
- `preferences`: Associative nested array that maps class or interface names to | ||
a service name that should be used to provide a dependency. See the | ||
[Type Preferences](#type-preferences) section below for details. | ||
|
||
- `types`: Associative array defining how classes or aliases should be | ||
constructed. Each key in this array is a class or alias name, and its value is | ||
another associative array with the following keys: | ||
|
||
- `preferences`: The same as `preferences` above, but only for the associated | ||
class. | ||
|
||
- `parameters`: Associative array declaring the values to inject for the | ||
declared construction parameters. Each key is the parameter name as | ||
declared in the constructor method of the associated class name. See the | ||
[Parameters](#parameters) section below for details. | ||
|
||
- `typeOf`: String that contains a class name. It declares that the | ||
associated key is an alias of the given class name. This class must exist. | ||
It cannot not be another alias. | ||
|
||
Below is an example of injector configuration. | ||
|
||
```php | ||
$config = new \Laminas\Di\Config([ | ||
// Declares global preferences to use when resolving | ||
// dependencies of the specified type | ||
'preferences' => [ | ||
// A map of classname => preferred type | ||
MyInterface::class => MyImplementation::class, | ||
], | ||
|
||
// Declares how types should be constructed. | ||
// This also allows declaring aliases of a specific class | ||
'types' => [ | ||
ClassName::class => [ | ||
// Declaration in the same way as global preferences | ||
// but these will aply when the type of the associated key | ||
// should be instanciated | ||
'preferences' => [], | ||
|
||
// Constructor parameters to inject. This option will define | ||
// the injections directly by the parameter name of the constructor | ||
// used as key. | ||
// | ||
// If the parameter is type-hinted by a class/interface name, you can | ||
// provide the injection by string. The injector will use the IoC | ||
// container to obtain it. | ||
'parameters' => [ | ||
'foo' => 'bar', | ||
], | ||
], | ||
|
||
// Define an alias | ||
'Alias.Name' => [ | ||
'typeOf' => ClassName::class, | ||
|
||
'preferences' => [], | ||
'parameters' => [], | ||
], | ||
], | ||
]); | ||
``` | ||
|
||
## Type Preferences | ||
|
||
In many cases, you might be using interfaces as type hints as opposed to | ||
concrete types. Even though type preferences are not limited to interfaces or | ||
abstract class names, they provide hints to the injector on how such types | ||
should be resolved. | ||
|
||
The resolver will look up the name finally passed to the container in the | ||
following way (the first match will be used): | ||
|
||
1. The preference defined in the type configuration of the class if it satifies | ||
the typehint (implements, extends, or typeOf). | ||
2. If there is a global preference defined and it satisfies the typehint. | ||
3. Use the typehinted name directly. | ||
|
||
```php | ||
// Assume the following classes are declared: | ||
|
||
interface FooInterface | ||
{} | ||
|
||
class Foo implements FooInterface | ||
{} | ||
|
||
class SpecialFoo implements FooInterface | ||
{} | ||
|
||
class Bar | ||
{} | ||
|
||
class MyClass | ||
{ | ||
public function __construct(FooInterface $foo) | ||
{ | ||
// ... | ||
} | ||
} | ||
|
||
// With the following configuration: | ||
|
||
use Laminas\Di\Injector; | ||
use Laminas\Di\Config; | ||
|
||
$injector = new Injector(new Config([ | ||
'preferences' => [ | ||
FooInterface::class => Foo::class, | ||
], | ||
'types' => [ | ||
'MyClass.A' => [ | ||
'typeOf' => MyClass::class, | ||
'preferences' => [ | ||
FooInterface::class => SpecialFoo::class, | ||
], | ||
], | ||
'MyClass.B' => [ | ||
'typeOf' => MyClass::class, | ||
'preferences' => [ | ||
FooInterface::class => Bar::class, | ||
], | ||
], | ||
], | ||
])); | ||
|
||
|
||
// The results are: | ||
$a = $injector->create(MyClass::class); // Constructed with Foo | ||
$b = $injector->create('MyClass.A'); // Constructed with SpecialFoo | ||
$c = $injector->create('MyClass.B'); // Constructed with Foo (since Bar does not satisfy FooInterface) | ||
``` | ||
|
||
|
||
## Parameters | ||
|
||
In contrast to type preferences, the resolver will not perform checks if the | ||
provided value satisfies the required type. It will be used directly to inject | ||
the value. | ||
|
||
There are several ways to define injections. | ||
|
||
- An IoC container service name as string: This is only possible if the required | ||
type is a class or interface. For other types (scalars, `iterable`, | ||
`callable`, etc) or typeless parameters, the string value is passed __as is__. | ||
|
||
- An instance of `Laminas\Di\Resolver\ValueInjection`: Injects the value returned | ||
by `getValue()` as is. | ||
|
||
- An instance of `Laminas\Di\Resolver\TypeInjection`: Obtains the injected value | ||
from the IoC container by passing the return value of `getType()` to the | ||
container's `get()` method. | ||
|
||
- The string literal `'*'`: This requests the injector to ignore any previously | ||
defined parameter and use the type preference resolution as described in | ||
[Type Preferences](#type-preferences). | ||
|
||
- Any other value will be used as is and encapsulated in a | ||
`Laminas\Di\Resolver\ValueInjection`. If the provided value's type does not fit | ||
the required parameter type, an exception is thrown. | ||
|
||
## Aliases | ||
|
||
Aliases allow you to configure the same class with different construction | ||
options. Aliases can directly be created with the injector or declared as type | ||
preferences. | ||
|
||
An alias must refer to an actual class or an interface, therefore you cannot | ||
declare aliases for another alias. | ||
|
||
For example the following the following class should be instantiated in two | ||
different ways: | ||
|
||
```php | ||
// Assume the following classes are declared: | ||
|
||
class Foo | ||
{} | ||
|
||
class SpecialFoo extends Foo | ||
{} | ||
|
||
class MyClass | ||
{ | ||
public function __construct(Foo $foo, string $bar) | ||
{ | ||
// ... | ||
} | ||
} | ||
|
||
// With the following injection config: | ||
|
||
use Laminas\Di\Injector; | ||
use Laminas\Di\Config; | ||
|
||
$injector = new Injector(new Config([ | ||
'types' => [ | ||
MyClass::class => [ | ||
'parameters' => [ | ||
'foo' => SpecialFoo::class, | ||
'bar' => 'Stringvalue', | ||
], | ||
], | ||
'MyClass.Alias' => [ | ||
'typeOf' => MyClass::class, | ||
'parameters' => [ | ||
'foo' => '*', | ||
'bar' => 'Stringvalue', | ||
], | ||
], | ||
], | ||
])); | ||
|
||
// The results are: | ||
$a = $injector->create(MyClass::class); // Constructed with SpecialFoo | ||
$b = $injector->create('MyClass.Alias'); // Constructed with Foo (since there are no type preferences for Foo) | ||
``` | ||
<noscript><meta http-equiv="refresh" content="0; url=/laminas-di/v3/config/"></noscript> | ||
<script> | ||
document.addEventListener("DOMContentLoaded", function (event) { | ||
window.location.pathname = '/laminas-di/v3/config/'; | ||
}); | ||
</script> |
Oops, something went wrong.