Comme la taille de votre bibliothèque de code PHP augmente, il existe un risque accru de redéfinition accidentelle d’une fonction ou d’un nom de classe qui a été déclaré auparavant. Le problème est exacerbant lorsque vous tentez d’ajouter des composants tiers ou plugins. Que se passe-t-il si vous instaurez une classe Database ou User qui existe déjà ? Jusqu’à présent, la seule solution a été d’utiliser des noms de fonctions longs. Par exemple, WordPress préfixe chaque nom avec “wp_ “. Le Zend Framework utilisais une convention de nommage très descriptif afin de donner des noms de classes longs comme la classe
Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive.
Les problèmes de collision de noms peuvent être résolus avec les espaces de noms. Les constantes PHP, classes et fonctions peuvent être regroupées en bibliothèques d’espace de noms.
Par défaut, toutes les constantes, les classes, et les noms de fonction sont placés dans l’espace global - comme elles l’étaient avant que PHP supporte les espaces de noms.
Le code des espaces de noms est défini en utilisant un mot-clé namespace unique en haut de votre fichier PHP. Ce doit être la première commande (à l’exception de declare) et pas de code non-PHP, HTML, ou d’espaces blancs ne peuvent précéder la commande, par exemple.
<?php
// défini ce code dans l'espace de noms 'MonProjet'
namespace MonProjet;
// ... code ...
Le code suivant sera affecté à l’espace de noms MonProjet. Il n’est pas possible de définir des nids d’espaces de noms ou plusieurs espaces de noms pour le même bloc de code (seul le dernier sera reconnu). Toutefois, vous pouvez définir un espace de noms de code différents dans le même fichier, par exemple :
<?php
namespace MonProjet1;
// code PHP pour l'espace de nom MonProjet1
namespace MonProjet2;
// code PHP pour l'espace de nom MonProjet2
// Syntaxe alternative
namespace MonProjet3 {
// // code PHP pour l'espace de nom MonProjet3
} ?>
Bien que cela soit possible, je le déconseille : conserver votre santé mentale en définissant un seul espace de noms par fichier.
PHP vous permet de définir une hiérarchie d’espaces de noms afin de subdiviser vos bibliothèques. Les sous-espaces de noms sont séparés par une barre oblique inverse (\), par exemple :
Dans un fichier nommé lib1.php, nous allons définir une constante, une fonction et une classe au sein de l’espace de noms App \ LIB1 :
<?php
// application library 1
namespace App\Lib1;
const MYCONST = 'App\Lib1\MYCONST';
function MyFunction() {
return __FUNCTION__;
}
class MyClass {
static function WhoAmI() {
return __METHOD__;
}
} ?>
Nous pouvons maintenant inclure ce code dans un autre fichier PHP, parexemple :
<?php
header('Content-type: text/plain');
require_once('lib1.php');
echo \App\Lib1\MYCONST . "\n";
echo \App\Lib1\MyFunction() . "\n";
echo \App\Lib1\MyClass::WhoAmI() . "\n";
?>
Aucun espace de noms n’est défini dans myapp.php afin que le code existe dans l’espace global. Toute référence directe à MYCONST, MyFunction ou MyClass va échouer parce qu’elles existent dans l’espace de noms App \ LIB1. Pour appeler du code dans lib1.php, on peut ajouter un préfixe \ App \ LIB1 pour définir des noms pleinement qualifiés. Le résultat suivant est émis quand on charge myapp.php :
App\Lib1\MYCONST
App\Lib1\MyFunction
App\Lib1\MyClass::WhoAmI
Les noms pleinement qualifiés peuvent devenir assez longs et il y a peu d’avantages évidents par rapport à la définition des noms de classe longs tels que App-LIB1-MyClass. Par conséquent, dans le prochain article, nous allons discuter du cumul et jeter un oeil de plus près sur le fonctionnement de PHP quant à la résolution des noms d’espace de noms.
A titre d’exemple, nous allons définir deux blocs de code presque identique, la seule différence est leur espace de noms :
<?php
// application library 1
namespace App\Lib1;
const MYCONST = 'App\Lib1\MYCONST';
function MyFunction() {
return __FUNCTION__;
}
class MyClass {
static function WhoAmI() {
return __METHOD__;
}
}
?>
<?php
// application library 2
namespace App\Lib2;
const MYCONST = 'App\Lib2\MYCONST';
function MyFunction() {
return __FUNCTION__;
}
class MyClass {
static function WhoAmI() {
return __METHOD__;
}
}
?>
Il y a un peu de terminologie PHP à comprendre avant de commencer...
Tout code PHP peut se référer à un nom pleinement qualifié - un identifiant commençant l’espace de noms et par le séparateur de barre oblique inverse - par exemple \App\LIB1\MYCONST, \App\LIB2\MyFunction(), etc.
Les noms pleinement qualifiés n’ont pas d’ambiguïté. La première barre oblique inverse fonctionne de manière semblable à un chemin de fichier, il signifie l’espace global du «root». Si nous avons implémenté une fonction MyFunction() différente dans notre espace global, elle pourrait être appelée à partir de lib1.php ou lib2.php en utilisant \MyFunction().
Les noms pleinement qualifiés sont utiles pour des appels uniques de fonctions ou d’initialisation objet. Toutefois, ils peuvent devenir impraticables lorsque vous faites beaucoup d’appels. Comme nous allons le découvrir, PHP offre d’autres options pour nous sauver des crampes de frappe des espaces de nommage.
Un identifiant avec au moins un séparateur d’espace de noms, par exemple LIB1\MyFunction().
Un identifiant, sans séparateur d’espace de noms, par exemple MyFunction().
Considérons le code suivant :
<?php
namespace App\Lib1;
require_once('lib1.php');
require_once('lib2.php');
header('Content-type: text/plain');
echo MYCONST . "\n";
echo MyFunction() . "\n";
echo MyClass::WhoAmI() . "\n";
?>
Même si l’on inclut à la fois lib1.php et lib2.php, les identificateurs MYCONST, MyFunction, et MyClass référencent seulement le code dans lib1.php. Cela se produit parce que le code myapp1.php est dans le même espace de noms \LIB1 :
Résultat :
App\Lib1\MYCONST
App\Lib1\MyFunction
App\Lib1\MyClass::WhoAmI
Les espaces de noms peuvent être importés grâce à l’opérateur use, par exemple :
<?php
use App\Lib2;
require_once('lib1.php');
require_once('lib2.php');
header('Content-type: text/plain');
echo Lib2\MYCONST . "\n";
echo Lib2\MyFunction() . "\n";
echo Lib2\MyClass::WhoAmI() . "\n";
?>
Résultat :
App\Lib2\MYCONST
App\Lib2\MyFunction
App\Lib2\MyClass::WhoAmI
Les alias d’espace de noms sont peut-être les implémentations les plus utiles. Les alias nous permettent de référencer les espaces de noms en utilisant un nom plus court.
<?php
use App\Lib1 as L;
use App\Lib2\MyClass as Obj;
header('Content-type: text/plain');
require_once('lib1.php');
require_once('lib2.php');
echo L\MYCONST . "\n";
echo L\MyFunction() . "\n";
echo L\MyClass::WhoAmI() . "\n";
echo Obj::WhoAmI() . "\n";
?>
La première déclaration de use définit App\LIB1 comme « L ». Les noms qualifiés utilisant des «L » seront traduits par App\LIB1 au moment de la compilation. On peut donc se référer à L\MYCONST et L\MyFunction() plutôt qu’à leurs noms pleinement qualifiés.
La seconde déclaration de use est plus intéressante. Elle définit ‘obj‘ comme alias pour la classe MyClass au sein de l’espace de noms App\Lib2\. Cela n’est possible que pour les classes, pas pour des constantes ou des fonctions. Nous pouvons maintenant utiliser new Obj() ou exécuter des méthodes statiques, comme indiqué ci-dessus.
Résultat
App\Lib1\MYCONST
App\Lib1\MyFunction
App\Lib1\MyClass::WhoAmI
App\Lib2\MyClass::WhoAmI
Les noms d’identifiants en PHP sont résolus en utilisant les règles d’espace de noms suivant. Reportez-vous au manuel PHP pour plus d’information.
_NAMESPACE_ est une chaîne PHP qui retourne toujours le nom de l’espace de noms courant. Dans l’espace global, elle renverra une chaîne vide.
<?php
namespace App\Lib1;
echo __NAMESPACE__;
// sortie : App\Lib1
?>
La valeur présente des avantages évidents durant le débogage. Elle peut également être utilisée pour générer dynamiquement un nom de classe entièrement qualifié, par exemple :
<?php
namespace App\Lib1;
class MyClass {
public function WhoAmI() {
return __METHOD__;
}
}
$c = __NAMESPACE__ . ' \\MyClass';
$m = new $c;
echo $m->WhoAmI(); // sortie : App\Lib1\MyClass::WhoAmI
?>
Le mot clé namespace peut être utilisé pour référencer explicitement un élément dans l’espace de nom courant ou un sous-espace de noms. C’est l’équivalent dans les espaces de noms de self au sein des classes :
<?php
namespace App\Lib1;
class MyClass {
public function WhoAmI() {
return __METHOD__;
}
}
$m = new namespace\MyClass;
echo $m->WhoAmI(); // sortie : App\Lib1\MyClass::WhoAmI
?>
L’un des meilleurs moyens de gagner du temps avec PHP 5 est l’autoloading. À l’échelle globale (non-espace de noms) du code PHP, une fonction de chargement automatique standard pourrait être écrite comme ceci :
<?php
$obj1 = new MyClass1(); // classes/MyClass1.php is auto-loaded
$obj2 = new MyClass2(); // classes/MyClass2.php is auto-loaded
// autoload function
function __autoload($class_name) {
require_once("classes/$class_name.php");
}
?>
En PHP 5.3, vous pouvez créer une instance d’une classe d’espace de noms. Dans cette situation, l’espace de noms entièrement qualifié et le nom de classe sont passés à la fonction __autoload(), par exemple la valeur de $class_name pourrait être App\LIB1\MyClass. Vous pouvez continuer à mettre tous vos fichiers de classe PHP dans le même dossier et y chainer l’espace de nom, cependant, cela pourrait entraîner des conflits de nom de fichier.
Alternativement, la hiérarchie de vos fichiers de classes pourraient être organisée de la même manière que la structure de votre espace de noms. Par exemple, un fichier MyClass.php pourrait être créé dans le dossier /classes/App/Lib1 :
<?php
namespace App\Lib1;
class MyClass {
public function WhoAmI() {
return __METHOD__;
}
}
?>
Un fichier dans le dossier racine pourrait alors utiliser le code suivant :
<?php
use App\Lib1\MyClass as MC;
$obj = new MC();
echo $obj->WhoAmI();
// autoload function
function __autoload($class) {
// convert namespace to full file path
$class = 'classes/' . str_replace('\\', '/', $class) . '.php';
require_once($class);
}
?>
Explications :