Latest release: v0.3.0

Dependency Injection (DI)

Content

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)
        autowire: false # true
1
2
3
4
5
6
7
8
9

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.

$loader = new ContainerLoader(TEMP_DIR, TRUE);
$class = $loader->load(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'))
            ->setClass(Request::class)
            ->setFactory(RequestFactory::class . '::createHttpRequest');
    };

    // called -> beforeCompile()
    $mutable->onBefore[] = function (CompilerExtension $ext, ContainerBuilder $builder): void {
        $classes = $builder->findByDefinition(Xyz::class);
    };

    ', 'neon'));
}, time());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

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 extension AbstractPass
{

    public function loadPassConfiguration(): void
    {
        $builder = $this->extension->getCompilerBuilder();
        // ...
    }

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

NewExtensionsExtension

From time to time you get to the point when you have a lot of extensions. Some depend on other and vice-versa. Therefore the need for NewExtensionsExtension arises.

In a classic Nette application you will see something like this:

extensions:
    foo: App\DI\FooExtension
    bar: App\DI\BarExtension
    baz1: App\DI\Baz1Extension
    baz2: App\DI\Baz2Extension
1
2
3
4
5

The bar & baz require to have foo registered. How to solve this?

First, you have to replace default extensions extension, yes, it's name is extensions! Change it manually or via the ConfiguratorHelper class.

Manual replacement

$configurator->defaultExtensions['extensions'] = Contributte\DI\Extension\NewExtensionsExtension::class;
1

ConfiguratorHelper

$configurator = new Configurator();
Contributte\DI\ConfiguratorHelper::upgrade($configurator);
1
2

New-way how to register extensions

extensions:
    # Register by key
    baz1: App\DI\Baz1Extension

    # Register unnamed
    - App\DI\Baz2Extension

    # Register with priority
    bar:
        class: App\DI\BarExtension
        # default priority is 10, you can omit it
        priority: 10
    foo:
        class: App\DI\FooExtension
        priority: 5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15