Utiliser les espaces de noms en PHP

Les espaces de noms sont apparus avec PHP 5.3.0.
Comme dans tout langage moderne, on retrouve cette notion fondamentale puisqu’elle conduit à voir tout ce qui est déclaré non pas d’abord comme une instruction, une variable, un type, ou autre, mais comme quelque chose qui peut être adressé par un nom au sein d’une hiérarchie d’espaces de noms. Autrement dit, avant la déclaration de quelque chose, il y a la définition du nom par lequel on l’adresse. L’idée est de pouvoir éviter des collisions de noms entre factorisations sans avoir à recourir à des noms à rallonge (du fait de préfixes et/ou d’enchâssements).
Typiquement, plutôt que… :
const _Zoo_Birds_TGE = "The Great Eagle";
…pouvoir se contenter de… :
namespace Zoo\Birds;
const THE_GREAT_EAGLE = "The Great Eagle";
…pour pouvoir écrire :
echo (Zoo\Birds\THE_GREAT_EAGLE);
Comme toujours, le point de vigilance est que l’association entre la hiérarchie des fichiers et la hiérarchie des espaces de noms n’est qu’un cas particulier. Dans le cas de PHP, c’est encore plus clair qu’en Python ou en Java. En effet, il n’existe pas d’équivalent à import de Java ou de Python, qui ferait à la fois le travail de require () (inclusion du fichier et intégration des éventuels espaces de noms qui sont définis à l’arborescence des espaces de noms courante) et de use (création d’alias pour les espace de noms ainsi intégrés). D’ailleurs, PHP permet même de répartir le contenu d’un espace de noms entre plusieurs fichiers !
On peut contester l’utilité du concept. Dans un échange critique sur Stack Overflow, un contributeur prétend y voir une manifestation du culte du cargo en programmation. Intéressant, même si pour un anthropologue, cela peut sembler comme une réappropriation d’un concept assez douteuse – il vaudrait mieux parler de syndrome du singe savant, déjà évoqué ici.
Voyons voir dans le détail comment utiliser cette fonctionnalité…
Mise à jour du 14/03/2018 : Mention à ::class

L’usage de base

Rien ne doit précéder la définition d’un espace de noms, sinon la définition d’un autre espace de noms, comme évoqué plus loin.
Un espace de noms peut contenir des classes, des fonctions, des constantes.
On parle de nom :
  • non qualifié : pas d’espace de noms spécifié (ex: THE_GREAT_EAGLE) ;
  • qualifié : espace de nom relatif spécifié (ex: Birds\THE_GREAT_EAGLE) ;
  • totalement qualifié : espace de nom absolu spécifié (ex: \Zoo\Birds\THE_GREAT_EAGLE).
Utiliser \ pour naviguer dans une hiérarchie d’espaces de noms, tant pour définir que pour adresser (\ seul correspond à l’espace de noms global) :
namespace Zoo\Birds\Names;
const THE_GREAT_EAGLE = "The Great Eagle";

namespace Game;
echo (\Zoo\Birds\Names\THE_GREAT_EAGLE);
Quand un nom n’est pas défini dans l’espace de noms courant, sa résolution par défaut (fallback) suit des règles différentes selon ce qui est adressé :
  • une classe qui n’est pas définie dans l’espace courant ne sera recherchée dans le global que si son nom est totalement qualifié ;
  • une fonction ou une constante qui n’est pas définie dans l’espace courant sera recherchée dans le global même si son nom n’est pas totalement qualifié.
Ainsi, si zoo.php contient des définitions dans l’espace de noms global… :
class GreatEagle {}
function showBird () {}
const THE_GREAT_EAGLE = "The Great Eagle";
…alors dans index.php, dans le contexte d’un espace de noms distinct :
namespace Zoo {
	require ("zoo.php");
	echo (THE_GREAT_EAGLE);		// Succès : PHP recherche la constante dans l'espace de noms global
	showBird ();				// Succès : PHP recherche la constante dans l'espace de noms global
	$b = new GreatEagle ();		// Echec : PHP ne recherche pas la classe dans l'espace de noms global
	$b = new \GreatEagle ();	// Succès : PHP recherche la classe dans l'espace de noms global
}

Les usages avancés

Plusieurs espaces de noms distincts peuvent être définis à la suite, mais c’est déconseillé (ie : un fichier par espace plutôt), et il convient d’utiliser {...} pour plus de lisibilité (mais il faut alors le faire pour tous les espaces dans le fichier) :
namespace Zoo\Birds {
}

namespace Game {
}
Utiliser l’aliasing permet d’abréger les références :
use Zoo\Birds as zb;							// Pour un espace de noms
use Zoo\Birds\GreatEagle as GE;					// Pour une classe
use function Zoo\Birds\showBird as sB;			// Pour une fonction
use const Zoo\Birds\THE_GREAT_EAGLE as TGE;		// Pour une constate
Noter qu’à défaut d’avoir défini un alias, il faut spécifier un nom totalement qualifié pour adresser un contenu d’un espace de noms :
namespace Zoo\Birds {
const THE_GREAT_EAGLE = "The Great Eagle";
}

namespace Game {
echo (Zoo\Birds\THE_GREAT_EAGLE);		// Echec : Zoo n'est pas défini
echo (\Zoo\Birds\THE_GREAT_EAGLE);		// Succès : \Zoo est défini
use Zoo as Z;
echo (Z\Birds\THE_GREAT_EAGLE);			// Succès : Z est défini
}
Plusieurs classes / fonctions / constantes peuvent être importées en une fois (aliasing touours facultatif) :
use level0\level1\{Class0 as C0, Class1, Class2 as C2}
use function level0\level1\{function0, function1 as f1, function2}
use const level0\level1\{const0 as c0, const1 as c1, const2}
La constante magique __NAMESPACE__ fournit le nom de l’espace courant (on ne voit pas trop l’utilité…) :
namespace Zoo\Jurassic\Birds {
class Bird { function __construct () { echo (__NAMESPACE__); } }
}

namespace Zoo\Quaternary\Birds {
class Bird { function __construct () { echo (__NAMESPACE__); } }
}

namespace Game {
$period = ["Jurassic", "Quaternary"][rand  (0, 1)];
echo ($period."\n");
$birdClass = "\\Zoo\\$period\\Birds\\Bird";
$bird = new $birdClass ();
}
Dans le cas d’ue classe, ::class permet de récupérer le nom totalement qualifié de la classe (idem) :
namespace Zoo\MovingThings;
class Bird {}
echo (Bird::class);
Zoo\MovingThings\Bird
L’opérateur namespace permet de faire référence à l’espace de nom courant (on voit encore moins l’utilité…) :
namespace Game;
class GreatEagle {}
$bird = new namespace\GreatEagle ();
Un même espace de noms peut être rappelé dans différents fichiers, ce qui permet de répartir son contenu dans ces derniers :
  • dans birds.php :
    namespace Zoo\Birds;
    class GreatEagle {}
    
  • dans mamals.php :
    namespace Zoo\Mammals;
    class Lion {}
    
  • dans game.php :
  • require ("birds.php");
    require ("mammals.php");
    use Zoo as Z;
    $bird = new Z\Birds\GreatEagle ();
    $mamal = new Z\Mammals\Lion ();
    
Utiliser les espaces de noms en PHP