Les Namespaces
Présentation
Un espace de noms (namespace) est un mécanisme permettant d'éviter les conflits de noms dans un programme PHP. En effet, dans un même projet, il est possible d'avoir plusieurs fonctions, classes ou constantes portant le même nom, ce qui peut entraîner des erreurs de redéfinition.
Les namespaces résolvent ce problème en permettant d'organiser le code en différentes sections isolées. Chaque élément du code peut être encapsulé dans un espace de noms spécifique, évitant ainsi toute collision avec des éléments portant le même nom ailleurs dans le projet.
L'utilisation des namespaces est particulièrement utile dans les grands projets et lors de l'intégration de bibliothèques externes. En structurant le code de manière claire, ils facilitent la maintenance et la lisibilité du projet.
Déclarer un Namespace
Pour déclarer un namespace, il suffit d'utiliser l'instruction namespace au début d'un fichier PHP, avant toute autre déclaration (sauf declare(strict_types=1)). Cela encapsule tout le code contenu dans ce fichier à l'intérieur de cet espace de noms.
Il est recommandé d'écrire les noms de namespaces en PascalCase (première lettre de chaque mot en majuscule), en cohérence avec les conventions de nommage des classes.
Il est courant de faire correspondre la structure des namespaces avec l'arborescence des fichiers et dossiers. Cela permet une meilleure organisation et facilite l'auto-chargement des classes grâce à la norme PSR-4. Cette norme définit une convention standard pour associer un namespace à un chemin de fichier, permettant ainsi aux classes d'être chargées dynamiquement sans nécessiter de nombreux require ou include. Un exemple d'implémentation courante de PSR-4 est l'auto-chargement des classes avec Composer.
Par exemple, supposons que notre projet ait la structure suivante :
projet/
├── core/
│ ├── GestionBdd.php
│ ├── GestionFormulaire.php
│ └── GestionAuthentification.php
Dans ce cas, le namespace des classes contenues dans le dossier core pourrait être Core. Voici comment déclarer un namespace dans un fichier PHP :
<?php
// fichier : projet/core/GestionBdd.php
namespace Core;
class GestionBdd
{
// ...
}
?>
Ainsi, toutes les classes du dossier core/ peuvent partager le même namespace Core, ce qui facilite leur identification et leur utilisation dans le reste du projet.
Importer et Utiliser les Éléments d'un Namespace
Lorsque vous souhaitez utiliser une classe, une fonction ou une constante provenant d'un autre espace de noms, vous devez d'abord importer le fichier qui la contient. Ensuite, vous devez préciser son namespace pour l'appeler correctement.
Par exemple, si l'on souhaite utiliser la classe GestionBdd située dans le namespace Core, il faudra l'appeler en précisant son namespace complet :
<?php
// fichier : projet/app/Models/UtilisateurModel.php
namespace App\Models;
// Importer le fichier contenant la classe GestionBdd.
require_once dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'GestionBdd.php';
class UtilisateurModel
{
// ...
public static function creerUtilisateur(string $pseudo, string $email, string $mdp): bool
{
// ...
// Appel de la classe "GestionBdd" en la préfixant avec son namespace complet.
return Core\GestionBdd::modifierDansTable($requete, [
'pseudo' => $pseudo,
'email' => $email,
'motDePasse' => $mdp
]);
}
// ...
}
?>
Éviter les Conflits avec les Namespaces
Dans certains cas, vous pourriez avoir deux classes portant le même nom mais provenant de namespaces différents. Par exemple, vous pourriez avoir une classe GestionMessage que vous avez créée pour gérer des messages système, tandis qu'une bibliothèque externe que vous utilisez possède également une classe du même nom.
Si vous importez ces deux classes sans spécifier leur namespace, PHP génèrera une erreur de redéfinition. Voici comment utiliser les namespaces pour éviter ce conflit :
<?php
require_once dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'GestionMessage.php';
require_once dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'BlogManager2000' . DIRECTORY_SEPARATOR . 'GestionMessage.php';
// Appel de la classe "GestionMessage" créée pour gérer les messages du projet.
Core\GestionMessage::maMethode();
// Appel de la classe "GestionMessage" provenant de la bibliothèque BlogManager2000.
BlogManager2000\GestionMessage::maMethode();
?>
Utilisation des Classes intégrées à PHP
Lorsque vous utilisez une classe intégrée à PHP (comme PDO), ou une classe qui n'a pas de namespace défini, vous devez la préfixer avec un antislash \ pour indiquer à PHP qu'il doit chercher la classe dans l'espace global :
<?php
// fichier : projet/core/GestionBdd.php
namespace Core;
class GestionBdd
{
public static function charger_bdd(): ?\PDO
{
$config = include(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'bdd.php');
try
{
$pdo = new \PDO("mysql:host={$config['serveur']};dbname={$config['bdd']};charset=utf8", $config['utilisateur'], $config['motDePasse']);
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
return $pdo;
}
catch(\PDOException $erreur)
{
// Relancer l'exception pour qu'elle soit capturée par la fonction parente.
throw $erreur;
}
}
// ...
}
?>
Importer un Namespace avec use
Plutôt que de devoir toujours préciser le namespace complet lorsqu'on utilise une classe, on peut le simplifier avec l'instruction use. Cela améliore la lisibilité du code en affichant directement les dépendances en début de fichier.
<?php
// fichier : projet/app/Models/UtilisateurModel.php
namespace App\Models;
use Core\GestionBdd;
// Importer le fichier contenant la classe GestionBdd.
require_once dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'GestionBdd.php';
class UtilisateurModel
{
// ...
public static function creerUtilisateur(string $pseudo, string $email, string $mdp): bool
{
// Maintenant, on peut appeler la classe sans préfixer son namespace.
return GestionBdd::modifierDansTable($requete, [
'pseudo' => $pseudo,
'email' => $email,
'motDePasse' => $mdp
]);
}
// ...
}
?>
Importer Plusieurs Éléments d'un Namespace
Si plusieurs classes d'un même namespace sont utilisées dans un fichier, il est possible de les importer en une seule instruction use :
<?php
use Core\{
GestionVue,
GestionFormulaire,
GestionMessage
};
require_once dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'GestionVue.php';
require_once dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'GestionFormulaire.php';
require_once dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'GestionMessage.php';
?>
Résolution des Conflits avec les Alias
Si plusieurs classes de noms identiques mais de namespaces différents doivent être utilisées dans un même fichier, on peut leur attribuer un alias avec le mot-clé as.
<?php
use Core\GestionMessage;
use BlogManager2000\GestionMessage as BlogGestionMessage;
require_once dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'GestionMessage.php';
require_once dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'BlogManager2000' . DIRECTORY_SEPARATOR . 'GestionMessage.php';
// Appel de la classe "GestionMessage" du projet.
GestionMessage::maMethode();
// Appel de la classe "GestionMessage" de la bibliothèque externe, renommée "BlogGestionMessage".
BlogGestionMessage::maMethode();
?>
Exercice : Projet Progressif 09
Bonus (facultatif)
- Objectif : Ajouter des namespaces à votre projet afin de mieux organiser votre code, d'éviter les conflits de noms et de clarifier la séparation des responsabilités.
- Poursuivre le projet progressif initié lors du chapitre sur les modèles de pages dynamiques.
- Adaptez le code pour répartir vos classes dans des namespaces cohérents et ajuster leurs inclusions de manière à assurer le bon fonctionnement de tout le projet.