Définition des services
La configuration est l'endroit où nous apprenons au conteneur DI comment assembler les différents services et comment les connecter à d'autres dépendances. Nette fournit une manière très claire et élégante d'y parvenir.
La section services
dans le fichier de configuration au format NEON est l'endroit où nous définissons nos
propres services et leurs configurations. Voyons un exemple simple de définition d'un service nommé database
, qui
représente une instance de la classe PDO
:
services:
database: PDO('sqlite::memory:')
La configuration indiquée aboutira à la méthode factory suivante dans le conteneur DI :
public function createServiceDatabase(): PDO
{
return new PDO('sqlite::memory:');
}
Les noms des services nous permettent d'y faire référence dans d'autres parties du fichier de configuration, sous la forme
@nomDuService
. S'il n'est pas nécessaire de nommer le service, nous pouvons simplement utiliser un tiret :
services:
- PDO('sqlite::memory:')
Pour obtenir un service du conteneur DI, nous pouvons utiliser la méthode getService()
avec le nom du service
comme paramètre, ou la méthode getByType()
avec le type du service :
$database = $container->getService('database');
$database = $container->getByType(PDO::class);
Création de service
La plupart du temps, nous créons un service simplement en créant une instance d'une certaine classe. Par exemple :
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Si nous devons étendre la configuration avec d'autres clés, la définition peut être répartie sur plusieurs lignes :
services:
database:
create: PDO('sqlite::memory:')
setup: ...
La clé create
a un alias factory
, les deux variantes sont courantes en pratique. Cependant, nous
recommandons d'utiliser create
.
Les arguments du constructeur ou de la méthode de création peuvent alternativement être écrits dans la clé
arguments
:
services:
database:
create: PDO
arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]
Les services ne doivent pas nécessairement être créés par simple instanciation d'une classe, ils peuvent aussi être le résultat d'appels de méthodes statiques ou de méthodes d'autres services :
services:
database: DatabaseFactory::create()
router: @routerFactory::create()
Notez que pour la simplicité, ::
est utilisé à la place de ->
, voir #expressions. Ces méthodes factory seront générées :
public function createServiceDatabase(): PDO
{
return DatabaseFactory::create();
}
public function createServiceRouter(): RouteList
{
return $this->getService('routerFactory')->create();
}
Le conteneur DI a besoin de connaître le type du service créé. Si nous créons un service à l'aide d'une méthode qui n'a pas de type de retour spécifié, nous devons explicitement indiquer ce type dans la configuration :
services:
database:
create: DatabaseFactory::create()
type: PDO
Arguments
Nous passons les arguments au constructeur et aux méthodes d'une manière très similaire à PHP lui-même :
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Pour une meilleure lisibilité, nous pouvons répartir les arguments sur des lignes séparées. Dans ce cas, l'utilisation de virgules est facultative :
services:
database: PDO(
'mysql:host=127.0.0.1;dbname=test'
root
secret
)
Vous pouvez également nommer les arguments et ne pas vous soucier de leur ordre :
services:
database: PDO(
username: root
password: secret
dsn: 'mysql:host=127.0.0.1;dbname=test'
)
Si vous souhaitez omettre certains arguments et utiliser leur valeur par défaut ou injecter un service via l'autowiring, utilisez le trait de soulignement :
services:
foo: Foo(_, %appDir%)
Comme arguments, on peut passer des services, utiliser des paramètres et bien plus encore, voir #expressions.
Setup
Dans la section setup
, nous définissons les méthodes qui doivent être appelées lors de la création du
service.
services:
database:
create: PDO(%dsn%, %user%, %password%)
setup:
- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
Cela ressemblerait à ceci en PHP :
public function createServiceDatabase(): PDO
{
$service = new PDO('...', '...', '...');
$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $service;
}
En plus des appels de méthodes, il est également possible de passer des valeurs aux propriétés. L'ajout d'un élément à un tableau est également pris en charge, ce qui doit être écrit entre guillemets pour ne pas entrer en conflit avec la syntaxe NEON :
services:
foo:
create: Foo
setup:
- $value = 123
- '$onClick[]' = [@bar, clickHandler]
Ce qui ressemblerait au code PHP suivant :
public function createServiceFoo(): Foo
{
$service = new Foo;
$service->value = 123;
$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
return $service;
}
Dans le setup, on peut cependant aussi appeler des méthodes statiques ou des méthodes d'autres services. Si vous avez besoin
de passer le service actuel comme argument, indiquez-le comme @self
:
services:
foo:
create: Foo
setup:
- My\Helpers::initializeFoo(@self)
- @anotherService::setFoo(@self)
Notez que pour la simplicité, ::
est utilisé à la place de ->
, voir #expressions. Une telle méthode factory sera générée :
public function createServiceFoo(): Foo
{
$service = new Foo;
My\Helpers::initializeFoo($service);
$this->getService('anotherService')->setFoo($service);
return $service;
}
Expressions
Nette DI nous offre des moyens d'expression extraordinairement riches, grâce auxquels nous pouvons écrire presque n'importe quoi. Dans les fichiers de configuration, nous pouvons ainsi utiliser des paramètres :
# paramètre
%wwwDir%
# valeur du paramètre sous la clé
%mailer.user%
# paramètre à l'intérieur d'une chaîne
'%wwwDir%/images'
De plus, créer des objets, appeler des méthodes et des fonctions :
# création d'objet
DateTime()
# appel de méthode statique
Collator::create(%locale%)
# appel de fonction PHP
::getenv(DB_USER)
Se référer aux services soit par leur nom, soit par leur type :
# service par nom
@database
# service par type
@Nette\Database\Connection
Utiliser la syntaxe first-class callable :
# création d'un callback, équivalent à [@user, logout]
@user::logout(...)
Utiliser des constantes :
# constante de classe
FilesystemIterator::SKIP_DOTS
# constante globale obtenue avec la fonction PHP constant()
::constant(PHP_VERSION)
Les appels de méthodes peuvent être chaînés comme en PHP. Seulement pour la simplicité, ::
est utilisé à la
place de ->
:
DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')
@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()
Vous pouvez utiliser ces expressions n'importe où, lors de la création de services, dans les arguments, dans la section setup ou les paramètres :
parameters:
ipAddress: @http.request::getRemoteAddress()
services:
database:
create: DatabaseFactory::create( @anotherService::getDsn() )
setup:
- initialize( ::getenv('DB_USER') )
Fonctions spéciales
Dans les fichiers de configuration, vous pouvez utiliser ces fonctions spéciales :
not()
négation de la valeurbool()
,int()
,float()
,string()
conversion sans perte vers le type donnétyped()
crée un tableau de tous les services du type spécifiétagged()
crée un tableau de tous les services avec le tag donné
services:
- Foo(
id: int(::getenv('ProjectId'))
productionMode: not(%debugMode%)
)
Contrairement à la conversion de type classique en PHP, comme par exemple (int)
, la conversion sans perte lèvera
une exception pour les valeurs non numériques.
La fonction typed()
crée un tableau de tous les services du type donné (classe ou interface). Elle omet les
services dont l'autowiring est désactivé. Il est possible d'indiquer plusieurs types séparés par une virgule.
services:
- BarsDependent( typed(Bar) )
Vous pouvez également passer un tableau de services d'un certain type comme argument automatiquement via l'autowiring.
La fonction tagged()
crée ensuite un tableau de tous les services avec un certain tag. Ici aussi, vous pouvez
spécifier plusieurs tags séparés par une virgule.
services:
- LoggersDependent( tagged(logger) )
Autowiring
La clé autowired
permet d'influencer le comportement de l'autowiring pour un service spécifique. Pour plus de
détails, voir le chapitre sur l'autowiring.
services:
foo:
create: Foo
autowired: false # le service foo est exclu de l'autowiring
Services Lazy
Le chargement paresseux (lazy loading) est une technique qui reporte la création d'un service jusqu'au moment où il est réellement nécessaire. Dans la configuration globale, il est possible d'activer la création lazy pour tous les services en même temps. Pour des services individuels, vous pouvez ensuite surcharger ce comportement :
services:
foo:
create: Foo
lazy: false
Lorsqu'un service est défini comme lazy, lors de sa demande depuis le conteneur DI, nous recevons un objet placeholder spécial. Celui-ci ressemble et se comporte comme le service réel, mais l'initialisation réelle (appel du constructeur et du setup) n'a lieu qu'au premier appel de l'une de ses méthodes ou propriétés.
Le chargement paresseux ne peut être utilisé que pour les classes utilisateur, pas pour les classes internes de PHP. Nécessite PHP 8.4 ou plus récent.
Tags
Les tags servent à ajouter des informations supplémentaires aux services. Vous pouvez ajouter un ou plusieurs tags à un service :
services:
foo:
create: Foo
tags:
- cached
Les tags peuvent également porter des valeurs :
services:
foo:
create: Foo
tags:
logger: monolog.logger.event
Pour obtenir tous les services avec certains tags, vous pouvez utiliser la fonction tagged()
:
services:
- LoggersDependent( tagged(logger) )
Dans le conteneur DI, vous pouvez obtenir les noms de tous les services avec un certain tag en utilisant la méthode
findByTag()
:
$names = $container->findByTag('logger');
// $names est un tableau contenant le nom du service et la valeur du tag
// par ex. ['foo' => 'monolog.logger.event', ...]
Mode Inject
À l'aide de l'indicateur inject: true
, le passage des dépendances via les variables publiques avec l'annotation
inject et les méthodes inject*() est activé.
services:
articles:
create: App\Model\Articles
inject: true
Par défaut, inject
est activé uniquement pour les presenters.
Modification des services
Le conteneur DI contient de nombreux services qui ont été ajoutés via une extension intégrée ou utilisateur. Vous pouvez modifier les définitions de ces services directement dans la configuration.
Par exemple, vous pouvez changer la classe du service application.application
, qui est par défaut
Nette\Application\Application
, en une autre :
services:
application.application:
create: MyApplication
alteration: true
L'indicateur alteration
est informatif et indique que nous modifions simplement un service existant.
Nous pouvons également compléter le setup :
services:
application.application:
create: MyApplication
alteration: true
setup:
- '$onStartup[]' = [@resource, init]
Lors de la réécriture d'un service, nous pouvons vouloir supprimer les arguments d'origine, les éléments de setup ou les
tags, ce à quoi sert reset
:
services:
application.application:
create: MyApplication
alteration: true
reset:
- arguments
- setup
- tags
Si vous souhaitez supprimer un service ajouté par une extension, vous pouvez le faire comme ceci :
services:
cache.journal: false