# Programmation PHP avec Symfony/Service

* Introduction
* Service
** Contrôleur
** Commande
* Composant
** HttpClient
** Évènement
** Formulaire
** Mailer
** Stimulus
* Bundle
** Twig
** Doctrine
** API
* Introduction
* Service
** Contrôleur
** Commande
* Composant
** HttpClient
** Évènement
** Formulaire
** Mailer
** Stimulus
* Bundle
** Twig
** Doctrine
** API
[ Modifier le sommaire ]

## Principe

Le principe des services Symfony est d'éviter d'instancier la plupart des classes avec des "new" dispersés dans le code, pour les déclarer une seule fois, grâce au container. Ils sont alors instanciés uniquement s'ils sont utilisés (ex : sur la page web courante), grâce au lazy loading du container.
Cette déclaration peut se faire en PHP, en YAML ou en XML. On baptise alors le service (il peut y en avoir plusieurs par classe), et on appelle ses arguments par leur nom de service. Exemple :

### Pas de include ou require

Les classes natives de PHP doivent être introduites par leur namespace ou bien par l'espace de nom global. Ex :
ou

## Autowiring

Avant SF2.8, il était obligatoire de déclarer chaque service dans les fichiers de configuration .yml ou .yaml, en plus de leurs classes .php (qui peuvent se contredire), et de les mettre à jour à chaque changement de structure.
Depuis SF2.8, l'"autoconfigure: true" permet de déclarer automatiquement chaque service à partir de sa classe, et l'"autowiring: true" d'injecter automatiquement les arguments connus (ex : une autre classe appelée par son espace de nom et son nom), donc sans déclaration manuelle.
Depuis SF4, cette déclaration est par défaut sans le fichier services.yaml, mais on peut la placer dans un autre fichier qui sera importé par le premier, par exemple avec :
ou :
Cette séparation des services en plusieurs .yaml nécessite par contre d'exclure les dossiers de ces services de l'autowiring, et de reprendre la section _defaults dans le nouveau .yaml.
Exemple d'exclusion récursive de plusieurs dossiers de même nom, avec ** :

### bind

Par défaut, l'autowiring ne fonctionne pas avec les classes avec des tags, ou ayant autre chose que des services dans leurs constructeurs. Néanmoins pour injecter des scalaires automatiquement, il suffit que ces derniers soit déclarés aussi. Ex :

### _instanceof

Pour ajouter un tag ou injecter un service si on implémente une interface. Ex :
Ici, toutes les classes qui implémentent LoggerAwareInterface verront leurs méthodes setLogger(LoggerInterface $logger) appelées automatiquement à l’instanciation.

### En SF <2.8

Les contrôleurs sont des services qui peuvent en appeler avec la méthode héritée de leur classe mère :
Pour déterminer si un service existe depuis un contrôleur :

## Paramètres

Chaque service doit donc être déclaré avec un paramètre "class", puis peut ensuite facultativement contenir les paramètres suivants :
Paramètres des services en YAML :
| Nom | Rôle |
| class | Nom de la classe instanciée par le service. |
| arguments | Tableau des arguments du constructeur de la classe, services ou variables. |
| calls | Tableau des méthodes de la classe à lancer après l'instanciation, généralement des setters. |
| factory | Instancie la classe depuis une autre classe donnée. Méthode statique de la classe qui sera renvoyée par le service. |
| configurator | Exécute un invocable donné après l'instanciation de la classe. |
| alias | Crée un autre nom pour un service, qui peut alors être modifié par d'autres paramètres de déclaration (ex : créer une version publique d'un service privé dans services_test.yaml). |
| parent | Nom de la superclasse. |
| abstract | Booléen indiquant si la méthode est abstraite. |
| public | Booléen indiquant une portée publique du service. |
| shared | Booléen indiquant un singleton. |
| tags | Quand on doit injecter un nombre indéterminé de services dans un autre, il est possible de le définir avec chacun des services à injecter, en y ajoutant un tag avec le nom du service qui peut les appeler. Ce tag doit néanmoins être défini dans un CompilerPass. |
| autowire | Booléen vrai par défaut, spécifiant si le framework doit injecter automatiquement les arguments du constructeur. |
| decorates | Remplace un service par sa version décorée (mais l'ancien est toujours accessible an ajoutant le suffixe .inner au service décorateur) |

### Injecter des services tagués

Dans un constructeur :
Dans une autre méthode :
Par défaut, l'itérateur contient des clés numériques, mais on peut les personnaliser. Ex :

### Service abstrait

Un service abstrait est un système de factorisation des injections par l'intermédiaire d'une classe abstraite. Par exemple si on veut que tous les contrôleurs héritent du service logger (comme l'exemple _instanceof ci-dessus), plus la méthode setLogger de leur classe abstraite, sans avoir à toucher à leurs constructeurs :

## Références

* ↑ https://symfony.com/doc/3.4/service_container.html
* ↑ https://symfony.com/doc/current/service_container/autowiring.html
* ↑ https://symfony.com/doc/current/service_container/autowiring.html#fixing-non-autowireable-arguments
* ↑ https://symfony.com/doc/current/service_container/factories.html
* ↑ https://symfony.com/doc/current/service_container/configurators.html
* ↑ https://symfony.com/doc/current/testing.html
* ↑ https://symfony.com/doc/current/service_container/compiler_passes.html
* ↑ https://symfony.com/doc/current/service_container/service_decoration.html
* ↑ https://symfony.com/doc/5.4/service_container/tags.html#tagged-services-with-index