Konfiguration des DI-Containers
*******************************
.[perex]
Übersicht der Konfigurationsmöglichkeiten für den Nette DI Container.
Konfigurationsdatei
===================
Der Nette DI Container lässt sich bequem über Konfigurationsdateien steuern. Diese werden üblicherweise im [Format NEON|neon:format] geschrieben. Wir empfehlen, [Editoren mit Unterstützung |tools:ide] für dieses Format zu verwenden.
<pre>
"decorator .[prism-token prism-atrule]":[#Decorator]: "Decorator .[prism-token prism-comment]"<br>
"di .[prism-token prism-atrule]":[#DI]: "DI-Container .[prism-token prism-comment]"<br>
"extensions .[prism-token prism-atrule]":[#Extensions]: "Weitere DI-Extensions installieren .[prism-token prism-comment]"<br>
"includes .[prism-token prism-atrule]":[#Dateien einbinden]: "Dateien einbinden .[prism-token prism-comment]"<br>
"parameters .[prism-token prism-atrule]":[#Parameter]: "Parameter .[prism-token prism-comment]"<br>
"search .[prism-token prism-atrule]":[#Search]: "Automatische Registrierung von Services .[prism-token prism-comment]"<br>
"services .[prism-token prism-atrule]":[services]: "Services .[prism-token prism-comment]"
</pre>
.[note]
Um einen String zu schreiben, der das Zeichen `%` enthält, müssen Sie es durch Verdoppeln zu `%%` escapen.
Parameter
=========
In der Konfiguration können Sie Parameter definieren, die sich anschließend als Teil der Service-Definitionen verwenden lassen. So machen Sie die Konfiguration übersichtlicher oder führen Werte an einer Stelle zusammen, die sich ändern können.
```neon
parameters:
dsn: 'mysql:host=127.0.0.1;dbname=test'
user: root
password: secret
```
Auf den Parameter `dsn` verweisen wir überall in der Konfiguration mit der Schreibweise `%dsn%`. Parameter lassen sich auch innerhalb von Strings verwenden, etwa `'%wwwDir%/images'`.
Parameter müssen nicht nur Strings oder Zahlen sein, sie können auch Arrays enthalten:
```neon
parameters:
mailer:
host: smtp.example.com
secure: ssl
user: franta@gmail.com
languages: [cs, en, de]
```
Auf einen bestimmten Schlüssel verweisen wir mit `%mailer.user%`.
Wenn Ihr Code (etwa eine Klasse) den Wert eines Parameters braucht, übergeben Sie ihn der Klasse. Zum Beispiel im Konstruktor. Es gibt kein globales Konfigurationsobjekt, das Klassen nach Werten von Parametern fragen könnten. Das wäre ein Verstoß gegen das Prinzip der Dependency Injection.
Services
========
Siehe [eigenes Kapitel|services].
Decorator
=========
Wie ändern Sie mehrere Services eines bestimmten Typs auf einmal? Wie rufen Sie zum Beispiel auf allen Presentern, die von einer bestimmten Basisklasse erben, eine bestimmte Methode auf? Dafür ist der Decorator da.
```neon
decorator:
# für alle Services, die Instanzen dieser Klasse oder dieses Interfaces sind
App\Presentation\BasePresenter:
setup:
- setProjectId(10) # diese Methode aufrufen
- $absoluteUrls = true # und die Variable setzen
```
Der Decorator lässt sich auch verwenden, um [Tags |services#Tags] zu setzen oder den [Inject-Modus |services#Inject-Modus] einzuschalten.
```neon
decorator:
InjectableInterface:
tags: [mytag: 1]
inject: true
```
DI
===
Technische Einstellungen des DI-Containers.
```neon
di:
# den DIC in der Tracy Bar anzeigen?
debugger: ... # (bool) standardmäßig automatische Erkennung (eingeschaltet, wenn Tracy vorhanden ist)
# Typen von Parametern, die Sie nie autowiren
excluded: ... # (string[])
# das lazy Erzeugen von Services einschalten?
lazy: ... # (bool) Standard ist false
# die Klasse, von der der DI-Container erbt
parentClass: ... # (string) standardmäßig Nette\DI\Container
```
Lazy Services .{data-version:3.2.4}
-----------------------------------
Die Einstellung `lazy: true` aktiviert das lazy (aufgeschobene) Erzeugen von Services. Das bedeutet, dass Services nicht in dem Moment tatsächlich erzeugt werden, in dem sie beim DI-Container angefordert werden, sondern erst bei ihrer ersten Verwendung. Das kann den Start der Anwendung beschleunigen und den Speicherverbrauch senken, weil nur die Services erzeugt werden, die für den jeweiligen Request wirklich nötig sind.
Für einen bestimmten Service lässt sich das lazy Erzeugen [anpassen |services#Lazy Services].
.[note]
Lazy Objekte lassen sich nur für eigene Klassen verwenden, nicht für interne PHP-Klassen. Erfordert PHP 8.4 oder neuer.
Export der Metadaten
--------------------
Die Klasse des DI-Containers enthält außerdem eine Menge Metadaten. Sie können ihre Größe verringern, indem Sie den Export der Metadaten einschränken.
```neon
di:
export:
# Parameter exportieren?
parameters: false # (bool) standardmäßig true
# Tags exportieren, und welche?
tags: # (string[]|bool) standardmäßig alle
- event.subscriber
# Daten für das Autowiring exportieren, und welche?
types: # (string[]|bool) standardmäßig alle
- Nette\Database\Connection
- Symfony\Component\Console\Application
```
Wenn Sie `$container->getParameters()` nicht verwenden, können Sie den Export der Parameter abschalten. Außerdem können Sie nur die Tags exportieren, die Sie tatsächlich verwenden, um Services über `$container->findByTag(...)` zu holen. Wenn Sie diese Methode überhaupt nicht aufrufen, können Sie den Export der Tags mit `false` vollständig abschalten.
Die Metadaten für das [Autowiring|autowiring] lassen sich erheblich verkleinern, indem Sie nur die Klassen aufführen, die Sie tatsächlich über `$container->getByType()` anfordern. Auch hier gilt: Wenn Sie diese Methode nicht aufrufen (oder sie nur in der Datei [bootstrap|application:bootstrapping] aufrufen, etwa um `Nette\Application\Application` zu holen), können Sie den Export der Typen mit `false` vollständig abschalten.
Extensions
==========
Registrierung weiterer DI-Extensions. So fügen Sie zum Beispiel die DI-Extension `Dibi\Bridges\Nette\DibiExtension3` unter dem Namen `dibi` hinzu:
```neon
extensions:
dibi: Dibi\Bridges\Nette\DibiExtension3
```
Konfiguriert wird sie anschließend im Abschnitt `dibi`:
```neon
dibi:
host: localhost
```
Als Extension lässt sich auch eine Klasse mit Parametern hinzufügen:
```neon
extensions:
application: Nette\Bridges\ApplicationDI\ApplicationExtension(%debugMode%, [%appDir%], %tempDir%/cache)
```
Dateien einbinden
=================
Weitere Konfigurationsdateien lassen sich im Abschnitt `includes` einbinden:
```neon
includes:
- parameters.php
- services.neon
- presenters.neon
```
Der Name `parameters.php` ist kein Tippfehler; die Konfiguration lässt sich auch in eine PHP-Datei schreiben, die sie als Array zurückgibt:
```php
<?php
return [
'database' => [
'main' => [
'dsn' => 'sqlite::memory:',
],
],
];
```
Erscheinen Elemente mit denselben Schlüsseln in mehreren Konfigurationsdateien, werden sie überschrieben oder bei Arrays [zusammengeführt |#Zusammenführen]. Eine später eingebundene Datei hat höhere Priorität als die vorherige. Die Datei, in der der Abschnitt `includes` steht, hat höhere Priorität als die darin eingebundenen Dateien.
Search
======
Die automatische Registrierung von Services im DI-Container vereinfacht die Entwicklung erheblich. Nette fügt dem Container die Presenter automatisch hinzu, Sie können aber ebenso leicht beliebige weitere Klassen ergänzen.
Geben Sie einfach an, in welchen Verzeichnissen (und Unterverzeichnissen) nach den Klassen gesucht werden soll:
```neon
search:
- in: %appDir%/Forms
- in: %appDir%/Model
```
Wenn Sie nur eine einzige Suchregel brauchen, können Sie die Liste weglassen und ihre Schlüssel direkt unter `search` schreiben:
```neon
search:
in: %appDir%
```
Üblicherweise wollen wir aber nicht absolut alle Klassen und Interfaces hinzufügen, also können wir sie filtern:
```neon
search:
- in: %appDir%/Forms
# Filterung nach Dateiname (string|string[])
files:
- *Factory.php
# Filterung nach Klassenname (string|string[])
classes:
- *Factory
```
Oder wir wählen die Klassen aus, die von mindestens einer der aufgeführten Klassen erben oder eines der Interfaces implementieren:
```neon
search:
- in: %appDir%
extends:
- App\*Form
implements:
- App\*FormInterface
```
Sie können auch Ausschlussregeln über Masken von Klassennamen oder über Vorfahren definieren. Passt eine Klasse auf eine Ausschlussregel, wird sie dem DI-Container nicht hinzugefügt:
```neon
search:
- in: %appDir%
exclude:
files: ...
classes: ...
extends: ...
implements: ...
```
Allen automatisch registrierten Services lassen sich Tags zuweisen:
```neon
search:
- in: %appDir%
tags: ...
```
Neben Klassen registriert die Suche auch Interfaces, die eine einzige Methode `create()` oder `get()` haben - als [generierte Factories oder Accessors |factory]. Klassen, für die im Container bereits ein Service desselben Typs registriert ist, werden übersprungen, es entstehen also keine Duplikate.
Zusammenführen
==============
Erscheinen Elemente mit denselben Schlüsseln in mehreren Konfigurationsdateien, werden sie überschrieben oder bei Arrays zusammengeführt. Die später eingebundene Datei hat höhere Priorität als die vorherige.
<table class=table>
<tr>
<th width=33%>config1.neon</th>
<th width=33%>config2.neon</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>
```neon
items:
- 1
- 2
```
</td>
<td>
```neon
items:
- 3
```
</td>
<td>
```neon
items:
- 1
- 2
- 3
```
</td>
</tr>
</table>
Bei Arrays lässt sich das Zusammenführen verhindern, indem hinter den Namen des Schlüssels ein Ausrufezeichen gesetzt wird:
<table class=table>
<tr>
<th width=33%>config1.neon</th>
<th width=33%>config2.neon</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>
```neon
items:
- 1
- 2
```
</td>
<td>
```neon
items!:
- 3
```
</td>
<td>
```neon
items:
- 3
```
</td>
</tr>
</table>
{{maintitle: Konfiguration der Dependency Injection}}
Konfiguration des DI-Containers
Übersicht der Konfigurationsmöglichkeiten für den Nette DI Container.
Konfigurationsdatei
Der Nette DI Container lässt sich bequem über Konfigurationsdateien steuern. Diese werden üblicherweise im Format NEON geschrieben. Wir empfehlen, Editoren mit Unterstützung für dieses Format zu verwenden.
decorator:
di:
extensions:
includes:
parameters:
search:
services:
Um einen String zu schreiben, der das Zeichen % enthält, müssen Sie es durch Verdoppeln zu
%% escapen.
Parameter
In der Konfiguration können Sie Parameter definieren, die sich anschließend als Teil der Service-Definitionen verwenden
lassen. So machen Sie die Konfiguration übersichtlicher oder führen Werte an einer Stelle zusammen, die sich ändern
können.
parameters:
dsn: 'mysql:host=127.0.0.1;dbname=test'
user: root
password: secret
Auf den Parameter dsn verweisen wir überall in der Konfiguration mit der Schreibweise %dsn%.
Parameter lassen sich auch innerhalb von Strings verwenden, etwa '%wwwDir%/images'.
Parameter müssen nicht nur Strings oder Zahlen sein, sie können auch Arrays enthalten:
parameters:
mailer:
host: smtp.example.com
secure: ssl
user: franta@gmail.com
languages: [cs, en, de]
Auf einen bestimmten Schlüssel verweisen wir mit %mailer.user%.
Wenn Ihr Code (etwa eine Klasse) den Wert eines Parameters braucht, übergeben Sie ihn der Klasse. Zum Beispiel im Konstruktor.
Es gibt kein globales Konfigurationsobjekt, das Klassen nach Werten von Parametern fragen könnten. Das wäre ein Verstoß gegen
das Prinzip der Dependency Injection.
Services
Siehe eigenes Kapitel.
Decorator
Wie ändern Sie mehrere Services eines bestimmten Typs auf einmal? Wie rufen Sie zum Beispiel auf allen Presentern, die von
einer bestimmten Basisklasse erben, eine bestimmte Methode auf? Dafür ist der Decorator da.
decorator:
# für alle Services, die Instanzen dieser Klasse oder dieses Interfaces sind
App\Presentation\BasePresenter:
setup:
- setProjectId(10) # diese Methode aufrufen
- $absoluteUrls = true # und die Variable setzen
Der Decorator lässt sich auch verwenden, um Tags
zu setzen oder den Inject-Modus
einzuschalten.
decorator:
InjectableInterface:
tags: [mytag: 1]
inject: true
DI
Technische Einstellungen des DI-Containers.
di:
# den DIC in der Tracy Bar anzeigen?
debugger: ... # (bool) standardmäßig automatische Erkennung (eingeschaltet, wenn Tracy vorhanden ist)
# Typen von Parametern, die Sie nie autowiren
excluded: ... # (string[])
# das lazy Erzeugen von Services einschalten?
lazy: ... # (bool) Standard ist false
# die Klasse, von der der DI-Container erbt
parentClass: ... # (string) standardmäßig Nette\DI\Container
Lazy Services
Die Einstellung lazy: true aktiviert das lazy (aufgeschobene) Erzeugen von Services. Das bedeutet, dass Services
nicht in dem Moment tatsächlich erzeugt werden, in dem sie beim DI-Container angefordert werden, sondern erst bei ihrer ersten
Verwendung. Das kann den Start der Anwendung beschleunigen und den Speicherverbrauch senken, weil nur die Services erzeugt werden,
die für den jeweiligen Request wirklich nötig sind.
Für einen bestimmten Service lässt sich das lazy Erzeugen anpassen.
Lazy Objekte lassen sich nur für eigene Klassen verwenden, nicht für interne PHP-Klassen. Erfordert PHP
8.4 oder neuer.
Die Klasse des DI-Containers enthält außerdem eine Menge Metadaten. Sie können ihre Größe verringern, indem Sie den Export
der Metadaten einschränken.
di:
export:
# Parameter exportieren?
parameters: false # (bool) standardmäßig true
# Tags exportieren, und welche?
tags: # (string[]|bool) standardmäßig alle
- event.subscriber
# Daten für das Autowiring exportieren, und welche?
types: # (string[]|bool) standardmäßig alle
- Nette\Database\Connection
- Symfony\Component\Console\Application
Wenn Sie $container->getParameters() nicht verwenden, können Sie den Export der Parameter abschalten.
Außerdem können Sie nur die Tags exportieren, die Sie tatsächlich verwenden, um Services über
$container->findByTag(...) zu holen. Wenn Sie diese Methode überhaupt nicht aufrufen, können Sie den Export der
Tags mit false vollständig abschalten.
Die Metadaten für das Autowiring lassen sich erheblich
verkleinern, indem Sie nur die Klassen aufführen, die Sie tatsächlich über $container->getByType() anfordern.
Auch hier gilt: Wenn Sie diese Methode nicht aufrufen (oder sie nur in der Datei bootstrap aufrufen, etwa um
Nette\Application\Application zu holen), können Sie den Export der Typen mit false vollständig
abschalten.
Extensions
Registrierung weiterer DI-Extensions. So fügen Sie zum Beispiel die DI-Extension
Dibi\Bridges\Nette\DibiExtension3 unter dem Namen dibi hinzu:
extensions:
dibi: Dibi\Bridges\Nette\DibiExtension3
Konfiguriert wird sie anschließend im Abschnitt dibi:
dibi:
host: localhost
Als Extension lässt sich auch eine Klasse mit Parametern hinzufügen:
extensions:
application: Nette\Bridges\ApplicationDI\ApplicationExtension(%debugMode%, [%appDir%], %tempDir%/cache)
Dateien einbinden
Weitere Konfigurationsdateien lassen sich im Abschnitt includes einbinden:
includes:
- parameters.php
- services.neon
- presenters.neon
Der Name parameters.php ist kein Tippfehler; die Konfiguration lässt sich auch in eine PHP-Datei schreiben, die
sie als Array zurückgibt:
<?php
return [
'database' => [
'main' => [
'dsn' => 'sqlite::memory:',
],
],
];
Erscheinen Elemente mit denselben Schlüsseln in mehreren Konfigurationsdateien, werden sie überschrieben oder bei Arrays zusammengeführt. Eine später eingebundene Datei hat höhere Priorität als die vorherige. Die
Datei, in der der Abschnitt includes steht, hat höhere Priorität als die darin eingebundenen Dateien.
Search
Die automatische Registrierung von Services im DI-Container vereinfacht die Entwicklung erheblich. Nette fügt dem Container
die Presenter automatisch hinzu, Sie können aber ebenso leicht beliebige weitere Klassen ergänzen.
Geben Sie einfach an, in welchen Verzeichnissen (und Unterverzeichnissen) nach den Klassen gesucht werden soll:
search:
- in: %appDir%/Forms
- in: %appDir%/Model
Wenn Sie nur eine einzige Suchregel brauchen, können Sie die Liste weglassen und ihre Schlüssel direkt unter
search schreiben:
search:
in: %appDir%
Üblicherweise wollen wir aber nicht absolut alle Klassen und Interfaces hinzufügen, also können wir sie filtern:
search:
- in: %appDir%/Forms
# Filterung nach Dateiname (string|string[])
files:
- *Factory.php
# Filterung nach Klassenname (string|string[])
classes:
- *Factory
Oder wir wählen die Klassen aus, die von mindestens einer der aufgeführten Klassen erben oder eines der Interfaces
implementieren:
search:
- in: %appDir%
extends:
- App\*Form
implements:
- App\*FormInterface
Sie können auch Ausschlussregeln über Masken von Klassennamen oder über Vorfahren definieren. Passt eine Klasse auf eine
Ausschlussregel, wird sie dem DI-Container nicht hinzugefügt:
search:
- in: %appDir%
exclude:
files: ...
classes: ...
extends: ...
implements: ...
Allen automatisch registrierten Services lassen sich Tags zuweisen:
search:
- in: %appDir%
tags: ...
Neben Klassen registriert die Suche auch Interfaces, die eine einzige Methode create() oder get()
haben – als generierte Factories oder Accessors. Klassen,
für die im Container bereits ein Service desselben Typs registriert ist, werden übersprungen, es entstehen also keine
Duplikate.
Zusammenführen
Erscheinen Elemente mit denselben Schlüsseln in mehreren Konfigurationsdateien, werden sie überschrieben oder bei Arrays
zusammengeführt. Die später eingebundene Datei hat höhere Priorität als die vorherige.
| config1.neon |
config2.neon |
Ergebnis |
items:
- 1
- 2
|
items:
- 3
|
items:
- 1
- 2
- 3
|
Bei Arrays lässt sich das Zusammenführen verhindern, indem hinter den Namen des Schlüssels ein Ausrufezeichen
gesetzt wird:
| config1.neon |
config2.neon |
Ergebnis |
items:
- 1
- 2
|
items!:
- 3
|
items:
- 3
|