Contributte Di
# Contributte DI (Dependency Injection)
# Content
- Setup
- ResourceExtension
- ContainerAware
- MutableExtension
- InjectValueExtension
- PassCompilerExtension
- Decorator
# Setup
composer require contributte/di
# ResourceExtension
First, you have to register the extension.
extensions:
autoload: Contributte\DI\Extension\ResourceExtension
2
Second, define some resources.
autoload:
resources:
App\Model\Services\:
paths: [%appDir%/model/services]
2
3
4
It may look familiar to you. You're right, the idea comes from Symfony 3.3 (opens new window).
That's all, the ResourceExtension
will try to register all non-abstract instantiable classes to the container.
‼️ ResourceExtension should be the first extension in your extensions
list.
# Resources
autoload:
App\Model\Services\:
paths: [%appDir%/model/services]
excludes: [App\Model\Services\Gopay, App\Model\Services\CustomService\Testing]
decorator:
tags: [autoload]
setup:
- setLogger(@customlogger)
autowired: false # true
inject: true # enables inject annotations and methods
2
3
4
5
6
7
8
9
10
# Performance
Service loading is triggered only once at dependency injection container compile-time. It should be pretty fast, almost as official registering of presenters as services (opens new window).
# ContainerAware
This package provides the missing IContainerAware
interface for your applications.
extensions:
aware: Contributte\DI\Extension\ContainerAwareExtension
2
From that moment you can use the IContainerAware
interface and let the container inject.
<?php
namespace App\Model;
use Contributte\DI\IContainerAware;
use Nette\DI\Container;
final class LoggableCachedEventDispatcher implements IContainerAware
{
/** @var Container */
protected $container;
public function setContainer(Container $container): void
{
$this->container = $container;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Don't repeat yourself, use the TContainerAware
trait.
<?php
namespace App\Model;
use Contributte\DI\IContainerAware;
use Contributte\DI\TContainerAware;
final class LoggableCachedEventDispatcher implements IContainerAware
{
use TContainerAware;
}
2
3
4
5
6
7
8
9
10
11
12
13
# MutableExtension
This extension is suitable for testing.
use Contributte\DI\Extension\MutableExtension;
use Nette\DI\CompilerExtension;
use Nette\DI\Compiler;
use Nette\DI\ContainerBuilder;
use Nette\DI\ContainerLoader;
$loader = new ContainerLoader(TEMP_DIR, TRUE);
$class = $loader->load(static function (Compiler $compiler): void {
$compiler->addExtension('x', $mutable = new MutableExtension());
// called -> loadConfiguration()
$mutable->onLoad[] = function (CompilerExtension $ext, ContainerBuilder $builder): void {
$builder->addDefinition($ext->prefix('request'))
->setType(Request::class)
->setFactory(RequestFactory::class . '::createHttpRequest');
};
// called -> beforeCompile()
$mutable->onBefore[] = static function (CompilerExtension $ext, ContainerBuilder $builder): void {
$definitions = $builder->findByType(Xyz::class);
};
', 'neon'));
}, time());
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# InjectValueExtension
This awesome extension allows you to inject values directly into public properties.
Let's say we have a service like this:
class FooPresenter extends Presenter
{
/** @var string @value(%appDir%/baz) */
public $bar;
}
2
3
4
5
6
7
First, register InjectValueExtension
under extensions
key.
extensions:
injectValue: Contributte\DI\Extension\InjectValueExtension
injectValue:
all: on/off
2
3
4
5
By default, the extension injects values
only for services having the inject.value
tag.
You can override it to inject to all services by defining all: on
. Or follow the preferred way
and use the Nette\DI decorator.
decorator:
App\MyBaseService:
tags: [inject.value]
App\MyBasePresenter:
tags: [inject.value]
2
3
4
5
6
In the end, after creating the FooPresenter
, the $bar
property will be filled with <path>/www/baz
. Cool right?
# PassCompilerExtension
With this extension you can split your big extension/configuration into more compiler passes (Symfony idea).
use Contributte\DI\Extension\PassCompilerExtension;
final class FoobarExtension extends PassCompilerExtension
{
public function __construct()
{
$this->addPass(new PartAPass($this));
$this->addPass(new PartBPass($this));
}
}
2
3
4
5
6
7
8
9
10
11
12
Extending AbstractPass
defines 3 methods:
loadPassConfiguration
beforePassCompile
afterPassCompile
use Contributte\DI\Pass\AbstractPass;
class PartAPass extends AbstractPass
{
public function loadPassConfiguration(): void
{
$builder = $this->extension->getCompilerBuilder();
// ...
}
}
2
3
4
5
6
7
8
9
10
11
12
# Decorator
Using decorator you can programmatically decorate services. It finds all definitions by given type and add tags and setup as you know in decorator section in neon. Useful in libraries.
use Contributte\DI\Decorator\Decorator;
use Nette\DI\CompilerExtension;
final class FooExtension extends CompilerExtension
{
public function beforeCompile(): void
{
Decorator::of($this->getContainerBuilder())
->decorate(BaseGrid::class)
->addSetup('injectGrid')
->addTags(['grid']);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15