Programmation orientée objet en PHP

Auteur: Mohamed CHINY Durée necessaire pour le cours de Programmation orientée objet en PHP Niveau recommandé pour le cours de Programmation orientée objet en PHP Supports vidéo non disponibles pour ce cours Exercices de renforcement disponibles pour ce cours Quiz non disponibles pour ce cours

Page 14: Les espaces de noms (namespaces)

Toutes les pages

Les espaces de noms en PHP (Namespaces)

Introduction

Imaginez que sur la racine de votre espace de stockage (C: sur Windows ou / sur Linux par exemple) vous avez déposé un fichier du nom de 'monCV.pdf'. Donc, si vous voulez déposer un autre fichier du même nom au même endroit votre système d'exploitation ne vous le permettra pas, ou au moins, il anticipera et renommera le nouveau document pour éviter tout conflit. Mais si tout de même vous voulez avoir une copie du fichier avec le même nom alors la solution qui semble évidente est de créer un dossier et y déposer votre fichier. Dans ce cas, même si votre fichier est présent deux fois sur votre ordinateur avec le même nom, cela ne créera pas de problème car ils ont été déposés dans des répertoires différents.

Par analogie, en programmation on ne peut pas créer deux fonctions de même nom (de même pour les classes et les constantes). La solution consiste donc soit à renommer nos éléments ou, mieux encore, à les déposer dans des espaces différents (comme des répértoires sur un système de fichiers). On appelle ces espaces des espaces de noms (ou namespaces).

Un espace de nom c'est quoi?

Un espace de nom ou namespace représente un moyen de séparer ses éléments au sein du code de telle sorte à éviter les conflits (ou collisions). Ces collisions sont dues à des duplications de noms (ou identifiants) d'éléments comme les fonctions, les constantes ou les classes.
Selon le langage de programmation, les variables ne génèrent pas souvent des conflits, car le faite d'évoquer une variable avec un nom de variable déjà existante fait tout simplement écraser l'ancienne par le nouvelle.
Les namespaces font alors en sorte de créer comme des répertoires abstraits qui permettent d'encapsuler les éléments et prévenir ainsi tout risque de collision. Toutefois, ils ont aussi un autre rôle qui consiste à créer des alias (des noms alternatifs) plus simples pour les éléments qui ont des noms trop longs ou trop compliqués.

Les namespaces ne sont pas propres qu'au PHP, mais de nombreux langages les prennent en charge. D'ailleurs l'implémentation des namespaces en PHP est inspirée du C++.

Quand est ce qu'aura-t-on vraiment besoin des namespaces?

On peut se service des espaces de noms quand on veut, mais les développeurs expérimentés les déconseillent sur des petits projets, car on risque de se retrouver avec de nombreux namespaces qui encapsulent chacun un ou deux éléments, ce qui peut donner lieu à du travail supplémentaire qui consiste à se déplacer entre ces espaces là bien que le risque de collision n'était pas vraiment considérable. Donc, le jeu n'en vaut pas vraiment la chandelle. Cependant, on peut y recourir des les cas suivants:
  • Dans un gros projet: où le nombre de fonctions, de constantes et de classes mises en jeu est énorme, donc le risque de collision est élevé.
  • Dans des projets qui utilisent des librairies externes: dans ce cas, il se peut que l'on utilise des éléments qui peuvent avoir le même nom que leur homologues qui sont embarquées dans la librairie.

Toutefois, il n'existe pas une règle fixe qui dicte quand est ce qu'on doit vraiment se servir des namespaces. Alors, ça revient à vous d'estimer quand est ce qu'ils pourront vous être utiles.

Utiliser les namespaces

Définition d'un namespace

Pour définir un namespace on utilise le mot-clé namespace suivi du nom de celui-ci. Le nom peut être choisi de la même façon qu'une fonction (lettres, chiffres et caractères souligné).
Exemple:
<?php
   namespace monEspace;
?>
Dans ce cas, toutes les fonctions, constantes et classes qui vont suivre cette déclaration feront partie du namespace monEspace.
La définition du namespace doit se faire avant d'envoyer aucun texte au navigateur. Il faut avoir l'habitude de toujours la déclarer au tout début du code.
Exemple:
<?php
   namespace monEspace;
   function ucfirst($str){
      echo "_".$str;
   }
   ucfirst("Bonjour");
?>
Si on exécute ce bout de code on aura:
_Bonjour
Normalement, la fonction ucfirst() est une fonction prédéfinie en PHP et permet de convertir le premier caractère de la chaine passée en argument en majuscule. Cependant, nous avons créé une fonction du même nom sans avoir de collision car on l'a fait à l'intérieur du namespace monEspace.

Espace de nom global

Dans l'exemple précédent, à chaque fois qu'on appellera la fonction ucfirst() elle préfixera la chaine passée en argument par le caractère souligné. Si toutefois on veut appeler la fonction native ucfirst() définie en PHP alors il faut se rendre à l'espace de nom global qui constitue la racine de tous les espaces de noms. Pour ce faire, nous allons préfixer notre fonction par un antislash (\).

Exemple:
<?php
   namespace monEspace;
   function ucfirst($str){
      echo "_".$str;
   }
   ucfirst("bonsoir"); // Notre fonction
   echo "<br />";
   echo \ucfirst("bonsoir"); // Fonction native au PHP
?>
Ce qui donne:
_bonsoir
Bonsoir
Ce résultat nous mène à conclure que toutes les fonctions natives au PHP sont définie dans le namespace global, et si on crée nos propres fonctions, constantes ou classes, alors elles sont par défaut créées dans le namespace global également.

Définir plusieurs namespaces dans le même document

Bien que c'est une pratique déconseillée, il est possible de définir autant de namespaces que l'on souhaite sur un même document. Pour ce faire, on peut recourir à deux techniques différentes:

Déclaration par blocs simples

Il s'agit de déclarer le namespace et le faire suivre du code voulue. Tout le code suivant fait partie de ce namespace. Pour changer de namespace, il suffit d'en déclarer un autre, et tout le code qui viendra ensuite appartiendra au dernier espace déclaré et ainsi de suite.

Exemple:
<?php
   namespace monEspace;
   // Code du namespace monEspace;

   namespace monAutreEspace;
   // Code du namespace monAutreEspace;
?>
Déclaration par accolades

Cette technique est préférée à la première. Elle consiste à renfermer tous les codes d'un namespace entre deux accolades.

Exemple:
<?php
   namespace monEspace{
      // Code du namespace monEspace;
   }

   namespace monAutreEspace{
      // Code du namespace monAutreEspace;
   }
?>

La constante __NAMESPACE__

Si on se trouve avec beaucoup de namespaces dans son projet, il est possible de s'y perdre et ne plus savoir dans quel espace on est. Heureusement, la constante __NAMESPACE__ et là. Elle sert à identifier dans quel espace on se trouve actuellement.

Exemple:
<?php
   namespace monEspace;
      echo __NAMESPACE__;
      echo "<br />";

   namespace monAutreEspace;
      echo __NAMESPACE__;   
?>
Ce qui donne:
monEspace
monAutreEspace

Parcourir les namespace

Dans l'exemple du namespace global on a vu que pour accéder à celui là on a utilisé le caractère antislash suivi du nom de la fonction souhaitée. De même, si on veut passer d'un namespace à un autre il suffit d'intercaler des antislashs (à la manière des dossiers sur un système de fichiers). Et on peut donc accéder à un namespace d'une manière relative ou absolue.

Exemple:
<?php
   namespace N1;
      function ucfirst($str){
         return "N1_".$str;
      }
      echo N2\ucfirst("bonjour"); // Fonction du namespace enfant (N2)
      echo "<br />";   


   namespace N1\N2;
      function ucfirst($str){
         return "N2_".$str;
      }
      echo ucfirst("bonjour");     // Fonction du namespace courant (N2)
      echo "<br />";
      echo N1\ucfirst("bonjour"); // Fonction du namespace parent (N1)
      echo "<br />";
      echo \ucfirst("bonjour");    // Fonction du namespace global
      echo "<br />";

?>
Ce qui donne le résultat suivant:

N2_bonjour
N2_bonjour
N1_bonjour
Bonjour

Définir les alias

Si on se trouve avec de trop longs noms de fonctions, constantes ou classes, ou encore des imbrications de beaucoup de namespaces, leur manipulation devient fastidieuse et peut même induire à l'erreur, c'est pourquoi l'utilisation des alias s'avère utile pour simplifier ces noms là.

Pour définir un alias on fait appel à la directive use comme ceci use nom_réél as alis.

Exemple:
<?php
   namespace N1\N2\N3\N4;
      function ucfirst($str){
         return "N4_".$str;
      }   

   namespace N1;
      use N1\N2\N3\N4 as MNS;
      echo MNS\ucfirst("bonjour");

?>
Après éxecution on obtient:
N4_bonjour
En effet, nous avons remplacé la séquence N1N2N3N4 par MNS qui est plus courte et plus claire.