Namespace en PHP : Présentation et application des espaces de noms
Par Francis Besset le jeudi 21 janvier 2010, 10:09 - Dev - Lien permanent

Les Namespaces sont l'équivalent d'un système de fichiers pouvant contenir plusieurs fichiers portant le même nom mais dans des dossiers différents. Ainsi, pour accéder à l'un de ces fichiers, il vous faudra indiquer le chemin complet, puisque les fichiers de même noms ne peuvent pas coexister dans un même dossier.
Ce même principe existe dans le monde de la programmation.
Cependant, avant PHP 5.3.0, il est impossible d'avoir deux fonctions, classes, constantes ou bibliothèques portant des noms identiques. C'est donc pour cette raison que les espaces de noms autrement dit les namespaces ont été créé à partir de PHP 5.3.0. Cette fonctionnalité existe déjà dans d'autres langages de programmations tels que Java, C++, etc.
Cette fonctionnalité permet d'éviter les collisions de noms entre le code que vous créez, les classes, fonctions ou constantes internes de PHP, ou celle de bibliothèques tierces. L'intérêt supplémentaire est de créer des alias ou de raccourcir des noms extrêmement longs. Pour finir, c'est un bon moyen de regrouper des fonctions, des classes et des constantes.
Les espaces de noms doivent être déclarées avant tout autre code. Le meilleur moyen est donc de le faire au début du fichier. Le même namespace peut être défini dans plusieurs fichiers afin de fragmenter ce même namespace.
- Syntaxe valide :
<?php
namespace MonNamespace;
const MA_CONSTANTE = 1;
class MaClasse { /* ... */ }
function ma_fonction() { /* ... */ }
?>
- Syntaxe invalide :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<?php
namespace MonNamespace; // Erreur fatale car le namespace doit-être le premier élément du script.
?>
Un namespace peut contenir une hiérarchie, ce qui permet de créer des sous-niveaux.
<?php
namespace MonNamespace\Niveau1\Niveau2;
const MA_CONSTANTE = 1;
class MaClasse { /* ... */ }
function ma_fonction() { /* ... */ }
?>
Dans l'exemple ci-dessus, la constante MonNamespace\Niveau1\Niveau2\MA_CONSTANTE, la classe MonNamespace\Niveau1\Niveau2\MaClasse et la fonction MonNamespace\Niveau1\Niveau2\ma_fonction sont créées.
Cela permet d'avoir un namespace répertoriant un thème avec différentes spécialisations.
Par ailleurs, vous remarquerez que les niveaux sont séparés par des antislashes \. Je trouve cette syntaxe un peu dommage car un point aurait été tellement plus apprécié. Tant pis.
Déclarer plusieurs namespaces dans une même script :
- Première syntaxe :
<?php
namespace MonProjet;
const CONNEXION_OK = 1;
class Connexion { /* ... */ }
function connecte() { /* ... */ }
namespace AutreProjet;
const CONNEXION_OK = 1;
class Connexion { /* ... */ }
function connecte() { /* ... */ }
?>
- Deuxième syntaxe :
<?php
namespace MonProjet {
const CONNEXION_OK = 1;
class Connexion { /* ... */ }
function connecte() { /* ... */ }
}
namespace AutreProjet {
const CONNEXION_OK = 1;
class Connexion { /* ... */ }
function connecte() { /* ... */ }
}
?>
Personnellement, je préfère la seconde syntaxe. Grâce aux accolades, on voit mieux ce qu'englobe nos namespaces.
Par ailleurs, il est recommandé de ne pas mélanger plusieurs namespaces dans un même fichier. Le mieux est de créer un fichier portant le nom du namespace contenu.
Combiner plusieurs codes sans espaces de noms dans du code avec espace de noms :
Seule la syntaxe à accolades est supportée. Le code global doit être encadré par un espace de noms sans nom :
<?php
namespace MonProjet {
const CONNEXION_OK = 1;
class Connexion { /* ... */ }
function connecte() { /* ... */ }
}
namespace { // code global
session_start();
$a = MonProjet\connecte();
echo MonProjet\Connexion::start();
}
?>
Voilà une bonne raison d'adopter systématiquement les accolades.
Vous remarquerez dans le script ci-dessus la notation d'utilisation d'un namespace.
Utilisation de l'instruction declare avec les namespaces :
Précédemment, je vous ai dit qu'un namespace devait être déclaré avant tout autre code. C'est presque juste, à une exception prêt ! On peut utiliser l'instruction declare avant un namespace :
<?php
declare(encoding='UTF-8');
namespace MonProjet {
const CONNEXION_OK = 1;
class Connexion { /* ... */ }
function connecte() { /* ... */ }
}
namespace { // code global
session_start();
$a = MonProjet\connecte();
echo MonProjet\Connexion::start();
}
?>
Namespace avec un sous-namespace :
Entrons plus dans en détail et voyons maintenant l'utilisation d'un espace de noms avec un sous-espace de noms :
- script1.php :
<?php
namespace Foo\Bar\sousespacedenoms;
const FOO = 1;
function foo() { /* ... */}
class Foo {
static function methodeStatique() { /* ... */ }
}
?>
- script2.php :
<?php
namespace Foo\Bar;
include 'script1.php';
const FOO = 2;
function foo() { /* ... */ }
class Foo {
static function methodeStatique() { /* ... */ }
}
/* nom non qualifié */
foo(); // Devient Foo\Bar\foo
Foo::methodeStatique(); // Devient Foo\Bar\Foo, méthode methodeStatique
echo FOO; // Devient la constante Foo\Bar\FOO
/* nom qualifié */
sousespacedenoms\foo(); // Devient la fonction Foo\Bar\sousespacedenoms\foo
sousespacedenoms\Foo::methodeStatique(); // Devient la classe Foo\Bar\sousespacedenoms\Foo, méthode methodeStatique
echo sousespacedenoms\FOO; // Devient la constante Foo\Bar\sousespacedenoms\FOO
/* nom absolu */
\Foo\Bar\foo(); // Devient la fonction Foo\Bar\foo
\Foo\Bar\Foo::methodeStatique(); // Devient la classe Foo\Bar\Foo, méthode methodeStatique
echo \Foo\Bar\FOO; // Devient la constante Foo\Bar\FOO
?>
Accéder à n'importe quelle fonction, classe ou constante globale :
Si vous avez besoins d'accéder à un élément global, un nom absolu peut être utilisé.
<?php
namespace Foo;
function strlen() { /* ... */ }
const INI_ALL = 3;
class Exception { /* ... */ }
$a = \strlen('hi'); // Appel la fonction globale strlen
$b = \INI_ALL; // Accès à une constante INI_ALL
$c = new \Exception('Error'); // Instantie la classe globale Exception
?>
Accès dynamiques et accès des namespaces :
Pour bien comprendre l'application des namespaces, suivez bien ce qui suit. Il s'agit de voir la différence entre les accès dynamiques et les accès à des namespaces.
- script3.php (Accès dynamique) :
<?php
class ClassName {
function __construct() {
echo __METHOD__,"\n";
}
}
function func_name() {
echo __FUNCTION__,"\n";
}
const CONSTANTE_NAME = "global";
$a = 'ClassName';
$obj = new $a; // Affiche ClassName::__construct
$b = 'func_name';
$b(); // Affiche func_name
echo constant('CONSTANTE_NAME'), "\n"; // Affiche global
?>
- script4.php (Accès dynamique à des namespaces) :
<?php
namespace NomNamespace;
class ClassName {
function __construct() {
echo __METHOD__,"\n";
}
}
function func_name() {
echo __FUNCTION__,"\n";
}
const CONSTANTE_NAME = "namespaced";
include 'script3.php';
$a = 'ClassName';
$obj = new $a; // Affiche ClassName::__construct
$b = 'func_name';
$b(); // affiche func_name
echo constant('CONSTANTE_NAME'), "\n"; // Affiche global
/* Note que si vous utilisez des guillemets doubles, "\\NomNamespace\\ClassName" doit être utilisé */
$a = '\NomNamespace\ClassName';
$obj = new $a; // Affiche NomNamespace\ClassName::__construct
$a = 'NomNamespace\ClassName';
$obj = new $a; // Affiche aussi NomNamespace\ClassName::__construct
$b = 'NomNamespace\func_name';
$b(); // Affiche NomNamespace\func_name
$b = '\NomNamespace\func_name';
$b(); // Affiche aussi NomNamespace\func_name
echo constant('\NomNamespace\CONSTANTE_NAME'), "\n"; // Affiche namespaced
echo constant('NomNamespace\CONSTANTE_NAME'), "\n"; // Affiche aussi namespaced
?>
Ça va vous êtes toujours là ? Allez on garde les yeux ouverts et on continue car là, on en est juste à la moitié !
Utilisation de la constante __NAMESPACE__ :
Cette constante permet de retourner le nom du namespace courant.
- Exemple 1 (Utilisation de __NAMESPACE__ dans un code avec espace de noms) :
<?php
namespace MonProjet;
echo '"', __NAMESPACE__, '"'; // Affiche "MonProjet"
?>
- Exemple 2 (Utilisation de __NAMESPACE__ dans un code avec espace de noms global) :
<?php
echo '"', __NAMESPACE__, '"'; // Affiche ""
?>
- Exemple 3 (Utilisation de __NAMESPACE__ pour une construction dynamique de noms) :
<?php
namespace MonProjet;
function get($className) {
$a = __NAMESPACE__ . '\\' . $className;
return new $a;
}
?>
- Exemple 4 (Utilisation de l'opérateur namespace dans un espace de noms :
<?php
namespace MonProjet;
use blah\blah as mine; // Voyez "Utilisation des espaces de noms : importation et alias"
blah\mine(); // Appelle la fonction MonProjet\blah\mine()
namespace\blah\mine(); // Appelle la fonction MonProjet\blah\mine()
namespace\func(); // Appelle la fonction MonProjet\func()
namespace\sub\func(); // Appelle la fonction MonProjet\sub\func()
namespace\cname::method(); // Appelle la méthode statique "method" de la classe MonProjet\cname
$a = new namespace\sub\cname(); // Instantie un objet de la classe MonProjet\sub\cname
$b = namespace\CONSTANT; // Assigne la valeur de la constante MonProjet\CONSTANT à $b
?>
- Exemple 5 (Utilisation de l'opérateur namespace dans l'espace de noms global) :
<?php
namespace\func(); // Appelle la fonction func()
namespace\sub\func(); // Appelle la fonction sub\func()
namespace\cname::method(); // Appelle la méthode statique "method" de la classe cname
$a = new namespace\sub\cname(); // Instantie un objet de la classe sub\cname
$b = namespace\CONSTANT; // Assigne la valeur de la constante CONSTANT à $b
?>
Importation et Alias :
- Exemple 1 (Importation et alias avec l'opérateur use) :
<?php
namespace Foo;
use My\Full\Classname as Another;
// Ceci est la même chose que use My\Full\NSname as NSname
use My\Full\NSname;
// Importation d'une classe globale
use \ArrayObject;
$obj = new namespace\Another; // Instantie un objet de la classe foo\Another
$obj = new Another; // Instantie un objet de la classe My\Full\Classname
NSname\subns\func(); // Appelle la fonction My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // Instantie un objet de la classe ArrayObject
// Sans l'instruction "use \ArrayObject" nous aurions instantié un objet de la classe Foo\ArrayObject
?>
- Exemple 2 (Importation et alias multiples avec l'opérateur use) :
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // Instantie un objet de la classe My\Full\Classname
NSname\subns\func(); // Appelle la fonction My\Full\NSname\subns\func
?>
- Exemple 3 (Importation et noms d'espaces dynamiques) :
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // Instantie un objet de la classe My\Full\Classname
$a = 'Another';
$obj = new $a; // Instantie un objet de la classe Another
?>
- Exemple 4 (Importation et noms d'espaces absolus) :
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // Instantie un objet de la classe My\Full\Classname
$obj = new \Another; // Instantie un objet de la classe Another
$obj = new Another\untruc; // Instantie un objet de la classe My\Full\Classname\untruc
$obj = new \Another\untruc; // Instantie un objet de la classe Another\untruc
?>
Utilisation de namespace global au sein d'un namespace :
<?php
namespace A\B\C;
/* Cette fonction est A\B\C\fopen */
function fopen() {
/* ... */
$f = \fopen(...); // Appel à fopen global
return $f;
}
?>
- Exemple 1 (Accès aux classes globales depuis un espace de noms) :
<?php
namespace A\B\C;
Class Exception extends \Exception { /* ... */ }
$a = new Exception('hi'); // $a est un objet de la classe A\B\C\Exception
$b = new \Exception('hi'); // $b est un objet de la classe Exception
$c = new ArrayObject; // Erreur fatale, classe A\B\C\ArrayObject non trouvée
?>
- Exemple 2 (Accès aux fonctions et constantes globales dans un espace de noms) :
<?php
namespace A\B\C;
const E_ERROR = 45;
function strlen($str) {
return \strlen($str) - 1;
}
echo E_ERROR, "\n"; // Affiche "45"
echo INI_ALL, "\n"; // Affiche "7" : accès dans l'espace de noms global INI_ALL
echo strlen('hi'), "\n"; // Affiche "1"
if (is_array('hi')) { // Affiche "n'est pas un tableau"
echo "Est un tableau\n";
} else {
echo "N'est pas un tableau\n";
}
?>
Règle de résolution de noms :
Pour finir, je vous conseille fortement de lire les règles de résolutions de noms afin de mieux comprendre la façon dont les noms sont résolus.
Je peux comprendre que cet article vous donne mal à la tête. Cependant c'est vraiment important d'assimiler cette fonctionnalité car elle risque d'être très présente dans les CMS fonctionnant à partir PHP 5.3.0.
Pour ma part, je vais prendre une boite de doliprane !
(php.net)


