Les nouveautés de PHP 5.3

Et comment migrer de PHP 5.2 vers PHP 5.3


précédentsommairesuivant

III. Espaces de noms (namespaces)

Cette section est une traduction de : http://www.ibm.com/developerworks/opensource/library/os-php-5.3new3/

III-A. Introduction

Dans cette troisième partie, nous parlerons des espaces de noms (namespaces), qui sont la fonctionnalité la plus attendue et la plus débattue de cette version de PHP. Le concept d'espace de noms donne les moyens d'aider à la prévention de problèmes avec plusieurs fonctions, classes et constantes ayant le même nom et définies plusieurs fois.

Les espaces de noms existent dans de nombreux langages incluant le C++ et le langage de programmation Java™. Ils sont apparus pour aider à l'organisation de grandes bases de code où, parfois, il y a des inquiétudes au sujet des noms de fonctions et de classes dans une application. L'utilisation d'espaces de noms peut aider à l'identification de l'objectif ou de l'utilité du code, ou encore préciser son origine. Un exemple de cela est l'espace de noms System en C# qui contient toutes les fonctions et les classes proposées par le framework .NET. Dans d'autres langages sans espaces de noms formels (comme PHP 5.2 et antérieurs), les gens émulent fréquemment les espaces de noms en essayant d'utiliser des conventions de nommage précises dans les noms de fonctions et de classes. Zend Framework le fait, c'est-à-dire que chaque nom de classe commence par Zend, et chaque espace de noms enfant est séparé par un signe de soulignement. Par exemple, la définition de la classe Zend_Db_Table est une classe qui fait partie de Zend Framework et qui a des fonctionnalités de bases de données. Un problème avec cette approche est que le code résultant peut devenir très verbeux, particulièrement si la classe ou la fonction est plusieurs à niveaux de profondeur (Zend_Cache_Backend_Apc est un exemple de cela dans Zend Framework). Un autre problème est que tout le code doit se conformer à ce style, ce qui peut être difficile si vous intégrez du code produit par d'autres personnes dans une application qui n'est pas conforme à cette convention de codage.

L'histoire des espaces de noms en PHP est une route très sinueuse. À l'origine, ils devaient faire part de PHP 5 mais ils furent enlevés des étapes de développement car aucune implémentation correcte n'avait été trouvée. Ils revinrent ensuite à la vie dans PHP 6, puis déplacés dans PHP 5.3 après que la décision en 2007 de déplacer toutes les améliorations excepté Unicode, vers une autre version de PHP 5.x. Tandis qu'une grande partie du comportement des espaces de noms est resté le même depuis la conception initiale, le débat du séparateur à utiliser a été très actif, avec des points de vue très différents de la part de tous les membres de la communauté concernant la solution idéale. Une décision finale d'utiliser la barre oblique arrière fut prise en octobre 2008, résolvant tous les problèmes posés par les autres opérateurs en termes de conception du langage et d'utilisabilité.

III-B. Les espaces de noms en PHP

PHP emprunte une grande partie de la syntaxe et de la conception des espaces de noms d'autres langages, et plus particulièrement de C++. Cependant, PHP réagit de manière unique à certaines situations, ce qui peut être déroutant pour les utilisateurs qui s'attendent à les voir fonctionner exactement de la même manière que dans d'autres langages. Dans cette section, nous voyons comment les espaces de noms fonctionnent en PHP.

III-B-1. Définir un espace de noms

Définir un espace de noms est une démarche triviale. Ajoutez simplement la ligne suivante du Listing 1 en tant que première commande d'un script :

Listing 1. Définir un espace de noms
Sélectionnez
<?php 
namespace Foo; 
class Exemple {} 
?> 

La déclaration ci-dessus de namespace doit être la première commande ou sortie d'un fichier. Avoir quoi que ce soit d'autre avant cela résulte en une erreur fatale. Listing 2 montre quelques exemples de cela :

Listing 2. Mauvaises manières de définir un espace de noms
Sélectionnez
/* Fichier1.php */ 
<?php 
echo "hello world!"; 
namespace Mauvais; 
class Incorrecte {}
?> 

/* Fichier2.php */ 
 <?php 
namespace Mauvais; 
class Incorrecte {} 
?>

Dans la première partie de Listing 2, nous essayons d'afficher dans la console avant la définition de l'espace de noms, ce qui a pour résultat une erreur fatale. Dans la seconde partie de l'exemple, nous avons ajouté un espace supplémentaire avant la balise d'ouverture <?php, ce qui a pour conséquence une autre erreur fatale. Il est important de faire attention à cela lorsque nous écrivons notre code, dans la mesure où c'est une erreur classique en travaillant avec les espaces de noms en PHP.

Toutefois, les deux exemples ci-dessus peuvent être réécrits en séparant la définition de l'espace de noms et le code prévu pour en faire partie vers un script indépendant qui puisse être inclus dans les scripts d'origine. Le Listing 3 montre un exemple :

Listing 3. Résoudre les mauvaises manières de définir un espace de noms
Sélectionnez
/* correct.php */ 
<?php 
namespace Correct; 
class EstCorrecte {} 
?> 

/* Fichier1.php */ 
<?php 
echo "hello world!"; 
include './correct.php'; 
?>

/* Fichier2.php */ 
 <?php 
include './correct.php'; 
?>

NDLT : Malgré les apparences, l'erreur fatale dont il est question ici n'a rien à voir avec l'erreur classique « headers already sent » causée par la tentative d'envoi d'en têtes HTTP après le début de texte au navigateur Web. Les exemples ci-dessus démontrent que, dans le cas des espaces de noms, il s'agit d'une restriction imposée par la syntaxe du langage PHP tandis que, dans l'autre situation, il s'agit du fonctionnement du serveur Web (il est donc possible de contourner ce problème). Alors qu'il est techniquement possible d'utiliser echo dans un script PHP avant d'appeler la fonction header() dans le même script (à condition d'avoir initié la gestion différée du cache de sortie), une déclaration d'espace de noms en PHP doit se trouver tout en haut du fichier.

Maintenant que nous avons vu comment définir un espace de noms pour le nom dans un fichier, voyons comment utiliser ce code organisé dans une application.

III-B-2. Utiliser du code avec espaces de noms

Une fois que nous avons défini un espace de noms et qu'il contient du code, nous pouvons l'appeler très simplement depuis une application. Il y a différentes options pour appeler des classes, des fonctions et des constantes organisées en espaces de noms. Une manière est de référencer l'espace de noms de manière explicite avec un préfixe au moment de l'appel. Une autre option est de définir un alias pour l'espace de noms et d'utiliser cela comme préfixe devant les appels, ce qui est conçu pour raccourcir les préfixes d'espaces de noms. Enfin, nous pouvons simplement utiliser l'espace de noms dans notre code, ce qui en fait l'espace de noms par défaut et, par défaut, fait que tous les appels utilisent cet espace de noms. Le Listing 4 illustre les différences entre les appels.

Listing 4. Appeler une fonction dans un espace de noms
Sélectionnez
/* Foo.php */ 
<?php 
namespace Foo; 
function bar() 
{ 
    echo "appel de bar...";
} 
?> 

/* Fichier1.php */ 
<?php 
include './Foo.php'; 
Foo\bar(); // affiche "appel de bar..."; 
?> 

/* Fichier2.php */ 
<?php 
include './Foo.php';
use Foo as ns; 
ns\bar(); // affiche "appel de bar..."; 
?> 

/* Fichier3.php */ 
<?php 
include './Foo.php'; 
use Foo; 
bar(); // erreur; 
?>

Le Listing 4 montre les différentes manières d'appeler une fonction bar() dans un espace de noms Foo. Dans Fichier1.php, nous voyons comment faire un appel explicite en ajoutant le nom complet de l'espace de noms avant l'appel. Dans Fichier2.php, nous utilisons un alias de l'espace de noms, ainsi nous remplaçons le nom de l'espace de noms par son alias au moment de l'appel. Enfin, Fichier3.php utilise simplement l'espace de noms, ce qui nous permet d'effectuer l'appel à bar() sans aucun préfixe.

Nous pouvons également définir plus d'un espace de noms dans un fichier en ajoutant d'autres appels à namespace dans le fichier. Le Listing 5 illustre cela :

Listing 5. Plusieurs espaces de noms dans un fichier
Sélectionnez
<?php 
namespace Foo; 
class Test {} 

namespace Bar; 
class Test {} 

$a = new \Foo\Test; 
$b = new \Bar\Test; 

Affiche : 
object(Foo\Test)#1 (0) {  
}  
object(Bar\Test)#2 (0) {  
} 

Maintenant que nous avons les connaissances de bases sur la manière de faire un appel dans un espace de noms, voyons davantage de situations complexes d'espaces de noms et comment ces appels fonctionnent.

III-B-3. Résolution d'espaces de noms

L'un des défis lors de la prise en main des espaces de noms est d'apprendre le fonctionnement de la portée. Tandis que des cas simples comme dans le Listing 4 sont faciles à comprendre, le problème survient quand nous commençons à imbriquer les espaces de noms les uns dans les autres, ou encore quand nous sommes dans un espace de noms et que nous essayons d'appeler quelque chose de l'espace global. PHP 5.3 a des règles pour résoudre automatiquement ces situations de manière judicieuse.

Créons quelques fichiers à inclure, chacun comportant la fonction bonjour() :

Listing 6. Fonction bonjour() définie dans plusieurs espaces de noms
Sélectionnez
/* global.php */ 
<?php 
function bonjour() 
{ 
    echo "bonjour depuis l'espace global !";
} 
?> 

/* Foo.php */ 
<?php 
namespace Foo; 
function bonjour() 
{ 
    echo "bonjour depuis l'espace Foo !";
} 
?> 

/* Foo_Bar.php */ 
<?php 
namespace Foo\Bar; 
function bonjour() 
{ 
    echo "bonjour depuis l'espace Foo\Bar !";
} 
?>

Le Listing 6 définit trois fois la fonction bonjour(), chacune dans un espace de noms différent : dans la portée globale, dans l'espace de noms Foo et dans l'espace de noms Foo\Bar. L'espace de noms appelant la fonction bonjour() détermine quelle fonction bonjour() est appelée. Un exemple de comment ces appels se matérialisent est montré ci-dessous. Ici, nous utilisons l'espace de noms Foo pour appeler la fonction bonjour() dans les autres espaces de noms.

Listing 7. Appel des fonctions bonjour() depuis l'espace de noms Foo
Sélectionnez
<?php 
include './global.php'; 
include './Foo.php';
include './Foo_Bar.php';

use Foo\Bar; // identique à "use Foo\Bar as Bar"

bonjour();       // affiche 'bonjour depuis l'espace global !'
\bonjour();       // affiche 'bonjour depuis l'espace global !'
Foo\bonjour();         // affiche 'bonjour depuis l'espace Foo !'
\Foo\bonjour();         // affiche 'bonjour depuis l'espace Foo !'
Bar\bonjour();   // affiche 'bonjour depuis l'espace Foo\Bar !'
\Bar\bonjour();   // erreur
?>

Nous voyons qu'il est possible de réduire le préfixe de l'espace de noms lors d'un appel à un espace de noms enfant depuis l'espace courant (l'appel à la fonction Foo\Bar\bonjour() peut être simplifié en Bar\bonjour()). De plus, nous voyons comment préciser que nous voulons utiliser la fonction de l'espace global en ajoutant l'opérateur de résolution d'espaces de noms devant l'appel.

Maintenant que nous avons les principes du fonctionnement des espaces de noms, voyons comment les utiliser dans notre code.

III-C. Cas d'utilisation pour les espaces de noms en PHP

L'objectif principal des espaces de noms est de nous aider à mieux organiser notre code en éliminant la quantité de définitions qui vivent dans l'espace global. Dans cette section, nous verrons quelques exemples de situations dans lesquelles la présence d'espaces de noms peut aider avec peu d'efforts.

III-C-1. Code produit par des tiers

De nombreuses applications PHP utilisent du code de plusieurs sources. C'est soit du code à la conception homogène comme ce qui existe dans le répertoire PEAR, soit du code de divers frameworks comme CakePHP ou Zend Framework, soit du code trouvé à divers endroits sur Internet. L'un des plus gros problèmes en intégrant ce code est qu'il peut ne pas être capable de cohabiter avec le reste du code ; des noms de fonctions ou de classes peuvent entrer en collision avec ce que nous avons déjà dans notre application.

Un exemple de cela est le paquetage Date dans PEAR. Il utilise le nom de classe Date, qui est un nom très générique et qui pourrait très bien exister dans d'autres endroits de notre code. Ainsi, un bon moyen de détourner cela est d'ajouter une simple commande d'espace de noms au début du fichier Date.php dans le paquetage. Nous pouvons maintenant être précis lorsque nous souhaitons utiliser la classe PEAR ou la nôtre.

Listing 8. Utiliser la classe Date de PEAR définie dans un espace de noms
Sélectionnez
<?php 

require_once('PEAR/Date.php'); 

use PEAR\Date;    // le nom de l'espace spécifié dans PEAR/Date.php

// puisque l'espace courant est PEAR\Date, 
//nous n'avons pas besoin d'ajouter le préfixe de l'espace de noms
$maintenant = new Date(); 
echo $maintenant->getDate();  // affiche la date au format ISO

// cet exemple montre l'espace de noms complet défini comme partie du nom de classe
$maintenant = new PEAR\Date\Date(); 
echo $maintenant->getDate();  // affiche la date au format ISO
?>

Nous avons défini la classe Date PEAR dans un espace de noms PEAR\Date dans le script PEAR/Date.php, ainsi, tout ce que nous avons à faire est d'inclure ce script dans notre fichier et d'utiliser le nouvel espace de noms, ou bien d'ajouter le nom de l'espace devant les noms des classes et des fonctions. Avec cela, nous pouvons inclure en toute sérénité du code tiers dans notre application.

Les collisions ne sont pas un problème uniquement avec le code produit par des tierces personnes. Elles peuvent également se produire avec des bases de code ayant des parts qui n'ont jamais été conçues pour s'approcher l'une de l'autre. Dans la section suivante, nous voyons comment les espaces de noms permettent de simplifier cette situation.

III-C-2. Éviter les collisions de fonctions utilitaires

La très grande majorité des applications PHP ont des fonctions utilitaires. Elles ne font pas vraiment partie d'un objet de l'application et elles n'ont pas vraiment de place dans un endroit unique de l'application ; elles jouent pourtant un rôle dans l'application dans son ensemble. Toutefois, elles peuvent nous causer des problèmes de maintenance à mesure que notre application grandit.

Un endroit où cela peut être un problème est avec les tests unitaires, où nous écrivons du code qui vérifie le code qui constitue l'application. La plupart des suites de tests unitaires sont prévues pour exécuter tous les tests dans la suite complète. Par exemple, nous pourrions avoir deux fichiers utilitaires qui ne seraient jamais inclus ensemble, mais ils le sont dans la suite de tests puisque nous sommes en train de vérifier toute l'application en une fois. Tandis que concevoir une application de cette manière n'est jamais une bonne idée pour la maintenance à long terme, la situation existe fréquemment dans les larges bases de code construites avec le temps.

Le Listing 9 montre comment éviter cela. Nous avons deux fichiers, utils_gauche.php et utils_droite.php, qui sont des ensembles de fonctions utilitaires pour utilisateurs gauchers et droitiers. Nous définissons chaque fichier dans son propre espace de noms.

Listing 9. utils_gauche.php et utils_droite.php
Sélectionnez
/* utils_gauche.php */
<?php 
namespace Utils\Gauche;

function quelleMain() 
{ 
    echo "J'utilise ma main gauche !";
} 
?> 

/* utils_droite.php */
<?php 
namespace Utils\Droite; 

function quelleMain() 
{ 
    echo "J'utilise ma main droite !";
} 
?>

Nous définissons une fonction quelleMain() qui affiche quelle main nous utilisons. Dans le Listing 10, nous voyons comme il est facile d'inclure sans problème les deux fichiers et de naviguer entre les espaces de noms que nous souhaitons appeler.

Listing 10. Exemple d'utilisation conjointe d'utils_gauche.php et utils_droite.php
Sélectionnez
<?php 
namespace {
    include('./utils_gauche.php');
    include('./utils_droite.php');

    Utils\Gauche\quelleMain(); // affiche "J'utilise ma main gauche !"
    Utils\Droite\quelleMain(); // affiche "J'utilise ma main droite !"
}

namespace {
    use Utils\Gauche as u;
    u\quelleMain(); // affiche "J'utilise ma main gauche !"
}

namespace {
    use Utils\Droite as u;
    u\quelleMain(); // affiche "J'utilise ma main droite !"
}

Maintenant, les deux fichiers peuvent être inclus à la fois sans problème, et nous précisons quel espace de noms utiliser pour prendre en charge l'appel de fonction. De plus, l'impact pour notre code existant est minime puisque la refonte de code nécessaire pour y parvenir nécessite seulement l'ajout de l'instruction use au départ du fichier pour indiquer l'espace de noms à utiliser.

La même idée peut être étendue au-delà de notre code PHP défini. Dans la prochaine section, nous voyons comment nous pouvons même remplacer des fonctions internes à l'aide des espaces de noms.

III-C-3. Remplacer des noms de fonction internes de PHP

Bien que les fonctions internes de PHP soient souvent d'une grande aide, parfois elles ne font pas exactement ce que nous voudrions. Nous pourrions avoir besoin d'améliorer leur comportement pour se conformer au comportement que nous attendons de la fonction, mais nous voulons également définir à nouveau la fonction avec un nouveau nom afin d'éviter de segmenter davantage la portée.

Les fonctions du système de fichiers sont un cas d'utilisation candidat pour cela. Admettons que tout fichier créé par file_put_contents() aie certains droits d'accès. Par exemple, les fichiers pourraient être créés en lecture seule ; nous pouvons définir la fonction dans un espace de noms, comme indiqué ci-dessous.

Listing 11. Définir file_put_contents() dans un espace de noms
Sélectionnez
<?php 
namespace Foo; 

function file_put_contents( $ficher, $données, $drapeaux = 0, $contexte = null ) 
{ 
    $retour = \file_put_contents( $fichier, $données, $drapeaux, $contexte );
    
    chmod($fichier, 0444);

    return $retour;
} 
?>

Nous appelons la fonction interne file_put_contents() dans la fonction et nous ajoutons une barre oblique arrière avant le nom de la fonction pour préciser qu'elle doit être appelée dans la portée globale, ce qui signifie que la fonction interne est appelée. Après l'appel de la fonction interne, nous appelons chmod() sur le fichier afin d'y attribuer les permissions adéquates.

Ce ne sont que quelques exemples de comment nous pourrions utiliser les espaces de noms pour améliorer notre code. Dans chaque situation, nous évitons de faire des détournements hideux comme ajouter un préfixe au nom de classe ou de fonction pour le rendre unique. Nous savons également comment l'utilisation des espaces de noms permet d'inclure du code écrit par une tierce personne dans de grandes applications sans avoir de problème de collisions de noms.

III-D. Conclusion

Les espaces de noms de PHP 5.3 sont un ajout bienvenu dans le langage. Ils aident les développeurs à mieux organiser leur code d'une manière cohérente dans une application. Ils vous permettent d'éviter d'utiliser des conventions de nommage pour simuler cette fonctionnalité, ce qui vous permet d'écrire du code plus efficace. Bien qu'ils se soient fait attendre, les espaces de noms sont un ajout bienvenu pour toute application à grande échelle et souffrant de collisions de noms.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2008 John Mertic et Guillaume Rossolini. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.