Les namespaces ou espaces de noms en PHP 8. L'autoloading.
#1 Les namespaces ou espaces de noms : Pour quoi faire ?
Souvent, lorsque l'on utilise des librairies, que l'on n'a pas développées soi-même, on a des télescopages au niveau des noms. Par télescopage, je veux dire que l'on se retrouve par exemple avec deux classes qui portent le même nom. Ca peut être des noms de classes, des noms de fonctions ou bien des noms de constantes. Et quand je dis classes, eh bien, c'est classes au sens large, c'est-à-dire classes, traits ou interfaces.
Les namespaces apportent une solution à ce problème, en mettant un préfixe devant le nom de la classe, de la fonction ou bien de la constante 😃.
Ensuite qu'est-ce qui s'est passé ? Eh bien, on a décidé d'utiliser ce préfixe pour lui faire porter une information supplémentaire. En fait, on a décidé de choisir ce préfixe de manière à pouvoir retrouver le chemin qui permet d'atteindre l'emplacement de la classe dans notre application. Et du coup, on s'est servi des espaces de noms pour automatiser les require
. C'est ce que l'on appelle l'autoloading.
Ci-dessous, on fait un exemple pour illustrer ce que je viens de dire.
- Je crée un répertoire
Test
sur le bureau. Ce sera mon espace de travail VSCode. - Je crée un fichier
index.php
, je mets dedans unecho
deBonjour
. - Je crée maintenant un répertoire
App
. - Dans
App
, je crée deux autres répertoiresClient
etProduit
. - Dans chacun de ces deux répertoires, je crée un fichier
Afficher.php
. - Dans chaque fichier PHP, je crée une classe
Afficher
. - Maintenant, je retourne dans
index.php
et je fais unrequire
de chaque classe. - J'exécute et je vois un message d'erreur, car deux classes portent le même nom.
Ci-dessous, pour éviter la collision, je vais déclarer l'espace de nom Produit
dans le fichier qui contient la classe Afficher
et qui est dans le répertoire Produit
. Je ne suis absolument pas obligé de prendre le nom du répertoire en tant que nom de l'espace de nom. Mais on verra que ça va nous arranger.
Par symétrie, je vais déclarer l'espace de nom Client
dans la classe Afficher
qui est dans le répertoire Client
. En fait, c'est comme ça qu'on travaille. On met une classe par fichier et on met un espace de nom en haut de ce fichier.
Maintenant, je peux instancier mes classes. Pour ça, je vais mettre l'espace de nom devant le nom de la classe. Ca va donner un new Produit\Afficher
et un new Client\Afficher
Alors, lorsque l'on met comme cela, le nom de l'espace de nom devant le nom de la classe, on dit que l'on utilise un FQCN pour Full Qualified Class Name.
Je vais également ajouter un constructeur dans chaque classe Afficher
. Dans ce constructeur, je vais mettre un echo
de la constante magique __NAMESPACE__
. Ceci va me permettre d'afficher, au moment du new
, l'espace de nom de chaque classe.
#2 L'autoloading
Maintenant, regardez ! Il ne manque pas grand-chose pour pouvoir reconstituer le chemin nécessaire pour le require
en utilisant le FQCN de la classe. Il suffit d'ajouter App
devant, puis de changer les backslashes en slashes et enfin d'ajouter .php
à la fin.
Alors App
, c'est moi qui vais l'ajouter. Je vais faire coïncider les espaces de noms avec les répertoires. Et le reste va être fait par l'appel à la fonction spl_autoload_register()
. Je ne rentre pas dans les détails.
Dans ces conditions, à chaque new
, un require_once
du fichier qui contient la classe est exécuté et la classe est autoloadée. Le chemin du require
est retrouvé au moyen du FQCN de la classe. De cette manière, les require
ne sont plus faits tous en même temps en haut du fichier.
#3 Comment utiliser correctement un espace de nom ?
- Vous devez respecter les règles de nommage de PHP que vous utilisez pour les noms des variables, des fonctions ect.
- Par convention, écrivez le nom en UpperCamelCase (* une majuscule à la première lettre en plus du camelCase). Attention, car PHP n'est pas case sensitive sur le nom de l'espace de nom. Il ne vous dira rien si vous faites une erreur.
-
declare
. Par exemple,declare(strict_types=1);
pour activer le mode strict. Même unsession_start();
il faut le mettre après. Même un espace devant la balise d'ouverture<?php
ne passera pas. - Vous pouvez utiliser le même espace de nom dans plusieurs fichiers différents. Ca vous permet de mettre plusieurs classes dans le même espace de nom et c'est utile. Ci-dessous, je compète mon exemple avec une nouvelle classe
Tester
, que je place dans l'espace de nomProduit
. - Vous pouvez aussi mettre plusieurs espaces de noms dans le même fichier, PHP ne l'interdit pas, mais en fait, on ne fait jamais ça.
- Enfin, il y a une autre façon de mettre en place un espace de nom. Il est possible de mettre le contenu de l'espace de nom entre accolades comme ci-dessous. Je ne m'étendrai pas sur cette méthode, car je l'ai rarement vu utilisée.
#4 L'espace de nom global
En fait, lorsque vous n'utilisez pas les namespaces, vous vous trouvez quand même dans un espace de nom global
. Et si vous ne connaissez pas le principe des namespaces, eh bien, vous ne le savez même pas 🙃.
Ci-dessous, dans le fichier index.php
, je vais déclencher une Exception('Mon Erreur')
. Je suis dans global
bien que je n'ai fait aucune déclaration de namespace. J'exécute et logiquement ça fonctionne correctement, car la classe Exception
est distribuée en natif dans global
.
Ci-dessous, je vais déclarer un espace de nom Index
dans le fichier index.php
. Et là, oups, je vois qu'il y a quatre erreurs (* en fait, c'est toujours à-peu-près la même). PHP ne voit plus mes classes Afficher
et Tester
et il ne voit plus la classe Exception
.
- La classe
Exception
, il ne la trouve plus car il la cherche dans l'espace de nomIndex
et qu'en fait, elle se trouve dans l'espace de nomglobal
. Notez bien que je suis obligé d'ajouter un backslashe pour que PHP aille la chercher dansglobal
. - C'est à peu près pareil pour les classes
Afficher
etTester
. Etant donné qu'à présent, je suis dansIndex
et que j'ai écrit mes FQCN en relatif, pour la classeProduit
par exemple, PHP cherche dansIndex\APP\Produit
. Il faut passer les FQCN en absolu. Pour ça il faut ajouter un backslashe devant.
Un FQCN exprimé en relatif est toujours évalué en le préfixant avec le namespace dans lequel il se trouve.
- Ci-dessous, nous sommes dans
global
. Que vous écriviezClient\Afficher
ou bien\Client\Afficher
ne change rien. (* De toute façon le premier backslashe sera toujours supprimé. Il ne sera pas fourni à l'autoloader car le chemin à utiliser parrequire
doit être relatif). - Ci-dessous,
Index
est ajouté au FQCN relatif.
#5 Travailler avec des fonctions ou des constantes
Ci-dessous, PHP va chercher la fonction strlen()
qui est dans global
sans que j'aie besoin de le préciser. Souvenez-vous, que juste un peu plus haut dans le tuto, il ne l'a pas fait pour la classe Exception
!
Ci-dessous, je déclare une fonction strlen()
dans Index
. PHP va prendre celle-là sauf si je précise \
.
Conclusion : Quand il y a un appel à une fonction dans un espace de nom, PHP la cherche dans l'espace de nom courant, sinon il va la chercher là où on le lui dit, par exemple ici, dans global
.
#6 Utiliser des use et des alias
A un moment, utiliser les FQCN va alourdir le code, surtout s'ils sont longs. On va pouvoir éviter ça, en utilisant des use
.
Ci-dessous, j'utilise un premier use
pour dire à PHP que lorsqu'il doit instancier la classe Tester
, eh bien, il s'agit de la classe \App\Produit\Tester
.
Maintenant, je mets un use
pour les classes Afficher
. Mais si je fais ça, tel quel, je me retrouve à nouveau avec deux classes qui portent le même nom. La solution ici, c'est de compléter le use
, par un alias. Ici encore, je vais en faire deux par symétrie.
Notez bien que le code des classes ne change pas (* ci-dessous, je ne l'ai même pas inséré car c'est le même). Et c'est bien là l'intérêt. Vous importez des classes, il y a collision, vous faites un alias et vous ne touchez pas au code que vous importez.
Si vous voulez connaître, le FQCN qui est passé à l'autoloader au moment d'un new
, alors ajoutez un callback à la fonction spl_auto_register
, comme ci-dessous.
Pour plus d'infos, voir les règles de résolutions dans le manuel PHP.