# Contributte DI (Dependency Injection)

# Content

# Setup

composer require contributte/di
1

# ResourceExtension

First, you have to register the extension.

extensions:
    autoload: Contributte\DI\Extension\ResourceExtension
1
2

Second, define some resources.

autoload:
    resources:
        App\Model\Services\:
            paths: [%appDir%/model/services]
1
2
3
4

It may look familiar to you. You're right, the idea comes from Symfony 3.3.

That's all, the ResourceExtension will try to register all non-abstract instantiable classes to the container.

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

# ContainerAware

This package provides the missing IContainerAware interface for your applications.

extensions:
    aware: Contributte\DI\Extension\ContainerAwareExtension
1
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;
    }

}
1
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;

}
1
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());
1
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;

}
1
2
3
4
5
6
7

First, register InjectValueExtension under extensions key.

extensions:
    injectValue: Contributte\DI\Extension\InjectValueExtension

injectValue:
    all: on/off
1
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]
1
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));
    }

}
1
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();
        // ...
    }

}
1
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']);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15