PHP 5.3 : namespace : les espaces de noms (partie 2)
Par Pascal MARTIN le mardi 25 novembre 2008, 07:30 - Développement Web - Lien permanent
Les exemples correspondant à ce point se trouvent dans le répertoire "namespace".
Voici la seconde partie de cet article traitant des namespaces, une des grosses nouveautés de PHP 5.3 — la première partie ayant été publiée hier, lundi 24 novembre 2008.
Nous avons vu hier comment déclarer des espaces de noms, et les utiliser pour regrouper des fonctions, des classes, et des constantes… Voici venu le moment de passer à la suite :
Sommaire de cette seconde partie :
- Espaces de noms et autoload
- Type-hinting
- Import d’un espace de noms : use
- Quelques mots sur l’ordre de résolution
- Faites attention !
- Quelques points qui n’existent pas
- Conlusion
Espaces de noms et autoload
Que ce soit pour éviter d’écrire de multiples require/include ou pour des raisons de performances, nous utilisons souvent, depuis PHP 5, une fonctionnalité nommée « autoload », pour charger automatiquement un fichier PHP contenant une définition de classe au moment où celle-ci devient utilisée par notre application.
Les avantages sont, principalement, les suivants :
- Vous avez, bien entendu, moins d’inclusions à écrire,
- Vous avez moins de cas particuliers à tester : à partir du moment où votre autoloading se fait correctement et que vous utilisez des classes existantes, vous limitez les risques d’inclusions multiples, ou d’oublis d’inclusions,
- Et, enfin, seul le code nécessaire est inclus ; PHP a donc moins de code à interpréter — ce qui ne peut être que positif pour les performances de votre application, et la charge serveur qu’elle entrainera.
L’autoloading se fait en déclarant une fonction qui reçoit en paramètre le nom de la classe à charger, et qui est responsable de l’inclusion du fichier contenant son code source.
Deux possibilités :
- Soit cette fonction est nommée
__autoload, et elle sera automatiquement considérée comme étant la fonction de chargement automatique, - Soit vous utilisez
spl_autoload_register(PHP >= 5.1.2) pour définir une ou plusieurs fonctions ou méthodes d’autoloading, qui s’empileront les unes au-dessus des autres.
Imaginons par exemple l’arborescence de fichiers suivante :
Nous enregistrerons comme méthode d’autoload la méthode statique Bootstrap::autoload, définie comme ceci :
<?php class Bootstrap { public static function autoload($className) { echo '<pre>Autoload : ' . $className; $tab = explode('\\', $className); $path = __DIR__ . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $tab) . '.php'; echo "\n => $path</pre>"; require $path; } }
Je disais plus haut qu’une fonction d’auto-chargement recevait en paramètre le nom de la classe qu’elle devait tenter de charger…
Et bien, cela ne change pas pour les classes se trouvant dans un espace de noms : en interne, leur nom est toujours « absolu », et contient l’intégralité du nom de l’espace de noms au sein duquel elles sont définies.
A partir de là, pour peu que l’arborescence des fichiers soit organisée de la même façon que vous espaces de noms et classes, il n’est pas difficile de déterminer quel fichier sera à inclure pour charger une classe : il suffit globalement d’exploser le nom complètement qualifié (Incluant le nom entier de l’espace de noms) de la classe sur le caractère séparateur de namespaces, l’antislash \ .
C’est précisément ce que fait la méthode définie un peu plus haut ^^
Maintenant, considérons que le fichier principal de notre application contienne le code suivant :
<?php require_once(__DIR__ . '/libs/autoload.php'); spl_autoload_register(array('Bootstrap', 'autoload')); $a = new LibA\SubLibA1\A1ClassGlop(); $a->method(); $a = new LibA\SubLibA2\A2ClassGlop(); $a->method(); echo '<hr />'; LibB\BMain::method(); echo '<hr />'; $a = new LibA\SubLibA1\X(); $a->method(); $b = new LibB\X(); $b->method();
Si l’on essaye de découper étapes par étapes, voyons ce qu’il se passera :
Tout d’abord, nous essayons d’instancier la classe A1ClassGlop, de l’espace de noms LibA\SubLibA1.
Cela entrainera l’auto-chargement du fichier libs/LibA/SubLibA1/A1ClassGlop.php[1], dont le contenu est le suivant :
<?php namespace LibA\SubLibA1; class A1ClassGlop { public function method() { var_dump(__NAMESPACE__ . ' \ ' . __CLASS__ . ' \ ' . __FUNCTION__); } }
L’appel à la méthode method entrainera donc l’affichage suivant :
string 'LibA\SubLibA1 \ LibA\SubLibA1\A1ClassGlop \ method' (length=50)
Passons maintenant à l’instanciation de la classe LibA\SubLibA1\A1ClassGlop.
Elle passera par l’auto-chargement du fichier libs/LibA/SubLibA2/A2ClassGlop.php, dont le contenu est reproduit ci-dessous :
<?php namespace LibA\SubLibA2; class A2ClassGlop { public function method() { var_dump(__NAMESPACE__ . ' \ ' . __CLASS__ . ' \ ' . __FUNCTION__); } }
Je vous laisse imaginer la suite
(au besoin, vous trouverez l’exemple complet dans le répertoire namespace/autoload-1/ de l’archive jointe à cet article — avec tous les autres exemples que j’ai utilisé au cours de cette série !)
En somme, pour résumer, la fonctionnalité d’autoload est toujours là, et elle fonctionne et peut être utilisée exactement de la façon dont nous étions habitués lorsque nous travaillions avec la convention de nommage de classes PEAR ; la différence est que, maintenant, nos classes sont organisées en espaces de noms, et que le séparateur est \ , et plus _.
Type-hinting
Je vois de plus en plus souvent des type-hint utilisés, pour indiquer, directement au sein des prototypes de méthodes, qu’elles attendent en paramètres des objets qui doivent être des instances d’une classe ou de l’une de ses classes filles, ou qui doivent implémenter une interface…
Cette possibilité ne disparait bien évidemment pas en PHP 5.3, et il est, en toute logique, possible d’utiliser des classes « namespacées » comme indications de types.
Par exemple, considérons le fichier lib-A.php reproduit ci-dessous :
<?php namespace NS_A; class ClassA { public function method() { var_dump(__NAMESPACE__ . ' \ ' . __CLASS__ . ' \ ' . __FUNCTION__); } }
Et le fichier lib-B.php dont voici le contenu :
<?php namespace NS_B; require_once(__DIR__ . '/lib-A.php'); class ClassB { // /!\ Il faut utiliser \NS_A\ClassA et non NS_A\ClassA, // puisque nous sommes actuellement dans NS_B ! public static function method(\NS_A\ClassA $a) { var_dump(__NAMESPACE__ . ' \ ' . __CLASS__ . ' :: ' . __FUNCTION__ ); var_dump($a); } }
Notez l’indication de type, basée sur une classe namespacée, sur le paramètre de ClassB::method !
Ces deux fichiers peuvent être utilisés depuis un fichier principal, ressemblant à ceci :
<?php require_once(__DIR__ . '/lib-A.php'); require_once(__DIR__ . '/lib-B.php'); $a = new NS_A\ClassA(); $a->method(); NS_B\ClassB::method($a);
Ici, aucun problème : ClassB::method reçoit une instance de \NS_A\ClassA, comme attendue, et nous obtenons l’affichage suivant :
string 'NS_A \ NS_A\ClassA \ method' (length=27) string 'NS_B \ NS_B\ClassB :: method' (length=28) object(NS_A\ClassA)[1]
Par contre, si nous avions passé autre chose qu’une instance de \NS_A\ClassA, par exemple comme ceci lors du second appel à ClassB::method :
<?php require_once(__DIR__ . '/lib-A.php'); require_once(__DIR__ . '/lib-B.php'); NS_B\ClassB::method('a'); // Catchable fatal error: Argument 1 passed to NS_B\ClassB::method() must be an instance of NS_A\ClassA, string given
Nous aurions alors obtenu une erreur de la forme suivante :
En effet, une chaine de caractères n’est de toute évidence pas une instance de \NS_A\ClassA, ni d’aucune de ses classes filles !
Finalement, la fonctionnalité de type-hinting continue à fonctionner comme en PHP < 5.3, même lorsque nous l’utilisons avec des classes organisées au sein d’espaces de noms — et, encore une fois, c’est heureux !
Pour plus d’informations sur le Type Hinting en PHP 5, n’hésitez pas à consulter la page de manuel qui lui est consacrée.
Import d’un espace de noms : use
Utiliser partout dans vos sources un nom d’espace de noms potentiellement long n’est pas forcément une bonne solution pour un développeur : plus il y a de code à saisir, plus il y a de code à maintenir, et plus il y a de risques d’erreurs…
Et cela ne résout aucunement le problème des noms de classes habituellement trop longs en PHP.
Pour répondre à ce problème, et à la demande qui va avec, PHP 5.3 introduit un nouveau mot-clef, ou, plutôt, un nouveau couple de mots-clefs : « use ... as ... »[2].
La façon la plus simple de définir ce mot-clef est certainement de dire qu’il permet de définir un « alias » pour un espace de noms.
use : création d’un alias d’espace de noms
Pour commencer avec un premier exemple, créons le fichier lib.php, contenant le code source reproduit ci-dessous :
<?php namespace NAMESPACE_AU_NOM_TRES_LONG; class ClassA { public function method() { var_dump(__NAMESPACE__ . ' \ ' . __CLASS__ . ' \ ' . __FUNCTION__); } }
Difficile à exploiter, comme nom de namespace, n’est-ce pas ?
Heureusement, à l’aide du mot-clef use, nous allons pouvoir, avant de travailler avec cet espace de nom, lui définir un alias :
<?php require_once(__DIR__ . '/lib.php'); use NAMESPACE_AU_NOM_TRES_LONG as A; // Défini "A" comme alias de "NAMESPACE_AU_NOM_TRES_LONG" $a = new A\ClassA(); // Utilisation de A, et non de "NAMESPACE_AU_NOM_TRES_LONG" $a->method(); $a = new NAMESPACE_AU_NOM_TRES_LONG\ClassA(); // Utilisation de NAMESPACE_AU_NOM_TRES_LONG : OK ! $a->method();
Et nous obtiendrons bien l’affichage attendu, à savoir :
string 'NAMESPACE_AU_NOM_TRES_LONG \ NAMESPACE_AU_NOM_TRES_LONG\ClassA \ method' (length=71) string 'NAMESPACE_AU_NOM_TRES_LONG \ NAMESPACE_AU_NOM_TRES_LONG\ClassA \ method' (length=71)
A noter, donc :
usepermet de définir un alias pour un nom d’espace de noms,- Utiliser
usene rend pas inaccessible le nom d’origine de l’espace de nom aliasé, - Et, en interne, c’est toujours le nom complètement qualifié de l’espace de noms qui est utilisé — pas le nom défini par l’alias, donc !
use : alias d’un espace de noms composé
Passons à un second exemple, où notre espace de noms n’aura plus un nom très long, mais un nom "composé" : définissons un fichier lib.php :
<?php namespace NS\NOM\COMPOSE; class ClassA { public function method() { var_dump(__METHOD__); } }
Et un second fichier sub-lib.php :
<?php namespace NS\NOM\COMPOSE\SOUS\LIBRAIRIE; class ClassB { public function method() { var_dump(__METHOD__); } }
Ces deux espaces de noms sont ensuite utilisés depuis le fichier principal de notre application :
<?php require_once(__DIR__ . '/lib.php'); require_once(__DIR__ . '/sub-lib.php'); use NS\NOM\COMPOSE as A; // Défini "A" comme alias de "NS\NOM\COMPOSE" $a = new A\ClassA(); // Utilisation de A, et non de "NS\NOM\COMPOSE" $a->method(); $b = new A\SOUS\LIBRAIRIE\ClassB(); // Même chose $b->method();
Et nous obtenons en sortie l’affichage suivant :
string 'NS\NOM\COMPOSE\ClassA::method' (length=29) string 'NS\NOM\COMPOSE\SOUS\LIBRAIRIE\ClassB::method' (length=44)
Pas de différence par rapport à ce que nous faisions plus haut : nous continuons à créer des alias sur des noms d’espaces de noms, même s’ils contiennent des \ .
Par contre, notez qu’il n’est pas possible d’utiliser la syntaxe suivante :
<?php require_once(__DIR__ . '/lib.php'); require_once(__DIR__ . '/sub-lib.php'); use NS\NOM\COMPOSE as A; // Défini "A" comme alias de "NS\NOM\COMPOSE" use A\SOUS as B; $b = new B\LIBRAIRIE\ClassB(); // Fatal error: Class 'A\SOUS\LIBRAIRIE\ClassB' not found $b->method();
Nous obtiendrons l’erreur suivante :
Il n’est pas possible de définir un alias lui-même basé sur un alias : PHP n’effectue l’interprétation d’aliases qu’une seule fois !
Définition d’un alias de classe
Jusqu’à présent, nous avons utilisé use pour définir des alias d’espaces de noms…
… Mais on peut aussi utiliser ce mot-clef pour définir directement un alias de classe !
Prenons par exemple le fichier lib.php suivant :
<?php namespace NS\NOM\COMPOSE; class ClassA { public function method() { var_dump(__NAMESPACE__ . ' \ ' . __CLASS__ . ' \ ' . __FUNCTION__); } }
Il est alors possible de définir un alias pour la classe ClassA :
<?php require_once(__DIR__ . '/lib.php'); use NS\NOM\COMPOSE\ClassA as MyClass; // Défini "MyClass" comme alias de "NS\NOM\COMPOSE\ClassA" $a = new MyClass(); // Utilisation de MyClass, et non de "NS\NOM\COMPOSE\ClassA" $a->method();
Et le résultat obtenu lors de l’exécution de ce script sera le suivant :
string 'NS\NOM\COMPOSE \ NS\NOM\COMPOSE\ClassA \ method' (length=47)
Je vous laisse imaginer l’utilité, lorsque vous travaillez très souvent avec une classe donnée, ou lorsque vous devez rendre namespaçable du code qui ne l’était pas…
… Et je tremble déjà à l’idée des difficultés que cela risque d’engendrer en termes de facilité de compréhension et de (re-)prise en main de code existant — du moins si ce n’est pas utilisé avec sagesse… 
Une des difficultés sera de trouver une norme valide pour ces noms d’aliases ; cela viendra avec le temps et la pratique, je suppose…
use sans as
Avant de terminer cette partie consacrée à use et à la définition d’alias, un petit dernier exemple ^^
Lorsque l’on travaille avec des espaces de noms en plusieurs parties, comme ceci :
<?php namespace NS\NOM\COMPOSE; class ClassA { public function method() { var_dump(__NAMESPACE__ . ' \ ' . __CLASS__ . ' \ ' . __FUNCTION__); } }
Il est possible d’utiliser le mot-clef use sans utiliser as : le nom de l’alias sera alors automatiquement construit à partir de la dernière composante du nom de l’espace de noms :
<?php require_once(__DIR__ . '/lib.php'); use NS\NOM\COMPOSE; // Défini "COMPOSE" comme alias de "NS\NOM\COMPOSE" $a = new COMPOSE\ClassA(); // Utilisation de COMPOSE, et non de "NS\NOM\COMPOSE" $a->method();
Qui donnerait la sortie suivante :
string 'NS\NOM\COMPOSE \ NS\NOM\COMPOSE\ClassA \ method' (length=47)
Un alias nommé COMPOSE a automatiquement été créé, à partir de la dernière composante de NS\NOM\COMPOSE.
Par contre, si notre espace de noms ne portait pas un nom « composé », cela revient à aliaser sa dernière — et unique — composante avec elle-même… Ce qui ne fait absolument rien…
Par exemple, si nous définissons un espace de noms comme suit :
<?php namespace NAMESPACE_AU_NOM_TRES_LONG; class ClassA { public function method() { var_dump(__NAMESPACE__ . ' \ ' . __CLASS__ . ' \ ' . __FUNCTION__); } }
Et que nous tentons de l’aliaser sans utiliser as :
<?php require_once(__DIR__ . '/lib.php'); use NAMESPACE_AU_NOM_TRES_LONG; // $a = new NAMESPACE_AU_NOM_TRES_LONG\ClassA(); // Utilisation de NAMESPACE_AU_NOM_TRES_LONG => Warning Warning: The use statement with non-compound name 'NAMESPACE_AU_NOM_TRES_LONG' has no effect $a->method();
Nous obtenons un avertissement :
Le code s’exécutera tout de même… Mais notre instruction use sera… useless[3].
Quelques mots sur l’ordre de résolution
Je n’ai pas encore eu suffisament l’occasion de m’amuser avec l’implémentation quasi-finale des espaces de noms en PHP 5.3, qui n’est arrivée qu’il y a quelques jours, mais, en termes d’ordre de résolution, voici ce que j’ai pu noter :
L’ordre de résolution est le suivant :
- Chargement depuis l’espace de noms courant,
- Pour les classes : appel à la (ou aux) fonction(s) d’autoload, s’il en existe,
- Pour les fonctions et les constantes : chargement depuis l’espace de noms global,
- Si non trouvé : échec.
Quelques points me semblent tout particulièrement intéressants :
- L’implémentation cherche en premier lieu dans l’espace de noms courant. Cela garanti que lorsque vous placez du code dans un espace de noms, ce sont les fonctions/constantes/classes définies dans cet espace de noms qui seront appelées par le code en question.
- Pour les classes, il n’y a pas de chargement par défaut depuis l’espace de noms global : si vous voulez utiliser une classe globale depuis du code situé dans un espace de noms, vous devez précéder le nom de classe par
\.
Pour ce qui est des raisons pour lesquelles il n’y a pas de fallback vers l’espace de noms global pour les classes :
- PHP fourni beaucoup moins de classes globales que de fonctions globales, et il est probable qu’il en aille de même pour les applications tirant parti de la fonctionnalité d’espaces de noms,
- Implémenter un fallback sur l’espace de noms global pour les classes entrainerait des problèmes avec le type-hinting, en forçant, dans certains cas, des auto-chargements.
Et pour ce qui est des raisons pour lesquelles un fallback vers l’espace de noms global a été implémenté pour les fonctions :
- Il y aura théoriquement souvent utilisation de classes, et non de fonctions, au sein d’espaces de noms — ceux-ci étant une fonctionnalité « objet »
- Mais il est certain que les développeurs voudront pouvoir accéder facilement à l’immense quantité de fonctions que PHP défini !
Et pour la source, la plus récente que j’aie pu trouver, qui explique clairement les choses, est un post de Lukas sur internals@ le 5 novembre — en espérant que l’implémentation finale ne s’éloigne pas trop de ces propositions…
Si vous souhaitez expérimenter un peu par vous-même, n’hésitez pas à jeter un coup d’oeils aux exemples se trouvant dans les répertoires namespace/resolution-1 et namespace/resolution-2 de l’archive jointe à cet article 
(Ils sont probablement un peu long pour être reproduits ici
— mais montrent quelques cas qui pourraient nous pièger !)
Notez que la documentation à propos des namespaces sur php.net n’est aujourd’hui absolument pas à jour… Mais considérant l’importance des espaces de noms pour PHP 5.3, il ne fait aucun doute qu’elle sera actualisée d’ici quelques temps — jetez un oeil de temps en temps, vous apprendrez sans aucun doute des choses utiles !
Quoi qu’il en soit, il y a toujours du travail en cours à ce niveau… Et la version alpha3 devrait sortir dans les prochains jours, pour nous permettre de tester tout ça !
Faites attention !
Bien évidemment, rien ni nul n’est parfait… L’implémentation des espaces de noms non plus, donc ^^
Le choix de l’antislash comme séparateur de namespaces a probablement suffisament remué les esprits sur la blogosphère pour que je n’en rajoute pas une couche, mais il peut être bon de montrer quelques exemples de situations dans lesquelles il convient de faire attention à ce que vous écrivez, afin de limiter les risques d’erreurs et de bugs…
Premier exemple
Pour notre premier exemple, créons un fichier, lib.php, contenant la définition suivante :
<?php namespace MyNamespace; function test() { var_dump(__NAMESPACE__ . ' \ ' . 'test()'); }
Une fois ce fichier créé, nous pouvons appeler la fonction qu’il contient en dynamique, de la manière suivante :
<?php require_once(__DIR__ . '/lib.php'); $nom = 'MyNameSpace\test'; var_dump($nom); $nom();
Cette syntaxe fonctionnera aussi :
<?php require_once(__DIR__ . '/lib.php'); $nom = "MyNameSpace\\test"; var_dump($nom); $nom();
Par contre, cette troisième possibilité ne fonctionnera pas :
<?php require_once(__DIR__ . '/lib.php'); $nom = "MyNameSpace\test"; var_dump($nom); $nom();
Et nous obtiendrons l’erreur suivante :
L’antislash est le caractère d’échappement dans les chaînes de caractères, et, dans une chaîne délimitée par des doubles-quotes, "\n" est interprété comme une tabulation… Ce qui entraine un nom de fonction n’existant pas !
Second exemple
Pour la route, voici un second exemple, en utilisant un espace de noms dont le nom est composé, et une classe :
<?php namespace my\ns\de\test; class MyGreatClass { public function glop() { var_dump(__NAMESPACE__ . ' \ ' . __CLASS__ . ' \ ' . __FUNCTION__); } }
Ici encore, la première façon d’appeler cette classe en dynamique est correcte :
<?php require_once(__DIR__ . '/lib.php'); $nom = 'my\ns\de\test\MyGreatClass'; var_dump($nom); $a = new $nom(); $a->glop();
La seconde, en échappant les antislash, l’est aussi :
<?php require_once(__DIR__ . '/lib.php'); $nom = "my\\ns\\de\\test\\MyGreatClass"; var_dump($nom); $a = new $nom(); $a->glop();
Par contre, encore une fois, la troisième, utilisant une chaîne de caractères délimitée par des doubles-quotes et des antislash non échappés n’est pas correcte :
<?php require_once(__DIR__ . '/lib.php'); $nom = "my\ns\de\test\MyGreatClass"; var_dump($nom); $a = new $nom(); $a->glop();
Elle entraine l’erreur suivante :
Certains d’entre vous se diront peut-être que le choix de \ comme séparateur de namespaces n’était pas bon… Sans entrer dans le Troll, ce choix présente à la fois des avantages et des inconvénients… Quoi qu’il en soit, c’est l’opérateur qui a été choisi, c’est ainsi et pas autrement : à nous de nous y faire — et nous nous y ferons, je n’en doute pas !
Accessoirement, pour les curieux, vous pouvez jeter un oeil à la Request for Comments: Namespace Separators, qui donne quelques arguments sur le sujet.
Quelques points qui n’existent pas
Avant de terminer, j’aimerais mettre en évidence une paire de points auxquels on pourrait penser, ou que l’on pourrait trouver potentiellement intéressant, et qui n’existent pas en PHP 5.3 — du moins pas en date du 22 novembre 2008 (le snapshot que j’ai utilisé pour créer et tester les exemples de cet article datant de ce jour là).
Le séparateur de namespaces est l’antislash ; pas le double-deux-points
Je me répéte par rapport à ce que je disais en tout début d’articles, mais ça ne peut pas faire de mal : le séparateur d’espaces de noms n’est pas le T_PAAMAYIM_NEKUDOTAYIM (le symbole « :: »), mais l’antislash : « \ ».
Oui, il a été prévu pendant longtemps que le séparateur utilisé soit « :: », ce qui explique qu’il soit utilisé dans grand nombre d’articles, blog-posts, et documentations qui n’ont pas été mis à jour ; mais il a finalement été décidé de retenir « \ » à la place, entre la sortie de la version alpha2 et de la version alpha3.
Premier exemple
Si vous essayez d’exécuter l’exemple composé des deux portions de code suivantes : lib.php :
<?php namespace LIB; function a() { var_dump('LIB::a'); }
Et, comme fichier principal de l’application :
<?php require_once(__DIR__ . '/lib.php'); LIB::a();
Vous obtiendrez une erreur :
En effet, puisque l’opérateur « :: » est utilisé pour séparer les noms de classes de leurs méthodes, PHP croit que vous souhaitez appeler la méthode statique a de la classe LIB, qui n’existe pas.
Second exemple
Et pour un second exemple, considérant le fichier lib.php suivant :
<?php namespace Mon::Espace::De::Noms; function a() { var_dump('Mon::Espace::De::Noms\a'); }
Qui sera appelé par le fichier principal de notre application :
<?php require_once(__DIR__ . '/lib.php'); Mon::Espace::De::Noms::a();
Ce qui se traduira par l’erreur suivante :
Ici, sans même aller jusqu’à la tentative d’appel de la fonction, c’est déjà la déclaration de l’espace de noms qui entrainera l’erreur !
Pas d’espace de noms entre accolades
L’idée de placer le contenu défini au sein d’un espace de noms entre accolades a souvent été évoqué, parfois comme solution permettant dans le futur l’implémentation de namespaces imbriquées…
… Mais cette idée n’a pas été retenue pour PHP 5.3 : si vous essayez d’exécuter le code d’un fichier construit de cette manière :
<?php namespace LIB { function a() { var_dump('LIB\a'); } function b() { var_dump('LIB\b'); } }
Eventuellement appelé par celui-ci :
<?php require_once(__DIR__ . '/lib.php'); LIB\a(); LIB\b();
Vous obtiendrez (encore une fois ^^ ) une erreur :
Ce code ne « compile » même pas !
Pas de use dynamique
Une troisième (pour terminer ? ) ?
Supposons que l’on ait déclaré un espace de noms, par exemple comme ceci :
<?php namespace NS\NOM\COMPOSE; class ClassA { public function method() { var_dump(__NAMESPACE__ . ' \ ' . __CLASS__ . ' \ ' . __FUNCTION__); } }
On pourrait éventuellement être tenté — je ne sais pas trop pourquoi, mais j’avais envie d’essayer
— de vouloir utiliser un namespace dont le nom ne soit pas connu à l’écriture du script[4], un peu comme ceci :
<?php require_once(__DIR__ . '/lib.php'); $namespace = 'NS\NOM\COMPOSE'; use $namespace as A; // Parse error: syntax error, unexpected T_VARIABLE, expecting T_STRING or T_NS_SEPARATOR
Et bien, oubliez : ce n’est pas possible, et se traduira par une erreur :
Tant pis ^^
Conlusion
Grand sujet de débats au sein de la communauté ces derniers mois, l’implémentation des namespaces en PHP 5.3 peut ne pas paraître parfaite… Mais elle est ainsi faite, et, quoi qu’il en soit, elle semble répondre aux objectifs qui lui avaient été fixés.
Maintenant, une seule chose nous manque : la pratique !
Ce n’est que dans quelques mois, lorsque nous aurons réellement pris l’habitude de travailler avec les espaces de noms, que nous serons à même de mettre en place des séries de bonnes pratiques, ainsi que d’identifier plus clairement certains dangers qui risquent de nuire à la maintenabilité du code de nos applications…
En attendant, l’utilisation des espaces de noms est-elle prévue ? Oui, au moins pour quelques projets !
En particulier, je pense aux Frameworks suivants :
- Zend Framework utilisera peut-être des espaces de noms en version 2.0 — sortie pour quelque chose comme mi-2009 ?
- Même chose pour le Framework ORM Doctrine, aussi pour sa version 2.0
- Et qu’en sera-t-il pour vos projet ? Peut-être pour vous simplifier les choses sur l’inclusion de certains composants externes ?
Pour ceux d’entre-vous qui seraient curieux, quelques exemples correspondant à l’ancienne implémentation des espaces de noms, jusqu’à la version alpha2 inclue, sont disponibles dans le répertoire "NOT-namespace-deuxdeux-points" de l’archive jointe — les exemples correspondant à l’implémentation finalement retenue étant disponibles dans le répertoire "namespace".
Notes
[1] Je me placerai ici, dans cet article, en relatif par rapport à la racine montrée sur la capture d’écran reproduite plus haut ; mais, en réalité, les chemins sont absolus ; ils sont juste trop longs pour être reproduits en intégralité ; je n’ai donc conservé que la partie significative
[2] Oui, je sais, j’ai déjà présenté use comme « nouveau » dans mon article sur les fonctions anonymes et les closures ; mais considérant que ce n’est pas le même usage…
[3] Et hop, encore un jeu de mots à deux balles… faut j’arrête 
[4] Je vous avais prévenu : je n’ai pas réfléchi à la question du pourquoi je pourrais vouloir faire ça — j’avais juste envie ^^
Pour être averti lors de la publication de nouvelles entrées, n'hésitez pas à vous abonner au flux RSS ou ATOM des articles de mon blog !









Commentaires
Bon article! On peut noter que les dernières snapshots supportent les namespaces entre accolade. C'est une bonne nouvelle.
L'implémentation actuelle est plutôt bonne je trouve. Cependant, le détail qui risque de perturber, du moins au début, est le recours à l'autoload dès qu'une classe non complètement qualifiée n'est pas présente dans le namespace courant... à moins que l'on utilise une alias.
--- Fichier 1:
namespace a\b {
class B {
function __construct() { echo __METHOD__; }
}
}
namespace c\d {
$b = new a\b\B(); // Fatal error: Class 'c\d\a\b\B' not found
$b = new \a\b\B(); // OK
}
--- Fichier 2:
namespace a\b {
class B {
function __construct() { echo __METHOD__; }
}
}
namespace c\d {
use a\b as alias; // partiellement qualifié
$b = new alias\B(); // OK
}
Bonsoir,
Il y a beaucoup de choses qui vont "perturber" en PHP 5.3, et les espaces de noms sont sans aucun doute un des points qui n'a pas fini de faire couler de l'encre ^^
Mais on s'y fera, j'ai confiance : quelques mois, et au fur et à mesure que nous commencerons à travailler en PHP 5.3 sur de vrais projets (par opposition à des simples scripts d'exemples comme j'ai pu en présenter ici), les bonnes pratiques arriveront, de même que l'expérience
Pour le support des espaces de noms entre accolades > rrrhhhaaa j'ai publié cet article quelques jours trop tôt
Il faut que je teste ça ^^
Un grand merci pour cet article clair et complet sur les espaces de noms.
Je vais enfin pouvoir renommer certaines de mes classes tel qu'il convient de le faire. En effet, certains noms déjà utilisés dans la SPL, tels que ArrayIterator, Iterator, me forçaient à coder des trucs du genre DefaultArrayIteraror, ou CustomIterator.
L'introduction des espaces de noms en PHP devrait changer tout ça... ^^
Merci beaucoup pour ces articles didactiques, et bravo pour votre sens du partage
Ma méthode autoload fonctionne bien avec les deux séparateurs "_" et "\".
@@
static public function autoload($class_name)
{
$class_name=str_replace('\\','/',$class_name);
$path_separator= (strpos($class_name,'/')>0) ?'/':'_';
$path=explode($path_separator,$class_name);
array_push($path, /*'class.'.*/ array_pop($path).'.php');
$path=SWFLIB.implode(DIRECTORY_SEPARATOR,$path);
if ((@include_once $path)==false)
{
echo ' '.$class_name.' :: '.$path.'<br />';
}
else { echo '<br />ok :: <b>'.$class_name.'</b><br />'; }
}
@@
mais TRÈS GROS problème avec des classe utilisant la spl :

class Montest implements Countable{ }me renvoie toujours un plantage , l'erreur : Countable.php non trouvé de même pour Iterator
l'autoload php 5.3 recherche les interfaces de la spl ?